Demos and Sample Code

Anyline Demo
AnylineOCR Plugin AUTO: Scrabble
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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
{
  "camera": {
    "captureResolution": "720"
  },
  "flash": {
    "mode": "manual",
    "alignment": "bottom_right",
    "imageOn": "flash_on",
    "imageOff": "flash_off"
  },
  "viewPlugin": {
    "cutoutConfig": {
      "style": "rect",
      "maxWidthPercent": "80%",
      "maxHeightPercent": "80%",
      "alignment": "center",
      "width": 600,
      "ratioFromSize": {
        "width": 4,
        "height": 1
      },
      "strokeWidth": 2,
      "cornerRadius": 4,
      "strokeColor": "FFFFFF",
      "outerColor": "000000",
      "outerAlpha": 0.3,
      "feedbackStrokeColor": "0099FF"
    },
    "scanFeedback": {
      "style": "contour_point",
      "strokeWidth": 2,
      "strokeColor": "0099FF",
      "fillColor": "220099FF",
      "beepOnResult": true,
      "vibrateOnResult": true,
      "blinkAnimationOnResult": true
    },
    "cancelOnResult": true
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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
import android.content.Intent;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.view.WindowManager;

import at.nineyards.anyline.camera.AnylineViewConfig;
import at.nineyards.anyline.modules.ocr.AnylineOcrConfig;
import at.nineyards.anyline.modules.ocr.AnylineOcrResult;
import at.nineyards.anyline.modules.ocr.AnylineOcrResultListener;
import at.nineyards.anyline.modules.ocr.AnylineOcrScanView;
import io.anyline.examples.R;
import io.anyline.examples.SettingsFragment;
import io.anyline.examples.ocr.apis.AnagramActivity;

public class ScanScrabbleActivity extends AppCompatActivity {

    private static final String TAG = ScanScrabbleActivity.class.getSimpleName();
    private AnylineOcrScanView scanView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //Set the flag to keep the screen on (otherwise the screen may go dark during scanning)
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        setContentView(R.layout.activity_anyline_ocr);

        String lic = getString(R.string.anyline_license_key);
        // Get the view from the layout
        scanView = (AnylineOcrScanView) findViewById(R.id.scan_view);

        //Configure the OCR for Scrabble
        AnylineOcrConfig anylineOcrConfig = new AnylineOcrConfig();
        // use the AUTO mode which automatically adjusts the scanning parameters to deliver the best result
        anylineOcrConfig.setScanMode(AnylineOcrConfig.ScanMode.AUTO);
        // Set the languages used for OCR
        // Copies given traineddata-file to a place where the core can access it.
        // The file must be located directly in the assets directory (or in tessdata/ but no other folders are allowed)
        anylineOcrConfig.setLanguages("tessdata/scrabble.traineddata");
        // allow only capital letters plus some german Umlaute
        anylineOcrConfig.setCharWhitelist("ABCDEFGHIJKLMNOPQRSTUVWXYZÄÜÖ");
        // the minimum confidence required to return a result, a value between 0 and 100.
        // (higher confidence means less likely to get a wrong result, but may be slower to get a result)
        anylineOcrConfig.setMinConfidence(80);
        // a simple regex for a basic validation of the scrabble characters. We require at least 4 characters, and a maximum of 10 (usually it would be 7, but Umlaute may use two ASCII symbols)
        anylineOcrConfig.setValidationRegex("^[A-ZÄÜÖ]{7,10}$");

        // set the ocr config
        scanView.setAnylineOcrConfig(anylineOcrConfig);

        // Configure the view (cutout, the camera resolution, etc.) via json (can also be done in xml in the layout)
        scanView.setConfig(new AnylineViewConfig(this, "scrabble_view_config.json"));

        scanView.initAnyline(lic, new AnylineOcrResultListener() {
            @Override
            public void onResult(AnylineOcrResult anylineOcrResult) {
                // Called when a valid result is found (minimum confidence is exceeded and validation with regex was ok)
                if (!anylineOcrResult.getResult().isEmpty()) {
                    Intent i = new Intent(ScanScrabbleActivity.this, AnagramActivity.class);
                    i.putExtra(AnagramActivity.SCRABBLE_INPUT, anylineOcrResult.getResult().trim());
                    startActivity(i);
                }
            }
        });

        // disable the reporting if set to off in preferences
        scanView.setReportingEnabled(PreferenceManager.getDefaultSharedPreferences(this).getBoolean(SettingsFragment
                .KEY_PREF_REPORTING_ON, true));
    }

    @Override
    protected void onResume() {
        super.onResume();

        //we use a postdelay for 'start scanning' to improve the user experience:
        //otherwise the scrabble result would be shown faster as the user realizes the scanning
        //process has already started
        scanView.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (!isFinishing()) {
                    scanView.startScanning();
                }
            }
        }, 1500);
    }

    @Override
    protected void onPause() {
        super.onPause();

        scanView.cancelScanning();
        scanView.releaseCameraInBackground();
    }

}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#import "ALScrabbleScanViewController.h"
#import <Anyline/Anyline.h>
#import "ALScrabbleViewController.h"
#import "ALCustomBarButton.h"
#import "ScanHistory.h"
#import "NSUserDefaults+ALExamplesAdditions.h"
#import "ALAppDemoLicenses.h"

// This is the license key for the examples project used to set up Aynline below
NSString * const kScrabbleLicenseKey = kDemoAppLicenseKey;
// The controller has to conform to <AnylineOCRModuleDelegate> to be able to receive results
@interface ALScrabbleScanViewController ()<ALOCRScanPluginDelegate, ALInfoDelegate>

// The Anyline plugin used for OCR
@property (nonatomic, strong) ALOCRScanViewPlugin *scrabbleScanViewPlugin;
@property (nonatomic, strong) ALOCRScanPlugin *scrabbleScanPlugin;
@property (nullable, nonatomic, strong) ALScanView *scanView;

@end

@implementation ALScrabbleScanViewController
/*
 We will do our main setup in viewDidLoad. Its called once the view controller is getting ready to be displayed.
 */
- (void)viewDidLoad {
    [super viewDidLoad];

    // Set the background color to black to have a nicer transition
    self.view.backgroundColor = [UIColor blackColor];
    self.title = @"Scrabble";
    // Initializing the module. Its a UIView subclass. We set the frame to fill the whole screen
    CGRect frame = [[UIScreen mainScreen] applicationFrame];
    frame = CGRectMake(frame.origin.x, frame.origin.y + self.navigationController.navigationBar.frame.size.height, frame.size.width, frame.size.height - self.navigationController.navigationBar.frame.size.height);
    
    ALOCRConfig *config = [[ALOCRConfig alloc] init];
    config.scanMode = ALAuto;
    NSString *anylineTraineddata = [[NSBundle mainBundle] pathForResource:@"scrabble" ofType:@"traineddata"];
    [config setLanguages:@[anylineTraineddata] error:nil];
    config.charWhiteList = @"ABCDEFGHIJKLMNOPQRSTUVWXYZÄÜÖ";
    config.validationRegex = @"^[A-ZÄÜÖ]{7,10}$";
    
    NSError *error = nil;
    
    self.scrabbleScanPlugin = [[ALOCRScanPlugin alloc] initWithPluginID:@"ANYLINE_OCR"
                                                             licenseKey:kScrabbleLicenseKey
                                                               delegate:self
                                                              ocrConfig:config
                                                                  error:&error];
    NSAssert(self.scrabbleScanPlugin, @"Setup Error: %@", error.debugDescription);
    [self.scrabbleScanPlugin addInfoDelegate:self];
    
    NSString *confPath = [[NSBundle mainBundle] pathForResource:@"scrabble_config" ofType:@"json"];
    ALScanViewPluginConfig *scanViewPluginConfig = [ALScanViewPluginConfig configurationFromJsonFilePath:confPath];
    
    self.scrabbleScanViewPlugin = [[ALOCRScanViewPlugin alloc] initWithScanPlugin:self.scrabbleScanPlugin
                                                             scanViewPluginConfig:scanViewPluginConfig];
    NSAssert(self.scrabbleScanViewPlugin, @"Setup Error: %@", error.debugDescription);
    
    self.scanView = [[ALScanView alloc] initWithFrame:frame scanViewPlugin:self.scrabbleScanViewPlugin];
    
    // After setup is complete we add the module to the view of this view controller
    [self.view addSubview:self.scanView];
    //Start Camera:
    [self.scanView startCamera];
    
    [self.view sendSubviewToBack:self.scanView];
    [self startListeningForMotion];
    self.controllerType = ALScanHistoryScrabble;

}

/*
 This method will be called once the view controller and its subviews have appeared on screen
 */
-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    // We use this subroutine to start Anyline. The reason it has its own subroutine is
    // so that we can later use it to restart the scanning process.
    [self startAnyline];
    
    //Update Position of Warning Indicator
    [self updateWarningPosition:
     self.scrabbleScanViewPlugin.cutoutRect.origin.y +
     self.scrabbleScanViewPlugin.cutoutRect.size.height +
     self.scrabbleScanViewPlugin.frame.origin.y +
     120];
}

/*
 Cancel scanning to allow the module to clean up
 */
- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    
    [self.scrabbleScanViewPlugin stopAndReturnError:nil];
}

/*
 This method is used to tell Anyline to start scanning. It gets called in
 viewDidAppear to start scanning the moment the view appears. Once a result
 is found scanning will stop automatically (you can change this behaviour
 with cancelOnResult:). When the user dismisses self.identificationView this
 method will get called again.
 */
- (void)startAnyline {
    NSError *error;
    BOOL success = [self.scrabbleScanViewPlugin startAndReturnError:&error];
    if( !success ) {
        // Something went wrong. The error object contains the error description
        NSAssert(success, @"Start Scanning Error: %@", error.debugDescription);
    }
    
    self.startTime = CACurrentMediaTime();
}

- (void)stopAnyline {
    if (self.scrabbleScanPlugin.isRunning) {
        [self.scrabbleScanViewPlugin stopAndReturnError:nil];
    }
}

#pragma mark -- AnylineOCRModuleDelegate
/*
 This is the main delegate method Anyline uses to report its results
 */
- (void)anylineOCRScanPlugin:(ALOCRScanPlugin *)anylineOCRScanPlugin didFindResult:(ALOCRResult *)result {
    [self anylineDidFindResult:result.result barcodeResult:@"" image:result.image scanPlugin:anylineOCRScanPlugin viewPlugin:self.scrabbleScanViewPlugin completion:^{
        [self stopAnyline];
        ALScrabbleViewController *vc = [[ALScrabbleViewController alloc] init];
        [vc setResult:result.result];
        [self.navigationController pushViewController:vc animated:YES];
    }];
}

- (void)anylineScanPlugin:(ALAbstractScanPlugin *)anylineScanPlugin reportInfo:(ALScanInfo *)info{
    if ([info.variableName isEqualToString:@"$brightness"]) {
        [self updateBrightness:[info.value floatValue] forModule:self.scrabbleScanViewPlugin];
    }
    
}

- (void)anylineScanPlugin:(ALAbstractScanPlugin *)anylineScanPlugin runSkipped:(ALRunSkippedReason *)runSkippedReason {
    
    //Handle run skipped reasons here:
    switch (runSkippedReason.reason) {
        case ALRunFailureResultNotValid:
            break;
        case ALRunFailureConfidenceNotReached:
            break;
        case ALRunFailureNoLinesFound:
            break;
        case ALRunFailureNoTextFound:
            break;
        case ALRunFailureUnkown:
            break;
        default:
            break;
    }
}

@end
AnylineOCR Plugin GRID: Bottlecap
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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
{
  "camera":{
  "captureResolution":"720"
  },
  "flash": {
    "mode": "manual",
    "alignment": "bottom_right",
    "imageOn": "flash_on",
    "imageOff": "flash_off"
  },
  "viewPlugin" : {
    "cutoutConfig": {
      "style": "rect",
      "maxWidthPercent": "80%",
      "maxHeightPercent": "80%",
      "alignment": "center",
      "width": 300,
      "ratioFromSize": {
        "width": 1,
        "height": 1
      },
      "strokeWidth": 2,
      "cornerRadius": 300,
      "strokeColor": "FFFFFF",
      "outerColor": "000000",
      "outerAlpha": 0.3,
      "offset": {
        "x": 0,
        "y": 0
      },
      "cropOffset": {
        "x": 0,
        "y": 0
      },
      "cropPadding": {
        "x": 10,
        "y": 10
      },
      "feedbackStrokeColor": "0099FF"
    },
    "cancelOnResult": true,
    "scanFeedback": {
      "style": "rect",
      "strokeWidth": 1,
      "strokeColor": "0099FF",
      "fillColor": "220099FF",
      "cornerRadius": 10,
      "beepOnResult": true,
      "vibrateOnResult": true,
      "blinkAnimationOnResult": true
    }
  }
}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import android.graphics.PointF;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.RelativeLayout;

import java.util.List;

import at.nineyards.anyline.camera.AnylineViewConfig;
import at.nineyards.anyline.modules.ocr.AnylineOcrConfig;
import at.nineyards.anyline.modules.ocr.AnylineOcrResult;
import at.nineyards.anyline.modules.ocr.AnylineOcrResultListener;
import at.nineyards.anyline.modules.ocr.AnylineOcrScanView;
import io.anyline.examples.R;
import io.anyline.examples.SettingsFragment;
import io.anyline.examples.ocr.result.BottlecapResultView;

public class ScanBottlecapActivity extends AppCompatActivity {

    private static final String TAG = ScanBottlecapActivity.class.getSimpleName();
    private AnylineOcrScanView scanView;
    private BottlecapResultView bottlecapResultView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //Set the flag to keep the screen on (otherwise the screen may go dark during scanning)
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        setContentView(R.layout.activity_anyline_ocr);

        addBottlecapResultView();

        String lic = getString(R.string.anyline_license_key);
        scanView = (AnylineOcrScanView) findViewById(R.id.scan_view);

        // see ScanRedBullCodeActivity for a more detailed description
        AnylineOcrConfig anylineOcrConfig = new AnylineOcrConfig();
        anylineOcrConfig.setLanguages("tessdata/bottlecap.traineddata");
        anylineOcrConfig.setCharWhitelist("123456789ABCDEFGHJKLMNPRSTUVWXYZ");
        anylineOcrConfig.setMinCharHeight(14);
        anylineOcrConfig.setMaxCharHeight(65);
        anylineOcrConfig.setMinConfidence(75);
        anylineOcrConfig.setValidationRegex("^[0-9A-Z]{3}\n[0-9A-Z]{3}\n[0-9A-Z]{3}");
        anylineOcrConfig.setScanMode(AnylineOcrConfig.ScanMode.GRID);
        anylineOcrConfig.setCharCountX(3);
        anylineOcrConfig.setCharCountY(3);
        anylineOcrConfig.setCharPaddingXFactor(0.3);
        anylineOcrConfig.setCharPaddingYFactor(0.5);
        anylineOcrConfig.setIsBrightTextOnDark(true);
        scanView.setAnylineOcrConfig(anylineOcrConfig);

        //init the scanViewPlugin config
        ScanViewPluginConfig ocrScanViewPluginConfig = new ScanViewPluginConfig(getApplicationContext(), "bottlecap_view_config.json");
        //init the scan view
        OcrScanViewPlugin scanViewPlugin = new OcrScanViewPlugin(getApplicationContext(), getString(R.string.anyline_license_key), anylineOcrConfig, ocrScanViewPluginConfig, "OCR");
        //init the base config used for camera and flash
        BaseScanViewConfig ocrBaseScanViewConfig = new BaseScanViewConfig(getApplicationContext(), "bottlecap_view_config.json");
        //set the scan Base config
        scanView.setScanViewConfig(ocrBaseScanViewConfig);
        //set the scan view plugin to the scan view
        scanView.setScanViewPlugin(scanViewPlugin);
        //add the scan result listener
        scanViewPlugin.addScanResultListener(new ScanResultListener<OcrScanResult>() {
            @Override
            public void onResult(OcrScanResult result) {
                String path = setupImagePath(result.getCutoutImage());

                startScanResultIntent(getResources().getString(R.string.title_bottlecap), getSerialNumberResult(result.getResult().toString()), path);
                setupScanProcessView(ScanBottlecapActivity.this, result, getScanModule());

            }

        });

        // disable the reporting if set to off in preferences
        scanView.setReportingEnabled(PreferenceManager.getDefaultSharedPreferences(this).getBoolean(SettingsFragment
                .KEY_PREF_REPORTING_ON, true));
        bottlecapResultView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                bottlecapResultView.setVisibility(View.INVISIBLE);
                if (!scanView.isRunning()) {
                    scanView.startScanning();
                }
            }
        });
    }

    private void addBottlecapResultView() {
        RelativeLayout mainLayout = (RelativeLayout) findViewById(R.id.main_layout);

        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        params.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
        params.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);

        bottlecapResultView = new BottlecapResultView(this);
        bottlecapResultView.setVisibility(View.INVISIBLE);

        mainLayout.addView(bottlecapResultView, params);
    }

    @Override
    protected void onResume() {
        super.onResume();

        if(bottlecapResultView == null || bottlecapResultView.getVisibility() != View.VISIBLE){
            scanView.start();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();

        scanView.stop();
        scanView.releaseCameraInBackground();
    }

    @Override
    public void onBackPressed() {
        //close the result view on back press if it is open
        if (bottlecapResultView.getVisibility() == View.VISIBLE) {
            bottlecapResultView.setVisibility(View.INVISIBLE);
            if (!scanView.isRunning()) {
                scanView.startScanning();
            }
        } else {
            super.onBackPressed();
        }

    }

}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#import "ALBottlecapScanViewController.h"
#import <Anyline/Anyline.h>
#import "ALResultOverlayView.h"
#import "NSUserDefaults+ALExamplesAdditions.h"
#import "ALAppDemoLicenses.h"
#import "ALResultViewController.h"
// This is the license key for the examples project used to set up Aynline below
NSString * const kBottlecapLicenseKey = kDemoAppLicenseKey;
// The controller has to conform to <AnylineOCRModuleDelegate> to be able to receive results
@interface ALBottlecapScanViewController ()<ALOCRScanPluginDelegate, ALInfoDelegate>

// The Anyline plugin used for OCR
@property (nonatomic, strong) ALOCRScanViewPlugin *bottlecapScanViewPlugin;
@property (nonatomic, strong) ALOCRScanPlugin *bottlecapvinScanPlugin;
@property (nullable, nonatomic, strong) ALScanView *scanView;

@end

@implementation ALBottlecapScanViewController
/*
 We will do our main setup in viewDidLoad. Its called once the view controller is getting ready to be displayed.
 */
- (void)viewDidLoad {
    [super viewDidLoad];
    // Set the background color to black to have a nicer transition
    self.view.backgroundColor = [UIColor blackColor];
    self.title = @"Bottlecap";

    ALOCRConfig *config = [[ALOCRConfig alloc] init];
    config.scanMode = ALGrid;
    config.charHeight = ALRangeMake(21, 97);
    NSString *anylineTraineddata = [[NSBundle mainBundle] pathForResource:@"bottlecap" ofType:@"traineddata"];
    [config setLanguages:@[anylineTraineddata] error:nil];
    config.charWhiteList = @"123456789ABCDEFGHJKLMNPRSTUVWXYZ";
    config.minConfidence = 75;
    config.validationRegex = @"^[0-9A-Z]{3}\n[0-9A-Z]{3}\n[0-9A-Z]{3}";
    
    config.charCountX = 3;
    config.charCountY = 3;
    config.charPaddingXFactor = 0.3;
    config.charPaddingYFactor = 0.5;
    config.isBrightTextOnDark = YES;
    
    NSError *error = nil;
    
    CGRect frame = [[UIScreen mainScreen] applicationFrame];
    frame = CGRectMake(frame.origin.x, frame.origin.y + self.navigationController.navigationBar.frame.size.height, frame.size.width, frame.size.height - self.navigationController.navigationBar.frame.size.height);
    
    self.bottlecapvinScanPlugin = [[ALOCRScanPlugin alloc] initWithPluginID:@"ANYLINE_OCR"
                                                                 licenseKey:kBottlecapLicenseKey
                                                                   delegate:self
                                                                  ocrConfig:config
                                                                      error:&error];
    NSAssert(self.bottlecapvinScanPlugin, @"Setup Error: %@", error.debugDescription);
    [self.bottlecapvinScanPlugin addInfoDelegate:self];
    
    NSString *confPath = [[NSBundle mainBundle] pathForResource:@"bottlecap_config" ofType:@"json"];
    ALScanViewPluginConfig *scanViewPluginConfig = [ALScanViewPluginConfig configurationFromJsonFilePath:confPath];
    
    self.bottlecapScanViewPlugin = [[ALOCRScanViewPlugin alloc] initWithScanPlugin:self.bottlecapvinScanPlugin
                                                              scanViewPluginConfig:scanViewPluginConfig];
    NSAssert(self.bottlecapScanViewPlugin, @"Setup Error: %@", error.debugDescription);
    
    self.scanView = [[ALScanView alloc] initWithFrame:frame scanViewPlugin:self.bottlecapScanViewPlugin];
    
    // After setup is complete we add the module to the view of this view controller
    [self.view addSubview:self.scanView];
    [self.view sendSubviewToBack:self.scanView];
    
    //Start Camera:
    [self.scanView startCamera];
    [self startListeningForMotion];
    
    self.controllerType = ALScanHistoryVIN;
}

/*
 This method will be called once the view controller and its subviews have appeared on screen
 */
-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    // We use this subroutine to start Anyline. The reason it has its own subroutine is
    // so that we can later use it to restart the scanning process.
    [self startAnyline];
}

/*
 Cancel scanning to allow the module to clean up
 */
- (void)viewWillDisappear:(BOOL)animated {
    [self.bottlecapScanViewPlugin stopAndReturnError:nil];
}

/*
 This method is used to tell Anyline to start scanning. It gets called in
 viewDidAppear to start scanning the moment the view appears. Once a result
 is found scanning will stop automatically (you can change this behaviour
 with cancelOnResult:). When the user dismisses self.identificationView this
 method will get called again.
 */
- (void)startAnyline {
    NSError *error;
    BOOL success = [self.bottlecapScanViewPlugin startAndReturnError:&error];
    if( !success ) {
        // Something went wrong. The error object contains the error description
        NSAssert(success, @"Start Scanning Error: %@", error.debugDescription);
    }
}

#pragma mark -- AnylineOCRModuleDelegate

/*
 This is the main delegate method Anyline uses to report its results
 */
- (void)anylineOCRScanPlugin:(ALOCRScanPlugin *)anylineOCRScanPlugin
               didFindResult:(ALOCRResult *)result {
    // We are done. Cancel scanning
    [self anylineDidFindResult:result.result barcodeResult:@"" image:result.image scanPlugin:anylineOCRScanPlugin viewPlugin:self.bottlecapScanViewPlugin completion:^{
        //Display the result
        NSMutableArray <ALResultEntry*> *resultData = [[NSMutableArray alloc] init];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Bottlecap code" value:result.result]];
        
        ALResultViewController *vc = [[ALResultViewController alloc] initWithResultData:resultData image:result.image];
        [self.navigationController pushViewController:vc animated:YES];
    }];
}

- (void)anylineScanPlugin:(ALAbstractScanPlugin *)anylineScanPlugin reportInfo:(ALScanInfo *)info{
    if ([info.variableName isEqualToString:@"$brightness"]) {
        [self updateBrightness:[info.value floatValue] forModule:self.bottlecapScanViewPlugin];
    }
    
}

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
    NSError *error = nil;
    BOOL success = [self.bottlecapScanViewPlugin startAndReturnError:&error];
    
    NSAssert(success, @"We failed starting: %@",error.debugDescription);
}


@end
AnylineOCR Plugin LINE: Record Numbers
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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
{
  "camera": {
    "captureResolution": "720"
  },
  "flash": {
    "mode": "manual",
    "alignment": "bottom_right",
    "imageOn": "flash_on",
    "imageOff": "flash_off"
  },
  "viewPlugin": {
    "plugin" : {
      "id" : "OCR_RECORD",
      "ocrPlugin" : {
        "scanMode" : "LINE",
        "languages" : ["eng_no_dict.traineddata", "deu.traineddata"],
        "charWhitelist": "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.",
        "minCharHeight":15,
        "maxCharHeight":70,
        "minConfidence": 75,
        "removeSmallContours":false
      }
    },
    "cutoutConfig": {
      "style": "rect",
      "maxWidthPercent": "80%",
      "maxHeightPercent": "80%",
      "alignment": "center",
      "width": 340,
      "ratioFromSize": {
        "width": 4,
        "height": 1
      },
      "strokeWidth": 2,
      "cornerRadius": 10,
      "strokeColor": "FFFFFF",
      "outerColor": "000000",
      "outerAlpha": 0.3,
      "feedbackStrokeColor": "0099FF"
    },
    "scanFeedback": {
      "animationDuration": 400,
      "style": "contour_point",
      "strokeWidth": 2,
      "strokeColor": "0099FF",
      "fillColor": "220099FF",
      "beepOnResult": true,
      "vibrateOnResult": true,
      "blinkAnimationOnResult": true
    },
    "cancelOnResult": true
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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
import android.content.Intent;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.view.WindowManager;

import at.nineyards.anyline.camera.AnylineViewConfig;
import at.nineyards.anyline.modules.ocr.AnylineOcrConfig;
import at.nineyards.anyline.modules.ocr.AnylineOcrResult;
import at.nineyards.anyline.modules.ocr.AnylineOcrResultListener;
import at.nineyards.anyline.modules.ocr.AnylineOcrScanView;
import io.anyline.examples.R;
import io.anyline.examples.SettingsFragment;
import io.anyline.examples.ocr.apis.RecordSearchActivity;

public class ScanRecordActivity extends AppCompatActivity {

    private static final String TAG = ScanRecordActivity.class.getSimpleName();
    private AnylineOcrScanView scanView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //Set the flag to keep the screen on (otherwise the screen may go dark during scanning)
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        setContentView(R.layout.activity_anyline_ocr);

        String lic = getString(R.string.anyline_license_key);
        scanView = (AnylineOcrScanView) findViewById(R.id.scan_view);


        //Configure the OCR for Record Numbers
        AnylineOcrConfig anylineOcrConfig = new AnylineOcrConfig();
        // use the LINE mode, since the numbers can be of different length
        anylineOcrConfig.setScanMode(AnylineOcrConfig.ScanMode.LINE);
        // Set the languages used for OCR
        // Copies given traineddata-file to a place where the core can access it.
        // The file must be located directly in the assets directory (or in tessdata/ but no other folders are allowed)
        anylineOcrConfig.setLanguages("tessdata/eng_no_dict.traineddata", "tessdata/deu.traineddata");
        // allow only capital letters and some numbers - these are the only characters the SDK will consider
        anylineOcrConfig.setCharWhitelist("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.");
        // a simple regex for a basic validation of the record numbers
        anylineOcrConfig.setValidationRegex("^([A-Z]+\\s*-*\\s*)?[0-9A-Z-\\s\\.]{3,}$");
        // the characters height is 15 pixels minimum (make sure your cutout size is aligned to this)
        anylineOcrConfig.setMinCharHeight(15);
        // the characters height is 70 pixels maximum (make sure your cutout size is aligned to this)
        anylineOcrConfig.setMaxCharHeight(70);
        // the minimum confidence required to return a result, a value between 0 and 100.
        // (higher confidence means less likely to get an incorrect result, but may be slower to deliver a result)
        anylineOcrConfig.setMinConfidence(75);
        // we don't want to remove small contours, as a . for example should be kept in the result
        anylineOcrConfig.setRemoveSmallContours(false);
        // we also don't want whitespaces to be removed - they are required for a search with the scanned record number
        anylineOcrConfig.setRemoveWhitespaces(false);
        scanView.setAnylineOcrConfig(anylineOcrConfig);

        scanView.setConfig(new AnylineViewConfig(this, "record_view_config.json"));

        scanView.initAnyline(lic, new AnylineOcrResultListener() {
            @Override
            public void onResult(AnylineOcrResult anylineOcrResult) {
                if (!anylineOcrResult.getResult().isEmpty()) {
                    Intent i = new Intent(ScanRecordActivity.this, RecordSearchActivity.class);
                    i.putExtra(RecordSearchActivity.RECORD_INPUT, anylineOcrResult.getResult().trim());
                    startActivity(i);
                }
            }
        });

        // disable the reporting if set to off in preferences
        scanView.setReportingEnabled(PreferenceManager.getDefaultSharedPreferences(this).getBoolean(SettingsFragment
                .KEY_PREF_REPORTING_ON, true));
    }

    @Override
    protected void onResume() {
        super.onResume();

        scanView.startScanning();
    }

    @Override
    protected void onPause() {
        super.onPause();

        scanView.cancelScanning();
        scanView.releaseCameraInBackground();
    }

}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#import "ALRecordNumberScanViewController.h"
#import <Anyline/Anyline.h>
#import "ALBaseViewController.h"
#import "NSUserDefaults+ALExamplesAdditions.h"
#import "ALAppDemoLicenses.h"

// This is the license key for the examples project used to set up Aynline below
NSString * const kRecordNumberLicenseKey = kDemoAppLicenseKey;
// The controller has to conform to <AnylineOCRModuleDelegate> to be able to receive results
@interface ALRecordNumberScanViewController ()<ALOCRScanPluginDelegate, ALInfoDelegate>

// The Anyline plugin used for OCR
@property (nonatomic, strong) ALOCRScanViewPlugin *recordScanViewPlugin;
@property (nonatomic, strong) ALOCRScanPlugin *recordScanPlugin;
@property (nullable, nonatomic, strong) ALScanView *scanView;

@end

@implementation ALRecordNumberScanViewController
/*
 We will do our main setup in viewDidLoad. Its called once the view controller is getting ready to be displayed.
 */
- (void)viewDidLoad {
    [super viewDidLoad];
    // Set the background color to black to have a nicer transition
    self.view.backgroundColor = [UIColor blackColor];
    self.title = @"Record Number";
    
    // Initializing the module. Its a UIView subclass. We set the frame to fill the whole screen
    CGRect frame = [[UIScreen mainScreen] applicationFrame];
    frame = CGRectMake(frame.origin.x, frame.origin.y + self.navigationController.navigationBar.frame.size.height, frame.size.width, frame.size.height - self.navigationController.navigationBar.frame.size.height);
    
    ALOCRConfig *config = [[ALOCRConfig alloc] init];
    config.charHeight = ALRangeMake(22, 105);
    NSString *engTraineddata = [[NSBundle mainBundle] pathForResource:@"eng_no_dict" ofType:@"traineddata"];
    NSString *deuTraineddata = [[NSBundle mainBundle] pathForResource:@"deu" ofType:@"traineddata"];
    [config setLanguages:@[engTraineddata,deuTraineddata] error:nil];
    config.charWhiteList = @"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.";
    config.minConfidence = 75;
    config.validationRegex = @"^([A-Z]+\\s*-*\\s*)?[0-9A-Z-\\s\\.]{3,}$";
    config.scanMode = ALLine;
    config.removeSmallContours = NO;
    
    NSError *error = nil;
    
    self.recordScanPlugin = [[ALOCRScanPlugin alloc] initWithPluginID:@"ANYLINE_OCR"
                                                           licenseKey:kRecordNumberLicenseKey
                                                             delegate:self
                                                            ocrConfig:config
                                                                error:&error];
    NSAssert(self.recordScanPlugin, @"Setup Error: %@", error.debugDescription);
    [self.recordScanPlugin addInfoDelegate:self];
    
    NSString *confPath = [[NSBundle mainBundle] pathForResource:@"record_number_config" ofType:@"json"];
    ALScanViewPluginConfig *scanViewPluginConfig = [ALScanViewPluginConfig configurationFromJsonFilePath:confPath];
    
    self.recordScanViewPlugin = [[ALOCRScanViewPlugin alloc] initWithScanPlugin:self.recordScanPlugin
                                                           scanViewPluginConfig:scanViewPluginConfig];
    NSAssert(self.recordScanViewPlugin, @"Setup Error: %@", error.debugDescription);
    
    self.scanView = [[ALScanView alloc] initWithFrame:frame scanViewPlugin:self.recordScanViewPlugin];
    
    // After setup is complete we add the module to the view of this view controller
    [self.view addSubview:self.scanView];
    [self.view sendSubviewToBack:self.scanView];
    
    //Start Camera:
    [self.scanView startCamera];
    [self startListeningForMotion];
    
    self.controllerType = ALScanHistoryRecordNumber;
}

/*
 This method will be called once the view controller and its subviews have appeared on screen
 */
-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    // We use this subroutine to start Anyline. The reason it has its own subroutine is
    // so that we can later use it to restart the scanning process.
    [self startAnyline];
    
    //Update Position of Warning Indicator
    [self updateWarningPosition:
     self.recordScanViewPlugin.cutoutRect.origin.y +
     self.recordScanViewPlugin.cutoutRect.size.height +
     self.recordScanViewPlugin.frame.origin.y +
     120];
}

/*
 Cancel scanning to allow the module to clean up
 */
- (void)viewWillDisappear:(BOOL)animated {
    [self.recordScanViewPlugin stopAndReturnError:nil];
}

/*
 This method is used to tell Anyline to start scanning. It gets called in
 viewDidAppear to start scanning the moment the view appears. Once a result
 is found scanning will stop automatically (you can change this behaviour
 with cancelOnResult:). When the user dismisses self.identificationView this
 method will get called again.
 */
- (void)startAnyline {
    NSError *error;
    BOOL success = [self.recordScanViewPlugin startAndReturnError:&error];
    if( !success ) {
        // Something went wrong. The error object contains the error description
        NSAssert(success, @"Start Scanning Error: %@", error.debugDescription);
    }
    
    self.startTime = CACurrentMediaTime();
}

#pragma mark -- AnylineOCRModuleDelegate
/*
 This is the main delegate method Anyline uses to report its results
 */
- (void)anylineOCRScanPlugin:(ALOCRScanPlugin *)anylineOCRScanPlugin didFindResult:(ALOCRResult *)result {
    [self anylineDidFindResult:result.result barcodeResult:@"" image:result.image scanPlugin:anylineOCRScanPlugin viewPlugin:self.recordScanViewPlugin completion:^{
        ALBaseViewController *vc = [[ALBaseViewController alloc] init];
        vc.result = result.result;
        NSString *url = [NSString stringWithFormat:@"https://www.google.at/search?q=\"%@\" site:discogs.com OR site:musbrainz.org OR site:allmusic.com", result.result];
        [vc startWebSearchWithURL:url];
        [self.navigationController pushViewController:vc animated:YES];
    }];
}

- (void)anylineScanPlugin:(ALAbstractScanPlugin *)anylineScanPlugin reportInfo:(ALScanInfo *)info{
    if ([info.variableName isEqualToString:@"$brightness"]) {
        [self updateBrightness:[info.value floatValue] forModule:self.recordScanViewPlugin];
    }
}


@end
Meter Plugin Analog/Digital Automatic
    <at.nineyards.anyline.modules.energy.EnergyScanView
        android:id="@+id/energy_scan_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:cutout_alignment="top"
        app:cutout_style="rect"
        app:cutout_outside_color="#55000000"
        app:cutout_offset_y="120"
        app:cutout_rect_corner_radius_in_dp="4"
        app:cutout_stroke_width_in_dp="2"
        app:cutout_stroke_color="#FFFFFF"
        app:flash_mode="manual"
        app:flash_alignment="bottom_right"
        app:beep_on_result="true"
        app:vibrate_on_result="true"
        app:blink_animation_on_result="true"
        app:cancel_on_result="true"
        app:visual_feedback_style="rect"
        app:visual_feedback_stroke_color="#0099FF"
        />
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import android.content.DialogInterface;
import android.graphics.Color;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.WindowManager;
import android.widget.CompoundButton;
import android.widget.Switch;

import at.nineyards.anyline.camera.CameraController;
import at.nineyards.anyline.camera.CameraOpenListener;
import at.nineyards.anyline.models.AnylineImage;
import at.nineyards.anyline.modules.barcode.NativeBarcodeResultListener;
import at.nineyards.anyline.modules.energy.EnergyResult;
import at.nineyards.anyline.modules.energy.EnergyResultListener;
import at.nineyards.anyline.modules.energy.EnergyScanView;
import io.anyline.examples.R;
import io.anyline.examples.ResultDialogBuilder;
import io.anyline.examples.SettingsFragment;

/**
 * Example activity for the Anyline-Energy-Module
 */
public class ScanAutoAnalogMeterActivity extends AppCompatActivity implements CameraOpenListener {

    private static final String TAG = ScanAnalogMeterActivity.class.getSimpleName();
    protected ScanView energyScanView;
    private String lastDetectedBarcodeValue = "";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scan_energy);

        // get the view from the layout (check out the xml for the configuration of the view)
        energyScanView = (ScanView) findViewById(R.id.scan_view);

        Switch barcodeDetectionSwitch = (Switch) findViewById(R.id.barcode_scanner_switch);

        // set a listener on the switch to enable and disable barcode detection
        barcodeDetectionSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
                lastDetectedBarcodeValue = "";
                if (isChecked) {
                    Log.d(TAG, "barcode detection enabled");

                    // enables the barcode detection for the full image (the preview view, rather than the cutout only)
                    energyScanView.enableBarcodeDetection(new NativeBarcodeResultListener() {
                        @Override
                        public void onBarcodesReceived(SparseArray<com.google.android.gms.vision.barcode.Barcode> sparseArray) {

                            // For this demonstration purpose, we only use the latest barcode that has been found.
                            // However, note that you receive a list of barcodes, e.g. it detects multiple barcodes at once.
                            // Also the listener is called every time barcodes are found on a frame,
                            // so it is independent from the energy result
                            if (sparseArray.size() > 0) {
                                lastDetectedBarcodeValue = sparseArray.valueAt(0).displayValue;
                            }
                        }
                    });
                } else {
                    Log.d(TAG, "barcode detection disabled");
                    energyScanView.disableBarcodeDetection();
                }
            }
        });

        // add a camera open listener that will be called when the camera is opened or an error occurred
        //  this is optional (if not set a RuntimeException will be thrown if an error occurs)
        energyScanView.setCameraOpenListener(this);

        // set reporting according to prefs or true on default
        energyScanView.setReportingEnabled(PreferenceManager.getDefaultSharedPreferences(this).getBoolean(SettingsFragment
                .KEY_PREF_REPORTING_ON, true));
        //init the scanViewPlugin configuration which hold the scan view ui configuration (cutoutConfig and ScanFeedbackConfig)		
	ScanViewPluginConfig energyScanviewPluginConfig = new ScanViewPluginConfig(getApplicationContext(), "energy_view_config.json");
	//init the scan view
	MeterScanViewPlugin scanViewPlugin = new MeterScanViewPlugin(getApplicationContext(), getString(R.string.anyline_license_key), energyScanviewPluginConfig, "METER");
	//init the base scanViewconfig which hold camera and flash configuration
	BaseScanViewConfig energyBaseScanViewConfig = new BaseScanViewConfig(getApplicationContext(), "energy_view_config.json");
	//set the base scanViewConfig to the ScanView
	energyScanView.setScanViewConfig(energyBaseScanViewConfig);
	//set the scanViewPlugin to the ScanView
	energyScanView.setScanViewPlugin(scanViewPlugin);
	//add result listener
	scanViewPlugin.addScanResultListener(new ScanResultListener<MeterScanResult>() {
		@Override
		public void onResult(MeterScanResult energyResult) {
			new ResultDialogBuilder(ScanAnalogMeterActivity.this)
                        .setResultImage(energyResult.getCutoutImage())
                        .setTextSize(TypedValue.COMPLEX_UNIT_DIP, 22)
                        .setTextGravity(Gravity.CENTER)
                        .setText(energyResult.getResult())
                        .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                // reset the last detected barcode value, as it has already been displayed
                                lastDetectedBarcodeValue = "";
                                if (!energyScanView.isRunning()) {
                                    energyScanView.startScanning();
                                }
                            }
                        })
                        .setTitle(getString(R.string.title_analog_meter))
                        .setOnCancelListener(new DialogInterface.OnCancelListener() {
                            @Override
                            public void onCancel(DialogInterface dialogInterface) {
                                // reset the last detected barcode value, as it has already been displayed
                                lastDetectedBarcodeValue = "";
                                if (!energyScanView.isRunning()) {
                                    energyScanView.startScanning();
                                }
                            }
                        })
                        .show();
		}
	});

        // ANALOG_METER will work for all types of analog meters (gas, electric, water) and
        // automatically detects digits before and after the point
        ((MeterScanViewPlugin)energyScanView.getScanViewPlugin()).setScanMode(MeterScanMode.AUTO_ANALOG_DIGITAL_METER);
    }


    @Override
    protected void onResume() {
        super.onResume();
        //start the actual scanning
        energyScanView.start();
    }

    @Override
    protected void onPause() {
        super.onPause();
        //stop the scanning
        energyScanView.stop();
        //release the camera (must be called in onPause, because there are situations where
        // it cannot be auto-detected that the camera should be released)
        energyScanView.releaseCameraInBackground();
    }

    @Override
    public void onCameraOpened(final CameraController cameraController, int width, int height) {
        //the camera is opened async and this is called when the opening is finished
        Log.d(TAG, "Camera opened successfully. Frame resolution " + width + " x " + height);
    }

    @Override
    public void onCameraError(Exception e) {
        //This is called if the camera could not be opened.
        // (e.g. If there is no camera or the permission is denied)
        // This is useful to present an alternative way to enter the required data if no camera exists.
        throw new RuntimeException(e);
    }

}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
#import "NSUserDefaults+ALExamplesAdditions.h"
#import "ALAutoAnalogDigitalMeterScanViewController.h"
#import <Anyline/Anyline.h>
#import "ALAppDemoLicenses.h"
#import "ALResultEntry.h"
#import "ALResultViewController.h"



// This is the license key for the examples project used to set up Aynline below
NSString * const kAutoAnalogDigitalMeterScanLicenseKey = kDemoAppLicenseKey;

static const NSInteger padding = 7;

// The controller has to conform to <AnylineEnergyModuleDelegate> to be able to receive results
@interface ALAutoAnalogDigitalMeterScanViewController ()<ALMeterScanPluginDelegate, AnylineNativeBarcodeDelegate>

// The Anyline plugins used to scan
@property (nonatomic, strong) ALMeterScanViewPlugin *meterScanViewPlugin;
@property (nonatomic, strong) ALMeterScanPlugin *meterScanPlugin;
@property (nullable, nonatomic, strong) ALScanView *scanView;

//Native barcode scanning properties
@property (nonatomic, strong) NSString *barcodeResult;

@property (nonatomic, strong) UIView *enableBarcodeView;
@property (nonatomic, strong) UISwitch *enableBarcodeSwitch;
@property (nonatomic, strong) UILabel *enableBarcodeLabel;

@end

@implementation ALAutoAnalogDigitalMeterScanViewController

/*
 We will do our main setup in viewDidLoad. Its called once the view controller is getting ready to be displayed.
 */
- (void)viewDidLoad {
    [super viewDidLoad];
    
    // Set the background color to black to have a nicer transition
    self.view.backgroundColor = [UIColor blackColor];
    
    self.title = @"Analog/Digital Meter";
    
    CGRect frame = [[UIScreen mainScreen] applicationFrame];
    frame = CGRectMake(frame.origin.x, frame.origin.y + self.navigationController.navigationBar.frame.size.height, frame.size.width, frame.size.height - self.navigationController.navigationBar.frame.size.height);
    
    //Add Meter Scan Plugin (Scan Process)
    NSError *error = nil;
    self.meterScanPlugin = [[ALMeterScanPlugin alloc] initWithPluginID:@"ENERGY" licenseKey:kDemoAppLicenseKey delegate:self error:&error];
    NSAssert(self.meterScanPlugin, @"Setup Error: %@", error.debugDescription);
    
    //Add Meter Scan View Plugin (Scan UI)
    self.meterScanViewPlugin = [[ALMeterScanViewPlugin alloc] initWithScanPlugin:self.meterScanPlugin];
    
    
    //Set ScanMode to ALAutoAnalogDigitalMeter
    BOOL success = [self.meterScanPlugin setScanMode:ALAutoAnalogDigitalMeter error:&error];
    if( !success ) {
        // Something went wrong. The error object contains the error description
        [[[UIAlertView alloc] initWithTitle:@"Set ScanMode Error"
                                    message:error.debugDescription
                                   delegate:self
                          cancelButtonTitle:@"OK"
                          otherButtonTitles:nil] show];
        
    }
    
    //Add ScanView (Camera and Flashbutton)
    self.scanView = [[ALScanView alloc] initWithFrame:frame scanViewPlugin:self.meterScanViewPlugin];
    
    [self.view addSubview:self.scanView];
    [self.scanView startCamera];
    
    BOOL enableReporting = [NSUserDefaults AL_reportingEnabled];
    [self.meterScanPlugin enableReporting:enableReporting];
    self.meterScanViewPlugin.translatesAutoresizingMaskIntoConstraints = NO;
    
    //After setup is complete we add the module to the view of this view controller
    [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scanView]|" options:0 metrics:nil views:@{@"scanView" : self.scanView}]];
    id topGuide = self.topLayoutGuide;
    [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[topGuide]-0-[scanView]|" options:0 metrics:nil views:@{@"scanView" : self.scanView, @"topGuide" : topGuide}]];

    
    self.controllerType = ALScanHistoryElectricMeter;

    self.barcodeResult = @"";
    [self.view sendSubviewToBack:self.scanView];
    [self.scanView addSubview:[self createBarcoeSwitchView]];
    [self.scanView bringSubviewToFront:self.enableBarcodeView];
}

/*
 This method will be called once the view controller and its subviews have appeared on screen
 */
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    /*
     This is the place where we tell Anyline to start receiving and displaying images from the camera.
     Success/error tells us if everything went fine.
     */
    NSError *error = nil;
    BOOL success = [self.meterScanViewPlugin startAndReturnError:&error];
    if( !success ) {
        // Something went wrong. The error object contains the error description
        [[[UIAlertView alloc] initWithTitle:@"Start Scanning Error"
                                    message:error.debugDescription
                                   delegate:self
                          cancelButtonTitle:@"OK"
                          otherButtonTitles:nil] show];
    }
}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
        
    [self updateLayoutBarcodeSwitchView];
}

/*
 Cancel scanning to allow the module to clean up
 */
- (void)viewWillDisappear:(BOOL)animated {
    [self.meterScanViewPlugin stopAndReturnError:nil];
}

#pragma mark - IBAction methods


- (IBAction)toggleBarcodeScanning:(id)sender {
    
    if (self.scanView.captureDeviceManager.barcodeDelegates.count > 0) {
        self.enableBarcodeSwitch.on = false;
        [self.scanView.captureDeviceManager removeBarcodeDelegate:self];
        //reset found barcode
        self.barcodeResult = @"";
    } else {
        self.enableBarcodeSwitch.on = true;
        [self.scanView.captureDeviceManager addBarcodeDelegate:self error:nil];
    }
}

#pragma mark - Barcode View layouting
- (UIView *)createBarcoeSwitchView {
    //Add UISwitch for toggling barcode scanning
    self.enableBarcodeView = [[UIView alloc] init];
    self.enableBarcodeView.frame = CGRectMake(100, 100, 150, 50);
    
    self.enableBarcodeLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 30)];
    self.enableBarcodeLabel.text = @"Barcode Detection";
    UIFont *font = [UIFont systemFontOfSize:14 weight:UIFontWeightThin];
    self.enableBarcodeLabel.font = font;
    self.enableBarcodeLabel.numberOfLines = 0;
    
    self.enableBarcodeLabel.textColor = [UIColor whiteColor];
    [self.enableBarcodeLabel sizeToFit];
    
    self.enableBarcodeSwitch = [[UISwitch alloc] init];
    [self.enableBarcodeSwitch setOn:false];
    self.enableBarcodeSwitch.onTintColor = [UIColor whiteColor];
    [self.enableBarcodeSwitch setOnTintColor:[UIColor colorWithRed:0.0/255.0 green:153.0/255.0 blue:255.0/255.0 alpha:1.0]];
    [self.enableBarcodeSwitch addTarget:self action:@selector(toggleBarcodeScanning:) forControlEvents:UIControlEventValueChanged];
    
    [self.enableBarcodeView addSubview:self.enableBarcodeLabel];
    [self.enableBarcodeView addSubview:self.enableBarcodeSwitch];
    
    return self.enableBarcodeView;
}

- (void)updateLayoutBarcodeSwitchView {
    self.enableBarcodeLabel.center = CGPointMake(self.enableBarcodeLabel.frame.size.width/2,
                                                 self.enableBarcodeView.frame.size.height/2);

    self.enableBarcodeSwitch.center = CGPointMake(self.enableBarcodeLabel.frame.size.width + self.enableBarcodeSwitch.frame.size.width/2 + padding,
                                                  self.enableBarcodeView.frame.size.height/2);

    CGFloat width = self.enableBarcodeSwitch.frame.size.width + padding + self.enableBarcodeLabel.frame.size.width;
    self.enableBarcodeView.frame = CGRectMake(self.scanView.frame.size.width-width-15,
                                              self.scanView.frame.size.height-self.enableBarcodeView.frame.size.height-55,
                                              width,
                                              50);
}

#pragma mark - ALMeterScanPluginDelegate methods
/*
 The main delegate method Anyline uses to report its scanned codes
 */
- (void)anylineMeterScanPlugin:(ALMeterScanPlugin *)anylineMeterScanPlugin
                 didFindResult:(ALMeterResult *)scanResult {
    
    [self anylineDidFindResult:scanResult.result barcodeResult:self.barcodeResult image:(UIImage*)scanResult.image scanPlugin:anylineMeterScanPlugin viewPlugin:self.meterScanViewPlugin  completion:^{
        //Display the result
        NSMutableArray <ALResultEntry*> *resultData = [[NSMutableArray alloc] init];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Meter Reading" value:scanResult.result]];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Barcode" value:self.barcodeResult]];
        
        ALResultViewController *vc = [[ALResultViewController alloc] initWithResultData:resultData image:scanResult.image];
        [self.navigationController pushViewController:vc animated:YES];
    }];
    
    //reset found barcodes
    self.barcodeResult = @"";
}

#pragma mark - AnylineNativeBarcodeDelegate methods
/*
 An additional delegate which will add all found, and unique, barcodes to a Dictionary simultaneously.
 */
- (void)anylineCaptureDeviceManager:(ALCaptureDeviceManager *)captureDeviceManager didFindBarcodeResult:(NSString *)scanResult type:(NSString *)barcodeType {
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([scanResult length] > 0 && ![self.barcodeResult isEqualToString:scanResult]) {
            self.barcodeResult = scanResult;
        }
    });
}


@end
Meter Plugin Analog (w/simultaneous barcode scanning)
    <at.nineyards.anyline.modules.energy.EnergyScanView
        android:id="@+id/energy_scan_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:cutout_alignment="top"
        app:cutout_style="rect"
        app:cutout_outside_color="#55000000"
        app:cutout_offset_y="120"
        app:cutout_rect_corner_radius_in_dp="4"
        app:cutout_stroke_width_in_dp="2"
        app:cutout_stroke_color="#FFFFFF"
        app:flash_mode="manual"
        app:flash_alignment="bottom_right"
        app:beep_on_result="true"
        app:vibrate_on_result="true"
        app:blink_animation_on_result="true"
        app:cancel_on_result="true"
        app:visual_feedback_style="rect"
        app:visual_feedback_stroke_color="#0099FF"
        />
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package io.anyline.examples.meter;

import android.content.DialogInterface;
import android.graphics.Color;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.WindowManager;
import android.widget.CompoundButton;
import android.widget.Switch;

import at.nineyards.anyline.camera.CameraController;
import at.nineyards.anyline.camera.CameraOpenListener;
import at.nineyards.anyline.models.AnylineImage;
import at.nineyards.anyline.modules.barcode.NativeBarcodeResultListener;
import at.nineyards.anyline.modules.energy.EnergyResult;
import at.nineyards.anyline.modules.energy.EnergyResultListener;
import at.nineyards.anyline.modules.energy.EnergyScanView;
import io.anyline.examples.R;
import io.anyline.examples.ResultDialogBuilder;
import io.anyline.examples.SettingsFragment;

/**
 * Example activity for the Anyline-Energy-Module
 */
public class ScanAnalogMeterActivity extends AppCompatActivity implements CameraOpenListener {

    private static final String TAG = ScanAnalogMeterActivity.class.getSimpleName();
    protected ScanView energyScanView;
    private String lastDetectedBarcodeValue = "";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scan_energy);

        // get the view from the layout (check out the xml for the configuration of the view)
        energyScanView = (ScanView) findViewById(R.id.scan_view);

        Switch barcodeDetectionSwitch = (Switch) findViewById(R.id.barcode_scanner_switch);

        // set a listener on the switch to enable and disable barcode detection
        barcodeDetectionSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
                lastDetectedBarcodeValue = "";
                if (isChecked) {
                    Log.d(TAG, "barcode detection enabled");

                    // enables the barcode detection for the full image (the preview view, rather than the cutout only)
                    energyScanView.enableBarcodeDetection(new NativeBarcodeResultListener() {
                        @Override
                        public void onBarcodesReceived(SparseArray<com.google.android.gms.vision.barcode.Barcode> sparseArray) {

                            // For this demonstration purpose, we only use the latest barcode that has been found.
                            // However, note that you receive a list of barcodes, e.g. it detects multiple barcodes at once.
                            // Also the listener is called every time barcodes are found on a frame,
                            // so it is independent from the energy result
                            if (sparseArray.size() > 0) {
                                lastDetectedBarcodeValue = sparseArray.valueAt(0).displayValue;
                            }
                        }
                    });
                } else {
                    Log.d(TAG, "barcode detection disabled");
                    energyScanView.disableBarcodeDetection();
                }
            }
        });

        // add a camera open listener that will be called when the camera is opened or an error occurred
        //  this is optional (if not set a RuntimeException will be thrown if an error occurs)
        energyScanView.setCameraOpenListener(this);

        // set reporting according to prefs or true on default
        energyScanView.setReportingEnabled(PreferenceManager.getDefaultSharedPreferences(this).getBoolean(SettingsFragment
                .KEY_PREF_REPORTING_ON, true));
        //init the scanViewPlugin configuration which hold the scan view ui configuration (cutoutConfig and ScanFeedbackConfig)		
	ScanViewPluginConfig energyScanviewPluginConfig = new ScanViewPluginConfig(getApplicationContext(), "energy_view_config.json.json");
	//init the scan view
	MeterScanViewPlugin scanViewPlugin = new MeterScanViewPlugin(getApplicationContext(), getString(R.string.anyline_license_key), energyScanviewPluginConfig, "METER");
	//init the base scanViewconfig which hold camera and flash configuration
	BaseScanViewConfig energyBaseScanViewConfig = new BaseScanViewConfig(getApplicationContext(), "energy_view_config.json.json");
	//set the base scanViewConfig to the ScanView
	energyScanView.setScanViewConfig(energyBaseScanViewConfig);
	//set the scanViewPlugin to the ScanView
	energyScanView.setScanViewPlugin(scanViewPlugin);
	//add result listener
	scanViewPlugin.addScanResultListener(new ScanResultListener<MeterScanResult>() {
		@Override
		public void onResult(MeterScanResult energyResult) {
			new ResultDialogBuilder(ScanAnalogMeterActivity.this)
                        .setResultImage(energyResult.getCutoutImage())
                        .setTextSize(TypedValue.COMPLEX_UNIT_DIP, 22)
                        .setTextGravity(Gravity.CENTER)
                        .setText(energyResult.getResult())
                        .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                // reset the last detected barcode value, as it has already been displayed
                                lastDetectedBarcodeValue = "";
                                if (!energyScanView.isRunning()) {
                                    energyScanView.startScanning();
                                }
                            }
                        })
                        .setTitle(getString(R.string.title_analog_meter))
                        .setOnCancelListener(new DialogInterface.OnCancelListener() {
                            @Override
                            public void onCancel(DialogInterface dialogInterface) {
                                // reset the last detected barcode value, as it has already been displayed
                                lastDetectedBarcodeValue = "";
                                if (!energyScanView.isRunning()) {
                                    energyScanView.startScanning();
                                }
                            }
                        })
                        .show();
		}
	});

        // ANALOG_METER will work for all types of analog meters (gas, electric, water) and
        // automatically detects digits before and after the point
        ((MeterScanViewPlugin)energyScanView.getScanViewPlugin()).setScanMode(MeterScanMode.AUTO_ANALOG_DIGITAL_METER);
    }


    @Override
    protected void onResume() {
        super.onResume();
        //start the actual scanning
        energyScanView.start();
    }

    @Override
    protected void onPause() {
        super.onPause();
        //stop the scanning
        energyScanView.stop();
        //release the camera (must be called in onPause, because there are situations where
        // it cannot be auto-detected that the camera should be released)
        energyScanView.releaseCameraInBackground();
    }

    @Override
    public void onCameraOpened(final CameraController cameraController, int width, int height) {
        //the camera is opened async and this is called when the opening is finished
        Log.d(TAG, "Camera opened successfully. Frame resolution " + width + " x " + height);
    }

    @Override
    public void onCameraError(Exception e) {
        //This is called if the camera could not be opened.
        // (e.g. If there is no camera or the permission is denied)
        // This is useful to present an alternative way to enter the required data if no camera exists.
        throw new RuntimeException(e);
    }

}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#import "NSUserDefaults+ALExamplesAdditions.h"
#import "ALAnalogMeterScanViewController.h"
#import "ALMeterScanResultViewController.h"
#import <Anyline/Anyline.h>
#import "ALAppDemoLicenses.h"

// This is the license key for the examples project used to set up Aynline below
NSString * const kAnalogMeterScanLicenseKey = kDemoAppLicenseKey;

static const NSInteger padding = 7;

// The controller has to conform to <AnylineEnergyModuleDelegate> to be able to receive results
@interface ALAnalogMeterScanViewController ()<AnylineEnergyModuleDelegate, AnylineNativeBarcodeDelegate>

// The Anyline module used to scan
@property (nonatomic, strong) AnylineEnergyModuleView *anylineEnergyView;

//Native barcode scanning properties
@property (nonatomic, strong) NSString *barcodeResult;

@property (nonatomic, strong) UIView *enableBarcodeView;
@property (nonatomic, strong) UISwitch *enableBarcodeSwitch;
@property (nonatomic, strong) UILabel *enableBarcodeLabel;

@end

@implementation ALAnalogMeterScanViewController

/*
 We will do our main setup in viewDidLoad. Its called once the view controller is getting ready to be displayed.
 */
- (void)viewDidLoad {
    [super viewDidLoad];
    
    // Set the background color to black to have a nicer transition
    self.view.backgroundColor = [UIColor blackColor];
    
    self.title = @"Electric Meter"; //Analog Meter
    CGRect frame = [[UIScreen mainScreen] applicationFrame];
    frame = CGRectMake(frame.origin.x, frame.origin.y + self.navigationController.navigationBar.frame.size.height, frame.size.width, frame.size.height - self.navigationController.navigationBar.frame.size.height);
    self.anylineEnergyView = [[AnylineEnergyModuleView alloc] initWithFrame:frame];
    
    NSError *error = nil;
    // We tell the module to bootstrap itself with the license key and delegate. The delegate will later get called
    // once we start receiving results.
    BOOL success = [self.anylineEnergyView setupWithLicenseKey:kAnalogMeterScanLicenseKey delegate:self error:&error];
    
    // setupWithLicenseKey:delegate:error returns true if everything went fine. In the case something wrong
    // we have to check the error object for the error message.
    if( !success ) {
        // Something went wrong. The error object contains the error description
        [[[UIAlertView alloc] initWithTitle:@"Setup Error"
                                    message:error.debugDescription
                                   delegate:self
                          cancelButtonTitle:@"OK"
                          otherButtonTitles:nil] show];
    }
    
    self.anylineEnergyView.translatesAutoresizingMaskIntoConstraints = NO;
    
    //Set ScanMode to ALAnalogMeter
    success = [self.anylineEnergyView setScanMode:ALAnalogMeter error:&error];
    if( !success ) {
        // Something went wrong. The error object contains the error description
        [[[UIAlertView alloc] initWithTitle:@"Set ScanMode Error"
                                    message:error.debugDescription
                                   delegate:self
                          cancelButtonTitle:@"OK"
                          otherButtonTitles:nil] show];
    }
    
    BOOL enableReporting = [NSUserDefaults AL_reportingEnabled];
    [self.anylineEnergyView enableReporting:enableReporting];
    self.anylineEnergyView.translatesAutoresizingMaskIntoConstraints = NO;
    
    // After setup is complete we add the module to the view of this view controller
    [self.view addSubview:self.anylineEnergyView];
    [self.view sendSubviewToBack:self.anylineEnergyView];
    
    [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[moduleView]|" options:0 metrics:nil views:@{@"moduleView" : self.anylineEnergyView}]];
    
    self.controllerType = ALScanHistoryElectricMeter;
    
    id topGuide = self.topLayoutGuide;
    [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[topGuide]-0-[moduleView]|" options:0 metrics:nil views:@{@"moduleView" : self.anylineEnergyView, @"topGuide" : topGuide}]];
    
    self.barcodeResult = @"";
    [self.anylineEnergyView addSubview:[self createBarcoeSwitchView]];
}

/*
 This method will be called once the view controller and its subviews have appeared on screen
 */
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    /*
     This is the place where we tell Anyline to start receiving and displaying images from the camera.
     Success/error tells us if everything went fine.
     */
    NSError *error = nil;
    BOOL success = [self.anylineEnergyView startScanningAndReturnError:&error];
    if( !success ) {
        // Something went wrong. The error object contains the error description
        [[[UIAlertView alloc] initWithTitle:@"Start Scanning Error"
                                    message:error.debugDescription
                                   delegate:self
                          cancelButtonTitle:@"OK"
                          otherButtonTitles:nil] show];
    }
}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
        
    [self updateLayoutBarcodeSwitchView];
}

/*
 Cancel scanning to allow the module to clean up
 */
- (void)viewWillDisappear:(BOOL)animated {
    [self.anylineEnergyView cancelScanningAndReturnError:nil];
}

#pragma mark - IBAction methods


- (IBAction)toggleBarcodeScanning:(id)sender {
    
    if (self.anylineEnergyView.captureDeviceManager.barcodeDelegates.count > 0) {
        self.enableBarcodeSwitch.on = false;
        [self.anylineEnergyView.captureDeviceManager removeBarcodeDelegate:self];
        //reset found barcode
        self.barcodeResult = @"";
    } else {
        self.enableBarcodeSwitch.on = true;
        [self.anylineEnergyView.captureDeviceManager addBarcodeDelegate:self error:nil];
    }
}

#pragma mark - Barcode View layouting
- (UIView *)createBarcoeSwitchView {
    //Add UISwitch for toggling barcode scanning
    self.enableBarcodeView = [[UIView alloc] init];
    self.enableBarcodeView.frame = CGRectMake(0, 0, 150, 50);
    
    self.enableBarcodeLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 30)];
    self.enableBarcodeLabel.text = @"Barcode Detection";
    UIFont *font = [UIFont systemFontOfSize:14 weight:UIFontWeightThin];
    self.enableBarcodeLabel.font = font;
    self.enableBarcodeLabel.numberOfLines = 0;
    
    self.enableBarcodeLabel.textColor = [UIColor whiteColor];
    [self.enableBarcodeLabel sizeToFit];
    
    self.enableBarcodeSwitch = [[UISwitch alloc] init];
    [self.enableBarcodeSwitch setOn:false];
    self.enableBarcodeSwitch.onTintColor = [UIColor whiteColor];
    [self.enableBarcodeSwitch setOnTintColor:[UIColor colorWithRed:0.0/255.0 green:153.0/255.0 blue:255.0/255.0 alpha:1.0]];
    [self.enableBarcodeSwitch addTarget:self action:@selector(toggleBarcodeScanning:) forControlEvents:UIControlEventValueChanged];
    
    [self.enableBarcodeView addSubview:self.enableBarcodeLabel];
    [self.enableBarcodeView addSubview:self.enableBarcodeSwitch];
    
    return self.enableBarcodeView;
}

- (void)updateLayoutBarcodeSwitchView {
    self.enableBarcodeLabel.center = CGPointMake(self.enableBarcodeLabel.frame.size.width/2,
                                                 self.enableBarcodeView.frame.size.height/2);
    
    self.enableBarcodeSwitch.center = CGPointMake(self.enableBarcodeLabel.frame.size.width + self.enableBarcodeSwitch.frame.size.width/2 + padding,
                                                  self.enableBarcodeView.frame.size.height/2);
    
    CGFloat width = self.enableBarcodeSwitch.frame.size.width + padding + self.enableBarcodeLabel.frame.size.width;
    self.enableBarcodeView.frame = CGRectMake(self.anylineEnergyView.frame.size.width-width-15,
                                              self.anylineEnergyView.frame.size.height-self.enableBarcodeView.frame.size.height-55,
                                              width,
                                              50);
}

#pragma mark - AnylineControllerDelegate methods
/*
 The main delegate method Anyline uses to report its scanned codes
 */
- (void)anylineEnergyModuleView:(AnylineEnergyModuleView *)anylineEnergyModuleView 
                  didFindResult:(ALEnergyResult *)scanResult {
    [self anylineDidFindResult:scanResult.result barcodeResult:self.barcodeResult image:(UIImage*)scanResult.image module:anylineEnergyModuleView completion:^{
        ALMeterScanResultViewController *vc = [[ALMeterScanResultViewController alloc] init];
        /*
         To present the scanned result to the user we use a custom view controller.
         */
        vc.scanMode = scanResult.scanMode;
        vc.meterImage = scanResult.image;
        vc.result = scanResult.result;
        vc.barcodeResult = self.barcodeResult;
        [self.navigationController pushViewController:vc animated:YES];
    }];
    
    //reset found barcodes
    self.barcodeResult = @"";
}

#pragma mark - AnylineNativeBarcodeDelegate methods
/*
 An additional delegate which will add all found, and unique, barcodes to a Dictionary simultaneously.
 */
- (void)anylineCaptureDeviceManager:(ALCaptureDeviceManager *)captureDeviceManager didFindBarcodeResult:(NSString *)scanResult type:(NSString *)barcodeType {
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([scanResult length] > 0 && ![self.barcodeResult isEqualToString:scanResult]) {
            self.barcodeResult = scanResult;
        }
    });
}


@end
ID Plugin MRZ
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
{
    "captureResolution":"1080p",

    "cutout": {
        "style": "rect",
        "maxWidthPercent": "90%",
        "maxHeightPercent": "90%",
        "alignment": "center",
        "strokeWidth": 2,
        "cornerRadius": 4,
        "strokeColor": "FFFFFF",
        "outerColor": "000000",
        "outerAlpha": 0.3,
        "feedbackStrokeColor": "0099FF",
        "cropPadding": {
            "x": -30,
            "y": -90
        },
        "cropOffset": {
            "x": 0,
            "y": 90
        }
    },
    "flash": {
        "mode": "manual",
        "alignment": "bottom_right"
    },
    "beepOnResult": true,
    "vibrateOnResult": true,
    "blinkAnimationOnResult": true,
    "cancelOnResult": true,
    "visualFeedback": {
        "style": "rect",
        "strokeColor": "0099FF",
        "strokeWidth": 2

    }
}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 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
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;

import at.nineyards.anyline.camera.CameraController;
import at.nineyards.anyline.camera.CameraOpenListener;
import at.nineyards.anyline.models.AnylineImage;
import at.nineyards.anyline.modules.mrz.Identification;
import at.nineyards.anyline.modules.mrz.MrzResult;
import at.nineyards.anyline.modules.mrz.MrzResultListener;
import at.nineyards.anyline.modules.mrz.MrzScanView;
import io.anyline.examples.R;

/**
 * Example Activity for the Anyline-MRZ-Module.
 */
public class ScanMrzActivity extends AppCompatActivity implements CameraOpenListener {

    private static final String TAG = ScanMrzActivity.class.getSimpleName();
    private ScanView mrzScanView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scan_mrz);
        //Set the flag to keep the screen on (otherwise the screen may go dark during scanning)
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        mrzScanView = (MrzScanView) findViewById(R.id.scan_view);

        // add a camera open listener that will be called when the camera is opened or an error occurred
        //  this is optional (if not set a RuntimeException will be thrown if an error occurs)
        mrzScanView.setCameraOpenListener(this);
	//add the mrzConfig specific for MRZ
	MrzConfig mrzConfig = new MrzConfig();
	mrzConfig.setStrictMode(false);

	//init the scanViewPlugin configuration which hold the scan view ui configuration (cutoutConfig and ScanFeedbackConfig)		
	ScanViewPluginConfig mrzScanViewPluginConfig = new ScanViewPluginConfig(getApplicationContext(), "mrz_view_config.json");
	//init the scan view
	IdScanViewPlugin scanViewPlugin = new IdScanViewPlugin(getApplicationContext(), getString(R.string.anyline_license_key), mrzScanViewPluginConfig, mrzConfig);
	//init the base scanViewconfig which hold camera and flash configuration
	BaseScanViewConfig scanViewBaseConfig = new BaseScanViewConfig(getApplicationContext(), "mrz_view_config.json");
	//set the base scanViewConfig to the ScanView
	mrzScanView.setScanViewConfig(scanViewBaseConfig);
	//set the scanViewPlugin to the ScanView
	mrzScanView.setScanViewPlugin(scanViewPlugin);
	//add the result listener	
	scanViewPlugin.addScanResultListener(new ScanResultListener<ScanResult<ID>>() {
			@Override
			public void onResult(ScanResult<ID> idScanResult) {
				Identification identification = (Identification) idScanResult.getResult();
				identification.toJSONObject();

				//set the path of the mrz Image
				String path = setupImagePath(idScanResult.getCutoutImage());



				startScanResultIntent(getResources().getString(R.string.title_mrz), getIdentificationResult(identification), path);
				setupScanProcessView(ScanMrzActivity.this, idScanResult, getScanModule());
			}


		});

    }

    @Override
    protected void onResume() {
        super.onResume();
        //start the actual scanning
        mrzScanView.start();
    }

    @Override
    protected void onPause() {
        super.onPause();
        //stop the scanning
        mrzScanView.stop();
        //release the camera (must be called in onPause, because there are situations where
        // it cannot be auto-detected that the camera should be released)
        mrzScanView.releaseCameraInBackground();
    }

    @Override
    public void onBackPressed() {
        //close the result view on back press if it is open
        if (mrzResultView.getVisibility() == View.VISIBLE) {
            mrzResultView.setVisibility(View.INVISIBLE);
            if (!mrzScanView.isRunning()) {
                mrzScanView.startScanning();
            }
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public void onCameraOpened(CameraController cameraController, int width, int height) {
        //the camera is opened async and this is called when the opening is finished
        Log.d(TAG, "Camera opened successfully. Frame resolution " + width + " x " + height);
    }

    @Override
    public void onCameraError(Exception e) {
        //This is called if the camera could not be opened.
        // (e.g. If there is no camera or the permission is denied)
        // This is useful to present an alternative way to enter the required data if no camera exists.
        throw new RuntimeException(e);
    }
}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#import "ALMRZScanViewController.h"
#import "ALIdentificationView.h"
#import <Anyline/Anyline.h>
#import "NSUserDefaults+ALExamplesAdditions.h"
#import "ALAppDemoLicenses.h"
#import "ALResultViewController.h"

// This is the license key for the examples project used to set up Aynline below
NSString * const kMRZLicenseKey = kDemoAppLicenseKey;
// The controller has to conform to <AnylineMRZModuleDelegate> to be able to receive results
@interface ALMRZScanViewController ()<ALIDPluginDelegate, ALInfoDelegate>

// The Anyline module used to scan machine readable zones
@property (nonatomic, strong) ALIDScanViewPlugin *mrzScanViewPlugin;
@property (nonatomic, strong) ALIDScanPlugin *mrzScanPlugin;
@property (nullable, nonatomic, strong) ALScanView *scanView;

@end

@implementation ALMRZScanViewController
/*
 We will do our main setup in viewDidLoad. Its called once the view controller is getting ready to be displayed.
 */
- (void)viewDidLoad {
    [super viewDidLoad];
    // Set the background color to black to have a nicer transition
    self.view.backgroundColor = [UIColor blackColor];
    self.title = @"MRZ";
    
    // Initializing the module. Its a UIView subclass. We set the frame to fill the whole screen
    CGRect frame = [[UIScreen mainScreen] applicationFrame];
    frame = CGRectMake(frame.origin.x, frame.origin.y + self.navigationController.navigationBar.frame.size.height, frame.size.width, frame.size.height - self.navigationController.navigationBar.frame.size.height);
    
    ALMRZConfig *mrzConfig = [[ALMRZConfig alloc] init];
    
    NSError *error = nil;

    self.mrzScanPlugin = [[ALIDScanPlugin alloc] initWithPluginID:@"ModuleID" licenseKey:kMRZLicenseKey delegate:self idConfig:mrzConfig error:&error];
    NSAssert(self.mrzScanPlugin, @"Setup Error: %@", error.debugDescription);
    [self.mrzScanPlugin addInfoDelegate:self];
    
    self.mrzScanViewPlugin = [[ALIDScanViewPlugin alloc] initWithScanPlugin:self.mrzScanPlugin];
    NSAssert(self.mrzScanViewPlugin, @"Setup Error: %@", error.debugDescription);
    
    self.scanView = [[ALScanView alloc] initWithFrame:frame scanViewPlugin:self.mrzScanViewPlugin];
    
    self.scanView.flashButtonConfig.flashAlignment = ALFlashAlignmentTopLeft;
    self.mrzScanViewPlugin.translatesAutoresizingMaskIntoConstraints = NO;
    
    self.controllerType = ALScanHistoryMrz;
    
    // After setup is complete we add the module to the view of this view controller
    [self.view addSubview:self.scanView];
    //Start Camera:
    [self.scanView startCamera];
    
    [self.view sendSubviewToBack:self.scanView];
    
    [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scanView]|" options:0 metrics:nil views:@{@"scanView" : self.scanView}]];
    
    id topGuide = self.topLayoutGuide;
    [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[topGuide]-0-[scanView]|" options:0 metrics:nil views:@{@"scanView" : self.scanView, @"topGuide" : topGuide}]];
    
    [self startListeningForMotion];
}

/*
 This method will be called once the view controller and its subviews have appeared on screen
 */
-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    // We use this subroutine to start Anyline. The reason it has its own subroutine is
    // so that we can later use it to restart the scanning process.
    [self startAnyline];
    
    //Update Position of Warning Indicator
    [self updateWarningPosition:
     self.mrzScanViewPlugin.cutoutRect.origin.y +
     self.mrzScanViewPlugin.cutoutRect.size.height +
     self.mrzScanViewPlugin.frame.origin.y +
     120];
}

/*
 Cancel scanning to allow the module to clean up
 */
- (void)viewWillDisappear:(BOOL)animated {
    [self.mrzScanViewPlugin stopAndReturnError:nil];
}


/*
   This method is used to tell Anyline to start scanning. It gets called in 
   viewDidAppear to start scanning the moment the view appears. Once a result 
   is found scanning will stop automatically (you can change this behaviour 
   with cancelOnResult:). When the user dismisses self.identificationView this
   method will get called again.
 */
- (void)startAnyline { 
    NSError *error;
    BOOL success = [self.mrzScanViewPlugin startAndReturnError:&error];
    if( !success ) {
        // Something went wrong. The error object contains the error description
        [[[UIAlertView alloc] initWithTitle:@"Start Scanning Error"
                                    message:error.debugDescription
                                   delegate:self
                          cancelButtonTitle:@"OK"
                          otherButtonTitles:nil] show];
    }
    
    self.startTime = CACurrentMediaTime();
}

#pragma mark -- AnylineMRZModuleDelegate

/*
 This is the main delegate method Anyline uses to report its results
 */
- (void)anylineIDScanPlugin:(ALIDScanPlugin *)anylineIDScanPlugin didFindResult:(ALIDResult *)scanResult {
    [self.mrzScanViewPlugin stopAndReturnError:nil];
    NSMutableString * result = [NSMutableString string];
    [result appendString:[NSString stringWithFormat:@"Document Type: %@\n", [scanResult.result documentType]]];
    [result appendString:[NSString stringWithFormat:@"Document Number: %@\n", [scanResult.result documentNumber]]];
    [result appendString:[NSString stringWithFormat:@"Surnames: %@\n", [scanResult.result surNames]]];
    [result appendString:[NSString stringWithFormat:@"Given Names: %@\n", [scanResult.result givenNames]]];
    [result appendString:[NSString stringWithFormat:@"Issuing Country Code: %@\n", [scanResult.result issuingCountryCode]]];
    [result appendString:[NSString stringWithFormat:@"Nationality Country Code: %@\n", [scanResult.result nationalityCountryCode]]];
    [result appendString:[NSString stringWithFormat:@"Day Of Birth: %@\n", [scanResult.result dayOfBirth]]];
    [result appendString:[NSString stringWithFormat:@"Expiration Date: %@\n", [scanResult.result expirationDate]]];
    [result appendString:[NSString stringWithFormat:@"Sex: %@\n", [scanResult.result sex]]];
    [result appendString:[NSString stringWithFormat:@"Check Digit Number: %@\n", [scanResult.result checkdigitNumber]]];
    [result appendString:[NSString stringWithFormat:@"Check Digit Expiration Date: %@\n", [scanResult.result checkdigitExpirationDate]]];
    [result appendString:[NSString stringWithFormat:@"Check Digit Day Of Birth: %@\n", [scanResult.result checkdigitDayOfBirth]]];
    [result appendString:[NSString stringWithFormat:@"Check Digit Final: %@\n", [scanResult.result checkdigitFinal]]];
    [result appendString:[NSString stringWithFormat:@"Personal Number: %@\n", [scanResult.result personalNumber]]];
    [result appendString:[NSString stringWithFormat:@"Check Digit Personal Number: %@\n", [scanResult.result checkDigitPersonalNumber]]];
    
    [super anylineDidFindResult:result barcodeResult:@"" image:scanResult.image scanPlugin:anylineIDScanPlugin viewPlugin:self.mrzScanViewPlugin completion:^{
        
        NSMutableArray <ALResultEntry*> *resultData = [[NSMutableArray alloc] init];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Given Name" value:[scanResult.result givenNames]]];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Surname" value:[scanResult.result surNames]]];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Sex" value:[scanResult.result sex]]];
        [resultData addObject:[self resultEntryWithDate:[scanResult.result dayOfBirthDateObject] dateString:[scanResult.result dayOfBirth] title:@"Date of Birth"]];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Document Type" value:[scanResult.result documentType]]];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Document Number" value:[scanResult.result documentNumber]]];
        [resultData addObject:[self resultEntryWithDate:[scanResult.result expirationDateObject] dateString:[((ALMRZIdentification *)scanResult.result) expirationDate] title:@"Expiration Date"]];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Nationality" value:[scanResult.result nationalityCountryCode]]];
        
        ALResultViewController *vc = [[ALResultViewController alloc] initWithResultData:resultData image:scanResult.image optionalImageTitle:@"Detected Face Image" optionalImage:[scanResult.result faceImage]];
        [self.navigationController pushViewController:vc animated:YES];
    }];
}

- (ALResultEntry *)resultEntryWithDate:(NSDate *)date dateString:(NSString *)dateString title:(NSString *)title {
    if (![self checkDateIfAvailable:date dateString:dateString]) {
        return [[ALResultEntry alloc] initWithTitle:title value:nil];
    }
    
    NSString *value = [self formatDate:date];
    return [[ALResultEntry alloc] initWithTitle:title value:value isAvailable:(date)];
}

- (BOOL)checkDateIfAvailable:(NSDate *)date dateString:(NSString *)dateString {
    if (!date && dateString.length == 0) {
        return NO;
    }
    return YES;
}

- (NSString *)formatDate:(NSDate *)date {
    if (!date) {
        return @"Date not valid";
    }
    return [NSDateFormatter localizedStringFromDate:date dateStyle:NSDateFormatterMediumStyle timeStyle:NSDateFormatterNoStyle];
}

@end
ID Plugin Driving License
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
{
    "captureResolution":"1080p",

    "cutout": {
        "style": "rect",
        "maxWidthPercent": "90%",
        "maxHeightPercent": "90%",
        "alignment": "center",
        "strokeWidth": 2,
        "cornerRadius": 4,
        "strokeColor": "FFFFFF",
        "outerColor": "000000",
        "outerAlpha": 0.3,
        "feedbackStrokeColor": "0099FF",
        "cropPadding": {
            "x": -30,
            "y": -90
        },
        "cropOffset": {
            "x": 0,
            "y": 90
        }
    },
    "flash": {
        "mode": "manual",
        "alignment": "bottom_right"
    },
    "beepOnResult": true,
    "vibrateOnResult": true,
    "blinkAnimationOnResult": true,
    "cancelOnResult": true,
    "visualFeedback": {
        "style": "rect",
        "strokeColor": "0099FF",
        "strokeWidth": 2

    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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
import android.util.Log;
import android.view.ViewGroup;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import at.nineyards.anyline.camera.CameraController;
import at.nineyards.anyline.camera.CameraOpenListener;
import at.nineyards.anyline.modules.AnylineBaseModuleView;
import io.anyline.examples.R;
import io.anyline.examples.ScanActivity;
import io.anyline.examples.ScanModuleEnum;
import io.anyline.plugin.ScanResult;
import io.anyline.plugin.ScanResultListener;
import io.anyline.plugin.id.DrivingLicense;
import io.anyline.plugin.id.DrivingLicenseConfig;
import io.anyline.plugin.id.ID;
import io.anyline.plugin.id.IdScanViewPlugin;
import io.anyline.view.ScanView;

/**
 * Example Activity for the Anyline-DrivingLicense-Module.
 */
public class ScanDrivingLicense extends AppCompatActivity implements CameraOpenListener {

	private ScanView drivingLicenseScanView;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		getLayoutInflater().inflate(R.layout.activity_anyline_scan_view, (ViewGroup) findViewById(R.id.scan_view_placeholder));
		
		drivingLicenseScanView = (ScanView) findViewById(R.id.scan_view);

		//init the scanViewPlugin configuration which hold the scan view ui configuration (cutoutConfig and ScanFeedbackConfig)		
		ScanViewPluginConfig drivingLicenseScanViewConfig = new ScanViewPluginConfig(getApplicationContext(), "driving_license_view_config.json");
		//init the scan view
		IdScanViewPlugin scanViewPlugin = new IdScanViewPlugin(getApplicationContext(), getString(R.string.anyline_license_key), drivingLicenseScanViewConfig, new DrivingLicenseConfig());
		//init the base scanViewconfig which hold camera and flash configuration
		BaseScanViewConfig drivingLicenseScanViewBaseConfig = new BaseScanViewConfig(getApplicationContext(), "driving_license_view_config.json");
		//set the base scanViewConfig to the ScanView
		drivingLicenseScanView.setScanViewConfig(drivingLicenseScanViewBaseConfig);
		//set the scanViewPlugin to the ScanView
		drivingLicenseScanView.setScanViewPlugin(scanViewPlugin);

		scanViewPlugin.addScanResultListener(new ScanResultListener<ScanResult<ID>>() {
			@Override
			public void onResult(ScanResult<ID> idScanResult) {

				DrivingLicense resultString = (DrivingLicense) idScanResult.getResult();

			}


		});

		drivingLicenseScanView.setCameraOpenListener(this);
	}


	@Override
	protected void onResume() {
		super.onResume();

		drivingLicenseScanView.start();
	}

	@Override
	protected void onPause() {
		super.onPause();
		//stop the scanning
		drivingLicenseScanView.stop();
		//release the camera (must be called in onPause, because there are situations where
		// it cannot be auto-detected that the camera should be released)
		drivingLicenseScanView.releaseCameraInBackground();
	}

	@Override
	public void onBackPressed() {
		super.onBackPressed();
	}

	@Override
	public void onCameraOpened(CameraController cameraController, int width, int height) {
		//the camera is opened async and this is called when the opening is finished
		Log.d(TAG, "Camera opened successfully. Frame resolution " + width + " x " + height);
	}

	@Override
	public void onCameraError(Exception e) {
		//This is called if the camera could not be opened.
		// (e.g. If there is no camera or the permission is denied)
		// This is useful to present an alternative way to enter the required data if no camera exists.
		throw new RuntimeException(e);
	}
}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 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
115
116
117
118
119
120
121
122
123
124
125
#import "ALDrivingLicenseScanViewController.h"
#import "ALAppDemoLicenses.h"
#import "ALResultViewController.h"
#import <Anyline/Anyline.h>


// This is the license key for the examples project used to set up Aynline below
NSString * const kDrivingLicenseLicenseKey = kDemoAppLicenseKey;
@interface ALDrivingLicenseScanViewController ()<ALIDPluginDelegate, ALInfoDelegate>
// The Anyline module used to scan machine readable zones
@property (nonatomic, strong) ALIDScanViewPlugin *drivingLicenseScanViewPlugin;
@property (nonatomic, strong) ALIDScanPlugin *drivingLicenseScanPlugin;
@property (nullable, nonatomic, strong) ALScanView *scanView;

@end

@implementation ALDrivingLicenseScanViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Set the background color to black to have a nicer transition
    self.view.backgroundColor = [UIColor blackColor];
    self.title = @"Driving License";
    
    // Initializing the module. Its a UIView subclass. We set the frame to fill the whole screen
    CGRect frame = [[UIScreen mainScreen] applicationFrame];
    frame = CGRectMake(frame.origin.x, frame.origin.y + self.navigationController.navigationBar.frame.size.height, frame.size.width, frame.size.height - self.navigationController.navigationBar.frame.size.height);
    
    ALDrivingLicenseConfig *drivingLicenseConfig = [[ALDrivingLicenseConfig alloc] init];
    
    NSError *error = nil;
    self.drivingLicenseScanPlugin = [[ALIDScanPlugin alloc] initWithPluginID:@"ModuleID" licenseKey:kDrivingLicenseLicenseKey delegate:self idConfig:drivingLicenseConfig error:&error];
    NSAssert(self.drivingLicenseScanPlugin, @"Setup Error: %@", error.debugDescription);
    [self.drivingLicenseScanPlugin addInfoDelegate:self];
    
    self.drivingLicenseScanViewPlugin = [[ALIDScanViewPlugin alloc] initWithScanPlugin:self.drivingLicenseScanPlugin];
    NSAssert(self.drivingLicenseScanViewPlugin, @"Setup Error: %@", error.debugDescription);
    
    self.scanView = [[ALScanView alloc] initWithFrame:frame scanViewPlugin:self.drivingLicenseScanViewPlugin];
    
    self.scanView.flashButtonConfig.flashAlignment = ALFlashAlignmentTopLeft;
    
    self.controllerType = ALScanHistoryDrivingLicense;
    
    // After setup is complete we add the module to the view of this view controller
    [self.view addSubview:self.scanView];
    //Start Camera:
    [self.scanView startCamera];
    
    [self.view sendSubviewToBack:self.scanView];
    [self startListeningForMotion];
}

/*
 This method will be called once the view controller and its subviews have appeared on screen
 */
-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    // We use this subroutine to start Anyline. The reason it has its own subroutine is
    // so that we can later use it to restart the scanning process.
    [self startAnyline];
}

/*
 Cancel scanning to allow the module to clean up
 */
- (void)viewWillDisappear:(BOOL)animated {
    [self.drivingLicenseScanViewPlugin stopAndReturnError:nil];
}

/*
 This method is used to tell Anyline to start scanning. It gets called in
 viewDidAppear to start scanning the moment the view appears. Once a result
 is found scanning will stop automatically (you can change this behaviour
 with cancelOnResult:). When the user dismisses self.identificationView this
 method will get called again.
 */
- (void)startAnyline {
    NSError *error;
    BOOL success = [self.drivingLicenseScanViewPlugin startAndReturnError:&error];
    if( !success ) {
        // Something went wrong. The error object contains the error description
        NSAssert(success, @"Start Scanning Error: %@", error.debugDescription);
    }
}


#pragma mark -- AnylineOCRModuleDelegate

/*
 This is the main delegate method Anyline uses to report its results
 */
- (void)anylineIDScanPlugin:(ALIDScanPlugin *)anylineIDScanPlugin
              didFindResult:(ALIDResult *)scanResult {
    [self.drivingLicenseScanViewPlugin stopAndReturnError:nil];
    
    NSMutableString * result = [NSMutableString string];
    [result appendString:[NSString stringWithFormat:@"Document Number: %@\n", [scanResult.result documentNumber]]];
    [result appendString:[NSString stringWithFormat:@"Last Name: %@\n", [scanResult.result surNames]]];
    [result appendString:[NSString stringWithFormat:@"First Name: %@\n", [scanResult.result givenNames]]];
    [result appendString:[NSString stringWithFormat:@"Date of Birth: %@", [scanResult.result dayOfBirth]]];
    ;
   [super anylineDidFindResult:result barcodeResult:@"" image:scanResult.image scanPlugin:anylineIDScanPlugin viewPlugin:self.drivingLicenseScanViewPlugin completion:^{
        NSMutableArray <ALResultEntry*> *resultData = [[NSMutableArray alloc] init];

        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Last Name" value:[scanResult.result surNames]]];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"First Name" value:[scanResult.result givenNames]]];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Date of Birth" value:[scanResult.result dayOfBirth]]];
        [resultData addObject:[[ALResultEntry alloc] initWithTitle:@"Document Number" value:[scanResult.result documentNumber]]];
        
        //Display the result
        ALResultViewController *vc = [[ALResultViewController alloc] initWithResultData:resultData image:scanResult.image];
        [self.navigationController pushViewController:vc animated:YES];
    }];
}

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
    NSError *error = nil;
    BOOL success = [self.drivingLicenseScanViewPlugin startAndReturnError:&error];
    
    NSAssert(success, @"We failed starting: %@",error.debugDescription);
}

@end
Barcode Plugin
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
  "captureResolution":"720p",

  "cutout": {
    "style": "rect",
    "maxWidthPercent": "80%",
    "alignment": "center",
    "ratioFromSize": {
      "width": 100,
      "height": 80
    },
    "strokeWidth": 4,
    "cornerRadius": 10,
    "strokeColor": "000000",
    "outerColor": "000000",
    "outerAlpha": 0.3,
    "feedbackStrokeColor": "0099FF"
  },
  "flash": {
    "mode": "manual",
    "alignment": "bottom_right"
  },
  "beepOnResult": true,
  "vibrateOnResult": true,
  "blinkAnimationOnResult": true,
  "cancelOnResult": false,
  "visualFeedback": {
    "style": "rect",
    "strokeColor": "0099FF",
    "fillColor": "220099FF",
    "animationDuration": 150
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.WindowManager;
import android.widget.TextView;

import at.nineyards.anyline.camera.CameraController;
import at.nineyards.anyline.camera.CameraOpenListener;
import at.nineyards.anyline.models.AnylineImage;
import at.nineyards.anyline.modules.barcode.BarcodeResult;
import at.nineyards.anyline.modules.barcode.BarcodeResultListener;
import at.nineyards.anyline.modules.barcode.BarcodeScanView;
import io.anyline.examples.R;

/**
 * Example activity for the Anyline-Barcode-Module
 */
public class ScanBarcodeActivity extends AppCompatActivity implements CameraOpenListener {

    private static final String TAG = ScanBarcodeActivity.class.getSimpleName();
    private BarcodeScanView barcodeScanView;
    private TextView resultText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scan_barcode);
        //Set the flag to keep the screen on (otherwise the screen may go dark during scanning)
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        resultText = (TextView) findViewById(R.id.text_result);

        barcodeScanView = (ScanView) findViewById(R.id.scan_view);
        // add a camera open listener that will be called when the camera is opened or an error occurred
        //  this is optional (if not set a RuntimeException will be thrown if an error occurs)
        barcodeScanView.setCameraOpenListener(this);
        // the view can be configured via a json file in the assets, and this config is set here
        // (alternatively it can be configured via xml, see the Energy Example for that)
        //barcodeScanView.setScanConfig("barcode_view_config.json");

        // optionally limit the barcode format to (multiple) specific types
        //barcodeScanView.setBarcodeFormats(BarcodeScanView.BarcodeFormat.QR_CODE,
        //        BarcodeScanView.BarcodeFormat.CODE_128);
	//init the scanViewPlugin configuration which hold the scan view ui configuration (cutoutConfig and ScanFeedbackConfig)		
	ScanViewPluginConfig barcodeScanviewPluginConfig = new ScanViewPluginConfig(getApplicationContext(), "barcode_view_config.json");
	//init the scan view
	BarcodeScanViewPlugin scanViewPlugin = new BarcodeScanViewPlugin(getApplicationContext(), getString(R.string.anyline_license_key), barcodeScanviewPluginConfig, "BARCODE");
	//init the base scanViewconfig which hold camera and flash configuration
	BaseScanViewConfig barcodeBaseScanViewConfig = new BaseScanViewConfig(getApplicationContext(), "barcode_view_config.json");
	//set the base scanViewConfig to the ScanView
	barcodeScanView.setScanViewConfig(barcodeBaseScanViewConfig);
	//set the scanViewPlugin to the ScanView
	barcodeScanView.setScanViewPlugin(scanViewPlugin);
	//add result listener
	scanViewPlugin.addScanResultListener(new ScanResultListener<BarcodeScanResult>() {
		@Override
		public void onResult(BarcodeScanResult result) {
			resultText.setText(result.getResult());
		}
	});
    }

    @Override
    protected void onResume() {
        super.onResume();
        //start the actual scanning
        barcodeScanView.start();
    }

    @Override
    protected void onPause() {
        super.onPause();
        //stop the scanning
        barcodeScanView.stop();
        //release the camera (must be called in onPause, because there are situations where
        // it cannot be auto-detected that the camera should be released)
        barcodeScanView.releaseCameraInBackground();
    }

    @Override
    public void onCameraOpened(CameraController cameraController, int width, int height) {
        //the camera is opened async and this is called when the opening is finished
        Log.d(TAG, "Camera opened successfully. Frame resolution " + width + " x " + height);
    }

    @Override
    public void onCameraError(Exception e) {
        //This is called if the camera could not be opened.
        // (e.g. If there is no camera or the permission is denied)
        // This is useful to present an alternative way to enter the required data if no camera exists.
        throw new RuntimeException(e);
    }
}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 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
115
116
117
118
119
#import "ALMultiformatBarcodeScanViewController.h"
#import <Anyline/Anyline.h>
#import "NSUserDefaults+ALExamplesAdditions.h"
#import "ALAppDemoLicenses.h"

// This is the license key for the examples project used to set up Aynline below
NSString * const kBarcodeScanLicenseKey = kDemoAppLicenseKey;
// The controller has to conform to <AnylineBarcodeModuleDelegate> to be able to receive results
@interface ALMultiformatBarcodeScanViewController() <ALBarcodeScanPluginDelegate>
// The Anyline plugin used to scan barcodes
@property (nonatomic, strong) ALBarcodeScanPlugin *barcodeScanPlugin;
@property (nonatomic, strong) ALBarcodeScanViewPlugin *barcodeScanViewPlugin;
@property (nullable, nonatomic, strong) ALScanView *scanView;

// A debug label to show scanned results
@property (nonatomic, strong) UILabel *resultLabel;

@end

@implementation ALMultiformatBarcodeScanViewController

/*
 We will do our main setup in viewDidLoad. Its called once the view controller is getting ready to be displayed.
 */
- (void)viewDidLoad {
    [super viewDidLoad];
    
    // Set the background color to black to have a nicer transition
    self.view.backgroundColor = [UIColor blackColor];
    
    self.title = @"Barcode / QR-Code";
    CGRect frame = [[UIScreen mainScreen] applicationFrame];
    frame = CGRectMake(frame.origin.x, frame.origin.y + self.navigationController.navigationBar.frame.size.height, frame.size.width, frame.size.height - self.navigationController.navigationBar.frame.size.height);
    
    //Add Barcode Scan Plugin (Scan Process)
    NSError *error = nil;

    self.barcodeScanPlugin = [[ALBarcodeScanPlugin alloc] initWithPluginID:@"BARCODE" licenseKey:kBarcodeScanLicenseKey delegate:self error:&error];
    NSAssert(self.barcodeScanPlugin, @"Setup Error: %@", error.debugDescription);
    
    //Set Barcode Formats
    [self.barcodeScanPlugin setBarcodeFormatOptions:ALCodeTypeAll];
    
    //Add Barcode Scan View Plugin (Scan UI)
    self.barcodeScanViewPlugin = [[ALBarcodeScanViewPlugin alloc] initWithScanPlugin:self.barcodeScanPlugin];
    NSAssert(self.barcodeScanViewPlugin, @"Setup Error: %@", error.debugDescription);
    
    //Add ScanView (Camera and Flashbutton)
    self.scanView = [[ALScanView alloc] initWithFrame:frame scanViewPlugin:self.barcodeScanViewPlugin];
    
    [self.view addSubview:self.scanView];
    [self.scanView startCamera];
    
    self.barcodeScanViewPlugin.translatesAutoresizingMaskIntoConstraints = NO;
    
    // After setup is complete we add the module to the view of this view controller
    [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scanView]|" options:0 metrics:nil views:@{@"scanView" : self.scanView}]];
    id topGuide = self.topLayoutGuide;
    [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[topGuide]-0-[scanView]|" options:0 metrics:nil views:@{@"scanView" : self.scanView, @"topGuide" : topGuide}]];
    
    // The resultLabel is used as a debug view to see the scanned results. We set its text
    // in anylineBarcodeModuleView:didFindScanResult:atImage below
    self.resultLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - 100, self.view.frame.size.width, 50)];
    self.resultLabel.textAlignment = NSTextAlignmentCenter;
    self.resultLabel.textColor = [UIColor whiteColor];
    self.resultLabel.font = [UIFont fontWithName:@"HelveticaNeue-UltraLight" size:35.0];
    self.resultLabel.adjustsFontSizeToFitWidth = YES;
    
    [self.view addSubview:self.resultLabel];

}

/*
 This method will be called once the view controller and its subviews have appeared on screen
 */
-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    /*
     This is the place where we tell Anyline to start receiving and displaying images from the camera.
     Success/error tells us if everything went fine.
     */
    NSError *error;
    BOOL success = [self.barcodeScanViewPlugin startAndReturnError:&error];
    if( !success ) {
        // Something went wrong. The error object contains the error description
        [[[UIAlertView alloc] initWithTitle:@"Start Scanning Error"
                                    message:error.debugDescription
                                   delegate:self
                          cancelButtonTitle:@"OK"
                          otherButtonTitles:nil] show];
    }
    
    //Update Position of Warning Indicator
    [self updateWarningPosition:
     self.barcodeScanViewPlugin.cutoutRect.origin.y +
     self.barcodeScanViewPlugin.cutoutRect.size.height +
     self.barcodeScanViewPlugin.frame.origin.y +
     120];
}

/*
 Cancel scanning to allow the module to clean up
 */
- (void)viewWillDisappear:(BOOL)animated {
    [self.barcodeScanViewPlugin stopAndReturnError:nil];
}


#pragma mark -- AnylineBarcodeModuleDelegate
/*
 The main delegate method Anyline uses to report its scanned codes
 */
- (void)anylineBarcodeScanPlugin:(ALBarcodeScanPlugin *)anylineBarcodeScanPlugin didFindResult:(ALBarcodeResult *)scanResult {
    [self anylineDidFindResult:scanResult.result barcodeResult:@"" image:scanResult.image scanPlugin:anylineBarcodeScanPlugin viewPlugin:self.barcodeScanViewPlugin completion:NULL];
    
    self.resultLabel.text = scanResult.result;
}

@end
Document Plugin
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{
    "captureResolution":"720p",
    "pictureResolution":"1080p",
    "cutout": {
        "style": "rect",
        "maxWidthPercent": "100%",
        "maxHeightPercent": "100%",
        "width": 720,
        "ratioFromSize": {
            "width": 10,
            "height": 15
        },
        "alignment": "center",
        "strokeWidth": 2,
        "cornerRadius": 0,
        "strokeColor": "00000000"
    },
    "flash": {
        "mode": "manual",
        "alignment": "bottom_left",
        "offset": {
            "x": 10,
            "y": 0
        }
    },
    "beepOnResult": true,
    "vibrateOnResult": true,
    "blinkAnimationOnResult": true,
    "cancelOnResult": false
}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
package io.anyline.examples.document;

import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.PointF;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;
import java.util.List;

import at.nineyards.anyline.camera.CameraController;
import at.nineyards.anyline.camera.CameraOpenListener;
import at.nineyards.anyline.models.AnylineImage;
import at.nineyards.anyline.modules.AnylineBaseModuleView;
import io.anyline.examples.R;
import io.anyline.examples.ScanActivity;
import io.anyline.examples.ScanModuleEnum;
import io.anyline.plugin.ScanResult;
import io.anyline.plugin.document.DocumentScanResultListener;
import io.anyline.plugin.document.DocumentScanViewPlugin;
import io.anyline.view.BaseScanViewConfig;
import io.anyline.view.ScanView;
import io.anyline.view.ScanViewPluginConfig;

/**
 * Example activity for the Anyline-Document-Detection-Module
 */
public class ScanDocumentActivity extends AppCompatActivity implements CameraOpenListener {

	private static final String TAG = ScanDocumentActivity.class.getSimpleName();
	private ScanView documentScanView;
	private Toast notificationToast;
	private ImageView imageViewResult;
	private ProgressDialog progressDialog;
	private ImageView imageViewFull;
	private List<PointF> lastOutline;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		getLayoutInflater().inflate(R.layout.document_scan_view, (ViewGroup) findViewById(R.id.scan_view_placeholder));

		imageViewFull = (ImageView) findViewById(R.id.image_result);

		imageViewResult = (ImageView) findViewById(R.id.full_image);

		documentScanView = (ScanView) findViewById(R.id.scan_view);
		// add a camera open listener that will be called when the camera is opened or an error occurred
		//  this is optional (if not set a RuntimeException will be thrown if an error occurs)
		documentScanView.setCameraOpenListener(this);
		//init the scanViewPluginConfig
		ScanViewPluginConfig documentScanViewPluginConfig = new ScanViewPluginConfig(getApplicationContext(), "document_view_config.json");
		//init the Document Scan View Plugin
		final DocumentScanViewPlugin documentScanViewPlugin = new DocumentScanViewPlugin(getApplicationContext(),getString(R.string.anyline_license_key), documentScanViewPluginConfig, "Document");
		//init the base config for camera and flash
		BaseScanViewConfig documentBaseScanViewconfig  = new BaseScanViewConfig(getApplicationContext(), "document_view_config.json");
		//set the base config to the scan view
		documentScanView.setScanViewConfig(documentBaseScanViewconfig);
		//set the scan view plugin to the scan view
		documentScanView.setScanViewPlugin(documentScanViewPlugin);

		//set cancel on result on true
		documentScanViewPlugin.getScanViewPluginConfig().setCancelOnResult(true);
		documentScanViewPlugin.cancelOnResult();

		documentScanViewPlugin.addScanResultListener(new DocumentScanResultListener() {

			@Override
			public void onPreviewProcessingSuccess(AnylineImage anylineImage) {

				notificationToast = Toast.makeText(ScanDocumentActivity.this, "Scanning full document. Please hold " +
						"still", Toast.LENGTH_LONG);
				notificationToast.show();

				showToast(getString(R.string.document_preview_success));
			}

			@Override
			public void onPreviewProcessingFailure(DocumentScanViewPlugin.DocumentError error) {
				// this is called on any error while processing the document image
				// Note: this is called every time an error occurs in a run, so that might be quite often
				// An error message should only be presented to the user after some time
				Log.d("Callback", "onPictureProcessingFailure: " + error.name());

			}

			@Override
			public void onPictureProcessingFailure(DocumentScanViewPlugin.DocumentError error) {

				// handle an error while processing the full picture here
				// the preview will be restarted automatically
				String text = getString(R.string.document_picture_error);
				switch (error) {
					case DOCUMENT_NOT_SHARP:
						text += getString(R.string.document_error_not_sharp);
						break;
					case DOCUMENT_SKEW_TOO_HIGH:
						text += getString(R.string.document_error_skew_too_high);
						break;
					case DOCUMENT_OUTLINE_NOT_FOUND:
						text += getString(R.string.document_error_outline_not_found);
						break;
					case GLARE_DETECTED:
						text += getString(R.string.document_error_glare_detected);
						break;
					case IMAGE_TOO_DARK:
						text += getString(R.string.document_error_too_dark);
						break;
				}

				showToast(text);
				if (progressDialog != null && progressDialog.isShowing()) {
					progressDialog.dismiss();
				}

				AnylineImage image = documentScanViewPlugin.getCurrentFullImage();

				if (image != null) {
					File outDir = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "error");
					outDir.mkdir();
					File outFile = new File(outDir, "" + System.currentTimeMillis() + error.name() + ".jpg");
					Log.d(TAG, "saved image to: " + outFile.getAbsolutePath());
					try {
						image.save(outFile, 100);
						Log.d(TAG, "error image saved to " + outFile.getAbsolutePath());
					} catch (IOException e) {
						e.printStackTrace();
					}
					image.release();
				}
			}

			@Override
			public void onTakePictureSuccess() {
				progressDialog = ProgressDialog.show(ScanDocumentActivity.this, "Processing", "Processing the picture" +
						". Please wait", true);

				if (notificationToast != null) {
					notificationToast.cancel();
				}

			}

			@Override
			public void onTakePictureError(Throwable error) {
				Log.d("Callback", "onTakePictureError: " + error.getMessage());
			}

			@Override
			public void onPictureTransformed(AnylineImage transformedImage) {
				Log.d("Callback", "onPictureTransformed");
			}

			@Override
			public void onPictureTransformError(DocumentScanViewPlugin.DocumentError error) {
				Log.d("Callback", "onPictureTransformError: " + error.name());
			}

			@Override
			public void onPictureCornersDetected(AnylineImage fullFrame, List corners) {
				Log.d("Callback", "onPictureCornersDetected");
			}


			@Override
			public boolean onDocumentOutlineDetected(List rect, boolean documentShapeAndBrightnessValid) {

				lastOutline = rect;
				return true;

			}

			@Override
			public void onResult(ScanResult result) {
				if (progressDialog != null && progressDialog.isShowing()) {
					progressDialog.dismiss();
				}

				// handle the result document images here
				if (progressDialog != null && progressDialog.isShowing()) {
					progressDialog.dismiss();
				}

				AnylineImage transformedImage = (AnylineImage) result.getResult();
				AnylineImage fullFrame = result.getFullImage();

				showToast(getString(R.string.document_picture_success));


				// show the picture on the screen
				displayPicture(transformedImage);

				/**
				 * IMPORTANT: cache provided frames here, and release them at the end of this onResult. Because
				 * keeping them in memory (e.g. setting the full frame to an ImageView)
				 * will result in a OutOfMemoryError soon. This error is reported in {@link #onTakePictureError
				 * (Throwable)}
				 *
				 * Use a DiskCache http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html#disk-cache
				 * for example
				 *
				 */
				File outDir = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "ok");
				outDir.mkdir();
				File outFile = new File(outDir, "" + System.currentTimeMillis() + ".jpg");
				try {
					transformedImage.save(outFile, 100);
					showToast("image saved to " + outFile.getAbsolutePath());
				} catch (IOException e) {
					e.printStackTrace();
				}

				// release the images
				transformedImage.release();
				fullFrame.release();
			}
		});
	}

	/**
	 * Performs an animation after the final image was successfully processed. This is just an example.
	 *
	 * @param transformedImage The transformed final image
	 */
	private void displayPicture(AnylineImage transformedImage) {

		imageViewResult.setImageBitmap(Bitmap.createScaledBitmap(transformedImage.getBitmap(), imageViewResult.getWidth(), imageViewResult.getHeight(), false));
		imageViewResult.setVisibility(View.VISIBLE);

		imageViewResult.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {
				resetScanning();
			}
		});

	}


	@Override
	protected AnylineBaseModuleView getScanView() {
		return null;
	}


	private void showToast(String text) {
		try {
			notificationToast.setText(text);
		} catch (Exception e) {
			notificationToast = Toast.makeText(this, text, Toast.LENGTH_LONG);
		}
		notificationToast.show();
	}

	@Override
	protected void onResume() {
		super.onResume();
		//start the actual scanning
		documentScanView.start();
	}

	@Override
	protected void onPause() {
		super.onPause();
		//stop the scanning
		documentScanView.stop();
		//release the camera (must be called in onPause, because there are situations where
		// it cannot be auto-detected that the camera should be released)
		documentScanView.releaseCameraInBackground();
	}

	@Override
	public void onCameraOpened(CameraController cameraController, int width, int height) {
		//the camera is opened async and this is called when the opening is finished
		Log.d(TAG, "Camera opened successfully. Frame resolution " + width + " x " + height);
	}

	@Override
	public void onCameraError(Exception e) {
		//This is called if the camera could not be opened.
		// (e.g. If there is no camera or the permission is denied)
		// This is useful to present an alternative way to enter the required data if no camera exists.
		throw new RuntimeException(e);
	}

	@Override
	protected ScanModuleEnum.ScanModule getScanModule() {
		return ScanModuleEnum.ScanModule.DOCUMENT;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {

		if (item.getItemId() == android.R.id.home) {
			if(imageViewResult.getVisibility() == View.VISIBLE){
				resetScanning();
			}else {
				onBackPressed();
			}
			return true;
		}

		return false;
	}

	@Override
	public void onBackPressed() {
		if(imageViewResult.getVisibility() == View.VISIBLE){
			resetScanning();
		}else {
			super.onBackPressed();
			overridePendingTransition(R.anim.fade_in, R.anim.activity_close_translate);
		}
	}

	private void resetScanning(){
		if(imageViewResult.getVisibility() == View.VISIBLE){
			imageViewResult.setVisibility(View.GONE);
			if(documentScanView.getScanViewPlugin() != null && !documentScanView.getScanViewPlugin().isRunning() ){
				documentScanView.start();
			}
		}
	}
}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#import "ALDocumentScanViewController.h"
#import <Anyline/Anyline.h>
#import "NSUserDefaults+ALExamplesAdditions.h"
#import "ALRoundedView.h"
#import "ALAppDemoLicenses.h"

NSString * const kDocumentScanLicenseKey = kDemoAppLicenseKey;

@class AnylineDocumentModuleView;

@interface ALDocumentScanViewController () <ALDocumentScanPluginDelegate, ALInfoDelegate, ALDocumentInfoDelegate>

// The Anyline plugins used for Document
@property (nonatomic, strong) ALDocumentScanViewPlugin *documentScanViewPlugin;
@property (nonatomic, strong) ALDocumentScanPlugin *documentScanPlugin;
@property (nullable, nonatomic, strong) ALScanView *scanView;

@property (nonatomic, strong) ALRoundedView *roundedView;
@property (nonatomic, assign) NSInteger showingLabel;

@end

@implementation ALDocumentScanViewController

/*
 We will do our main setup in viewDidLoad. Its called once the view controller is getting ready to be displayed.
 */
- (void)viewDidLoad {
    [super viewDidLoad];
    // Set the background color to black to have a nicer transition
    self.view.backgroundColor = [UIColor blackColor];
   
    [super viewDidLoad];
    // Set the background color to black to have a nicer transition
    self.view.backgroundColor = [UIColor blackColor];
    self.title = NSLocalizedString(@"Scan Document", @"Scan Document");
    // Initializing the module. Its a UIView subclass. We set the frame to fill the whole screen
    CGRect frame = [[UIScreen mainScreen] applicationFrame];
    frame = CGRectMake(frame.origin.x, frame.origin.y + self.navigationController.navigationBar.frame.size.height, frame.size.width, frame.size.height - self.navigationController.navigationBar.frame.size.height);

    NSError *error = nil;
    self.documentScanPlugin = [[ALDocumentScanPlugin alloc] initWithPluginID:@"DOCUMENT" licenseKey:kDocumentScanLicenseKey delegate:self error:&error];;
    NSAssert(self.documentScanPlugin, @"Setup Error: %@", error.debugDescription);

    [self.documentScanPlugin addInfoDelegate:self];
    
    [self.documentScanPlugin setDocumentRatios:@[@(ALDocumentRatioLetterPortrait), @(ALDocumentRatioLetterLandscape)]];
    self.documentScanPlugin.maxDocumentRatioDeviation = @(0.15);
    
    self.documentScanViewPlugin = [[ALDocumentScanViewPlugin alloc] initWithScanPlugin:self.documentScanPlugin];
    NSAssert(self.documentScanViewPlugin, @"Setup Error: %@", error.debugDescription);
    
    self.scanView = [[ALScanView alloc] initWithFrame:frame
                                       scanViewPlugin:self.documentScanViewPlugin
                                         cameraConfig:[ALCameraConfig defaultDocumentCameraConfig]
                                    flashButtonConfig:[ALFlashButtonConfig defaultFlashConfig]];
    
    // Enable PostProcessing
    [self.documentScanPlugin setPostProcessingEnabled:YES];
    // Disable justDetectCornersIfPossible
    self.documentScanPlugin.justDetectCornersIfPossible = NO;
    
    [self.documentScanPlugin enableReporting:[NSUserDefaults AL_reportingEnabled]];
    self.controllerType = ALScanHistoryDocument;
    self.documentScanViewPlugin.translatesAutoresizingMaskIntoConstraints = NO;
    
    // After setup is complete we add the module to the view of this view controller
    [self.view addSubview:self.scanView];
    // Start Camera:
    [self.scanView startCamera];
    
    [self.view sendSubviewToBack:self.scanView];
    [self startListeningForMotion];

    // This view notifies the user of any problems that occur while he is scanning
    self.roundedView = [[ALRoundedView alloc] initWithFrame:CGRectMake(20, 115, self.view.bounds.size.width - 40, 30)];
    self.roundedView.fillColor = [UIColor colorWithRed:98.0/255.0 green:39.0/255.0 blue:232.0/255.0 alpha:0.6];
    self.roundedView.textLabel.text = @"";
    self.roundedView.alpha = 0;
    [self.view addSubview:self.roundedView];
}

/*
 This method will be called once the view controller and its subviews have appeared on screen
 */
-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    /*
     This is the place where we tell Anyline to start receiving and displaying images from the camera.
     Success/error tells us if everything went fine.
     */
    NSError *error;
    BOOL success = [self.documentScanViewPlugin startAndReturnError:&error];
    if( !success ) {
        // Something went wrong. The error object contains the error description
        [[[UIAlertView alloc] initWithTitle:@"Start Scanning Error"
                                    message:error.debugDescription
                                   delegate:self
                          cancelButtonTitle:@"OK"
                          otherButtonTitles:nil] show];
    }
    
    //Update Position of Warning Indicator
    [self updateWarningPosition:
     self.documentScanViewPlugin.cutoutRect.origin.y +
     self.documentScanViewPlugin.cutoutRect.size.height +
     self.documentScanViewPlugin.frame.origin.y - 100];
}

/*
 Cancel scanning to allow the module to clean up
 */
- (void)viewWillDisappear:(BOOL)animated {
    [self.documentScanViewPlugin stopAndReturnError:nil];
}

#pragma mark -- AnylineDocumentModuleDelegate

/*
 This is the main delegate method Anyline uses to report its scanned codes
 */
- (void)anylineDocumentScanPlugin:(ALDocumentScanPlugin *)anylineDocumentScanPlugin
                        hasResult:(UIImage *)transformedImage
                        fullImage:(UIImage *)fullFrame
                  documentCorners:(ALSquare *)corners {
    UIViewController *viewController = [[UIViewController alloc] init];
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:viewController.view.bounds];
    imageView.center = CGPointMake(imageView.center.x, imageView.center.y + 30);
    imageView.contentMode = UIViewContentModeScaleAspectFit;
    imageView.image = transformedImage;
    [viewController.view addSubview:imageView];
    [self.navigationController pushViewController:viewController animated:YES];
}

/*
 This method receives errors that occured during the scan.
 */
- (void)anylineDocumentScanPlugin:(ALDocumentScanPlugin *)anylineDocumentScanPlugin
  reportsPictureProcessingFailure:(ALDocumentError)error {
    [self showUserLabel:error];
}

/*
 This method receives errors that occured during the scan.
 */
- (void)anylineDocumentScanPlugin:(ALDocumentScanPlugin *)anylineDocumentScanPlugin
  reportsPreviewProcessingFailure:(ALDocumentError)error {
    [self showUserLabel:error];
}

/**
 *  Crops an arbitrary rectangle (e.g. trapezoid) of the input image and perspectively transforms it to a rectangle (e.g. square).
 *  After the transformation is complete the result delegate anylineDocumentScanPlugin:hasResult:fullImage:documentCorners will be triggered.
 *  In any case call [AnylineDocumentModuleView cancelScanningAndReturnError:] before using this method.
 *
 *  Return value indicating the success / failure of the call.
 *
 *
 */
- (BOOL)transformImageWithSquare:(ALSquare *)square
                           image:(UIImage *)image
                           error:(NSError * *)error {
    [self.scanViewPlugin transformImageWithSquare:square image:image error:&error];
}

- (BOOL)transformALImageWithSquare:(ALSquare *)square
                             image:(ALImage *)image
                             error:(NSError * *)error {
    [self.scanViewPlugin transformALImageWithSquare:square image:image error:&error];
}

/**
 *  A manual picture will be taken. Anyline tries to detected any document corners and will report the result
 *  with the delegate method anylineDocumentModuleView:detectedPictureCorners:inImage:
 */
- (BOOL)triggerPictureCornerDetectionAndReturnError:(NSError **)error {
    [self.documentModuleView triggerPictureCornerDetectionAndReturnError:&error];
}

#pragma mark -- Helper Methods

/*
 Shows a little round label at the bottom of the screen to inform the user what happended
 */
- (void)showUserLabel:(ALDocumentError)error {
    NSString *helpString = nil;
    switch (error) {
        case ALDocumentErrorNotSharp:
            helpString = @"Document not Sharp";
            break;
        case ALDocumentErrorSkewTooHigh:
            helpString = @"Wrong Perspective";
            break;
        case ALDocumentErrorImageTooDark:
            helpString = @"Too Dark";
            break;
        case ALDocumentErrorShakeDetected:
            helpString = @"Shake";
            break;
        default:
            break;
    }
    
    //Display Error to indicate what the User should do.
}

@end
License Plate Plugin
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
{
  "captureResolution":"1080",
  "cutout": {
    "style": "rect",
    "maxWidthPercent": "80%",
    "maxHeightPercent": "80%",
    "alignment": "center",
    "width": 720,
    "ratioFromSize": {
      "width": 2,
      "height": 1
    },
    "strokeWidth": 2,
    "cornerRadius": 10,
    "strokeColor": "FFFFFF",
    "outerColor": "000000",
    "outerAlpha": 0.3,
    "feedbackStrokeColor": "0099FF"
  },
  "flash": {
    "mode": "manual",
    "alignment": "bottom_right",
    "imageOn": "flash_on",
    "imageOff": "flash_off"
  },
  "beepOnResult": true,
  "vibrateOnResult": true,
  "blinkAnimationOnResult": true,
  "cancelOnResult": true,
  "visualFeedback": {
    "animationDuration" : 70,
    "style": "rect",
    "strokeWidth": 2,
    "strokeColor": "0099FF"
  }
}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 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
115
116
117
118
119
120
121
122
package io.anyline.examples.licenseplate;

import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.RelativeLayout;

import at.nineyards.anyline.camera.AnylineViewConfig;
import at.nineyards.anyline.modules.licenseplate.LicensePlateResult;
import at.nineyards.anyline.modules.licenseplate.LicensePlateResultListener;
import at.nineyards.anyline.modules.licenseplate.LicensePlateScanView;
import io.anyline.examples.R;
import io.anyline.examples.SettingsFragment;
import io.anyline.examples.licenseplate.result.LicensePlateResultView;

public class ScanLicensePlateActivity extends AppCompatActivity {

    private static final String TAG = ScanLicensePlateActivity.class.getSimpleName();
    protected ScanView scanView;
    private LicensePlateResultView licensePlateResultView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //Set the flag to keep the screen on (otherwise the screen may go dark during scanning)
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        setContentView(R.layout.activity_scan_license_plate);

        String license = getString(R.string.anyline_license_key);

        // Get the view from the layout
        scanView = (ScanView) findViewById(R.id.scan_view);
	//init the scanViewPlugin configuration which hold the scan view ui configuration (cutoutConfig and ScanFeedbackConfig)		
	ScanViewPluginConfig licensePlateScanviewPluginConfig = new ScanViewPluginConfig(getApplicationContext(), "license_plate_view_config.json");
	//init the scan view
	LicensePlateScanViewPlugin scanViewPlugin = new LicensePlateScanViewPlugin(getApplicationContext(), getString(R.string.anyline_license_key), licensePlateScanviewPluginConfig, "LICENSE_PLATE");
	//init the base scanViewconfig which hold camera and flash configuration
	BaseScanViewConfig licensePlateBaseScanViewConfig = new BaseScanViewConfig(getApplicationContext(), "license_plate_view_config.json");
	//set the base scanViewConfig to the ScanView
	scanView.setScanViewConfig(licensePlateBaseScanViewConfig);
	//set the scanViewPlugin to the ScanView
	scanView.setScanViewPlugin(scanViewPlugin);
	//add result listener
	scanViewPlugin.addScanResultListener(new ScanResultListener<LicensePlateScanResult>() {
		@Override
		public void onResult(LicensePlateScanResult result) {
			resultText.setText(result.getResult());
		}
	});

        // disable the reporting if set to off in preferences
        scanView.setReportingEnabled(PreferenceManager.getDefaultSharedPreferences(this).getBoolean(SettingsFragment
                .KEY_PREF_REPORTING_ON, true));
        addLicensePlateResultView();
    }

    private void addLicensePlateResultView() {
        RelativeLayout mainLayout = (RelativeLayout) findViewById(R.id.license_plate_main_layout);

        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        params.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
        params.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);

        licensePlateResultView = new LicensePlateResultView(this);
        licensePlateResultView.setVisibility(View.INVISIBLE);

        mainLayout.addView(licensePlateResultView, params);
        licensePlateResultView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                clearAndHideLicensePlateResultView();
                startScanning();
            }
        });
    }

    private void clearAndHideLicensePlateResultView() {
        licensePlateResultView.setCountry("");
        licensePlateResultView.setLicensePlate("");
        licensePlateResultView.setVisibility(View.INVISIBLE);
    }

    private void startScanning() {
        // this must be called in onResume, or after a result to start the scanning again
        if (!scanView.getScanViewPlugin().isRunning()) {
            scanView.start();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        startScanning();
    }

    @Override
    protected void onPause() {
        super.onPause();

        scanView.stop();
        scanView.releaseCameraInBackground();
    }

    @Override
    public void onBackPressed() {
        if (licensePlateResultView.getVisibility() == View.VISIBLE) {
            clearAndHideLicensePlateResultView();
            startScanning();
        } else {
            super.onBackPressed();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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
#import "ALLicensePlateViewController.h"
#import <Anyline/Anyline.h>
#import "NSUserDefaults+ALExamplesAdditions.h"
#import "ALAppDemoLicenses.h"
#import "ALResultEntry.h"
#import "ALResultViewController.h"
#import "Anyline/ALMeterScanPlugin.h"
#import "Anyline/ALMeterScanViewPlugin.h"




// This is the license key for the examples project used to set up Aynline below
NSString * const kLicensePlateLicenseKey = kDemoAppLicenseKey;
// The controller has to conform to <AnylineOCRModuleDelegate> to be able to receive results
@interface ALLicensePlateViewController ()<ALLicensePlateScanPluginDelegate, ALInfoDelegate>
// The Anyline module used for OCR
@property (nonatomic, strong) ALLicensePlateScanViewPlugin *licensePlateScanViewPlugin;
@property (nonatomic, strong) ALLicensePlateScanPlugin *licensePlateScanPlugin;
@property (nullable, nonatomic, strong) ALScanView *scanView;

@end

@implementation ALLicensePlateViewController
/*
 We will do our main setup in viewDidLoad. Its called once the view controller is getting ready to be displayed.
 */
- (void)viewDidLoad {
    [super viewDidLoad];
    // Set the background color to black to have a nicer transition
    self.view.backgroundColor = [UIColor blackColor];
    self.title = @"License Plate";
    
    // Initializing the module. Its a UIView subclass. We set the frame to fill the whole screen
    CGRect frame = [[UIScreen mainScreen] applicationFrame];
    frame = CGRectMake(frame.origin.x, frame.origin.y + self.navigationController.navigationBar.frame.size.height, frame.size.width, frame.size.height - self.navigationController.navigationBar.frame.size.height);

    NSError *error = nil;
    self.licensePlateScanPlugin = [[ALLicensePlateScanPlugin alloc] initWithPluginID:@"LICENSE_PLATE" licenseKey:kDemoAppLicenseKey delegate:self error:&error];
    NSAssert(self.licensePlateScanPlugin, @"Setup Error: %@", error.debugDescription);
    [self.licensePlateScanPlugin addInfoDelegate:self];
    
    self.licensePlateScanViewPlugin = [[ALLicensePlateScanViewPlugin alloc] initWithScanPlugin:self.licensePlateScanPlugin];
    NSAssert(self.licensePlateScanViewPlugin, @"Setup Error: %@", error.debugDescription);
    
    self.scanView = [[ALScanView alloc] initWithFrame:frame scanViewPlugin:self.licensePlateScanViewPlugin];
    
    self.controllerType = ALScanHistoryLicensePlates;
    
    // After setup is complete we add the module to the view of this view controller
    [self.view addSubview:self.scanView];
    //Start Camera:
    [self.scanView startCamera];
    
    [self.view sendSubviewToBack:self.scanView];
}

/*
 This method will be called once the view controller and its subviews have appeared on screen
 */
-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    // We use this subroutine to start Anyline. The reason it has its own subroutine is
    // so that we can later use it to restart the scanning process.
    NSError *error;
    BOOL success = [self.licensePlateScanViewPlugin startAndReturnError:&error];
    if( !success ) {
        // Something went wrong. The error object contains the error description
        NSAssert(success, @"Start Scanning Error: %@", error.debugDescription);
    }
}

/*
 Cancel scanning to allow the module to clean up
 */
- (void)viewWillDisappear:(BOOL)animated {
    [self.licensePlateScanViewPlugin stopAndReturnError:nil];
}

#pragma mark -- AnylineOCRModuleDelegate

/*
 This is the main delegate method Anyline uses to report its results
 */
- (void)anylineLicensePlateScanPlugin:(ALLicensePlateScanPlugin *)anylineLicensePlateScanPlugin
                        didFindResult:(ALLicensePlateResult *)result {

    //Handle the returned scan Result here.
}

@end

Tutorials and How To Guides

A growing list of tutorials for developers can be found at https://www.anyline.com/tutorials/