├── .DS_Store
├── .gitignore
├── README.md
└── ScanDemoExample
├── .DS_Store
├── apk
└── app-debug.apk
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── scanner
│ │ └── demo
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── scanner
│ │ └── demo
│ │ └── MainActivity.java
│ └── res
│ ├── drawable-hdpi
│ └── ic_launcher.png
│ ├── drawable-mdpi
│ └── ic_launcher.png
│ ├── drawable-xhdpi
│ └── ic_launcher.png
│ ├── drawable-xxhdpi
│ └── ic_launcher.png
│ ├── layout
│ └── activity_main.xml
│ ├── menu
│ └── menu_main.xml
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── build
└── intermediates
│ ├── dex-cache
│ └── cache.xml
│ └── model_data.bin
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── scanlibrary
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── scanlibrary
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ ├── .DS_Store
│ └── com
│ │ └── scanlibrary
│ │ ├── ExifInterfaceUtils.java
│ │ ├── ImageResizer.java
│ │ ├── PolygonView.java
│ │ ├── ProgressDialogFragment.java
│ │ ├── ScalingUtilities.java
│ │ ├── ScanActivity.java
│ │ ├── ScanFragment.java
│ │ ├── ScanUtils.java
│ │ └── Utils.java
│ ├── libs
│ └── armeabi-v7a
│ │ └── libopencv_java.so
│ └── res
│ ├── drawable-hdpi
│ ├── ic_color_lens_white_24dp.png
│ ├── ic_crop_white_24dp.png
│ ├── ic_done_white_24dp.png
│ └── ic_rotate_90_degrees_ccw_white_24dp.png
│ ├── drawable-mdpi
│ ├── ic_color_lens_white_24dp.png
│ ├── ic_crop_white_24dp.png
│ ├── ic_done_white_24dp.png
│ └── ic_rotate_90_degrees_ccw_white_24dp.png
│ ├── drawable-xhdpi
│ ├── ic_color_lens_white_24dp.png
│ ├── ic_crop_white_24dp.png
│ ├── ic_done_white_24dp.png
│ └── ic_rotate_90_degrees_ccw_white_24dp.png
│ ├── drawable-xxhdpi
│ ├── ic_color_lens_white_24dp.png
│ ├── ic_crop_white_24dp.png
│ ├── ic_done_white_24dp.png
│ └── ic_rotate_90_degrees_ccw_white_24dp.png
│ ├── drawable-xxxhdpi
│ ├── ic_color_lens_white_24dp.png
│ ├── ic_crop_white_24dp.png
│ ├── ic_done_white_24dp.png
│ └── ic_rotate_90_degrees_ccw_white_24dp.png
│ ├── drawable
│ └── circle.xml
│ ├── layout
│ ├── activity_scan.xml
│ └── fragment_scan.xml
│ ├── menu
│ └── scan_menu.xml
│ ├── values-lt
│ └── strings.xml
│ ├── values-ru
│ └── strings.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ └── strings.xml
├── screenshots
├── a.png
├── b.png
├── c.png
├── d.png
├── e.png
├── f.png
└── g.png
└── settings.gradle
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | .idea/
3 | *.iml
4 | ScanDemoExample/local.properties
5 | ScanDemoExample/build/intermediates/dex-cache/cache.xml
6 |
7 | ScanDemoExample/build/intermediates/dex-cache/cache.xml
8 | ScanDemoExample/gradle.properties
9 | ScanDemoExample/scanlibrary/gradle.properties
10 | ScanDemoExample/scanlibrary/AndroidDocumentScanner.gpg
11 | ScanDemoExample/gradle.properties
12 | ScanDemoExample/build/intermediates/dex-cache/cache.xml
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ScanLibrary
2 | ScanLibrary is an android document scanning library built on top of OpenCV, using the app you will be able to select the exact edges and crop the document accordingly from the selected 4 edges and change the perspective transformation of the cropped image.
3 |
4 | # Screenshots
5 |
6 |
7 |
8 |

9 |
![]()
10 |

11 |
![]()
12 |

13 |
![]()
14 |

15 |
![]()
16 |

17 |
![]()
18 |

19 |
![]()
20 |

21 |
22 |
23 | # Using it in your project
24 | - If you are using android studio, add the dependency to your main app build.gradle this way:
25 |
26 | ```
27 | - compile 'com.github.andrejlukasevic:document-scanner:4.0.0' // Check for latest version and replace version code
28 | ```
29 | - In your activity or fragment when you want to give an option of document scanning to user then:
30 | Start the scanlibrary ScanActivity, with this the app will go to library, below is the sample code snippet:
31 | ```java
32 | Intent intent = new Intent(this, ScanActivity.class);
33 | intent.putExtra(ScanActivity.EXTRA_BRAND_IMG_RES, R.drawable.ic_crop_white_24dp); // Set image for title icon - optional
34 | intent.putExtra(ScanActivity.EXTRA_TITLE, "Crop Document"); // Set title in action Bar - optional
35 | intent.putExtra(ScanActivity.EXTRA_ACTION_BAR_COLOR, R.color.green); // Set title color - optional
36 | intent.putExtra(ScanActivity.EXTRA_LANGUAGE, "en"); // Set language - optional
37 | startActivityForResult(intent, REQUEST_CODE_SCAN);
38 | ```
39 |
40 | - Once the scanning is done, the application is returned from scan library to main app, to retrieve the scanned image, add onActivityResult in your activity or fragment from where you have started startActivityForResult, below is the sample code snippet:
41 | ```java
42 | @Override
43 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
44 | super.onActivityResult(requestCode, resultCode, data);
45 | if (requestCode == REQUEST_CODE_SCAN && resultCode == Activity.RESULT_OK) {
46 | String imgPath = data.getStringExtra(ScanActivity.RESULT_IMAGE_PATH);
47 | BitmapFactory.Options options = new BitmapFactory.Options();
48 | options.inPreferredConfig = Bitmap.Config.ARGB_8888;
49 | Bitmap bitmap = BitmapFactory.decodeFile(imgPath, options);
50 | viewHolder.image.setImageBitmap(bitmap);
51 | }
52 | }
53 | ```
54 |
--------------------------------------------------------------------------------
/ScanDemoExample/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/.DS_Store
--------------------------------------------------------------------------------
/ScanDemoExample/apk/app-debug.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/apk/app-debug.apk
--------------------------------------------------------------------------------
/ScanDemoExample/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/ScanDemoExample/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.2"
6 |
7 | defaultConfig {
8 | applicationId "com.scanner.demo"
9 | minSdkVersion 14
10 | targetSdkVersion 22
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | compile 'com.android.support:appcompat-v7:21.0.3'
25 | compile 'com.github.andrejlukasevic:document-scanner:4.0.0' // Check for latest version
26 | }
27 |
--------------------------------------------------------------------------------
/ScanDemoExample/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/jhansi/softwares/adt-bundle-mac-x86_64-20130729/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/ScanDemoExample/app/src/androidTest/java/com/scanner/demo/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.scanner.demo;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/ScanDemoExample/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/ScanDemoExample/app/src/main/java/com/scanner/demo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.scanner.demo;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.graphics.Bitmap;
6 | import android.os.Bundle;
7 | import android.support.v7.app.ActionBarActivity;
8 | import android.view.View;
9 | import android.view.View.OnClickListener;
10 | import android.widget.ImageView;
11 |
12 | import com.scanlibrary.ScanActivity;
13 | import com.scanlibrary.Utils;
14 |
15 |
16 | public class MainActivity extends ActionBarActivity implements OnClickListener {
17 |
18 | // ===========================================================
19 | // Constants
20 | // ===========================================================
21 |
22 | private static final int REQUEST_CODE_SCAN = 47;
23 |
24 | private static final String SAVED_SCANNED_HHOTO = "scanned_photo";
25 |
26 | // ===========================================================
27 | // Fields
28 | // ===========================================================
29 |
30 | private final ViewHolder viewHolder = new ViewHolder();
31 |
32 | private String scannedPhoto;
33 |
34 | // ===========================================================
35 | // Constructors
36 | // ===========================================================
37 |
38 | // ===========================================================
39 | // Getters & Setters
40 | // ===========================================================
41 |
42 | // ===========================================================
43 | // Methods for/from SuperClass/Interfaces
44 | // ===========================================================
45 |
46 | @Override
47 | protected void onCreate(Bundle savedInstanceState) {
48 | super.onCreate(savedInstanceState);
49 | setContentView(R.layout.activity_main);
50 | viewHolder.prepare(findViewById(android.R.id.content));
51 |
52 | if (savedInstanceState != null) {
53 | scannedPhoto = savedInstanceState.getString(SAVED_SCANNED_HHOTO);
54 | }
55 |
56 | if (scannedPhoto != null) {
57 | viewHolder.image.setImageBitmap(Utils.getBitmapFromLocation(scannedPhoto));
58 | }
59 | }
60 |
61 | @Override
62 | public void onClick(View v) {
63 | if (v.equals(viewHolder.scabBtn)) {
64 | onScanButtonClicked();
65 | }
66 | }
67 |
68 | @Override
69 | protected void onResume() {
70 | super.onResume();
71 | viewHolder.scabBtn.setOnClickListener(this);
72 | }
73 |
74 | @Override
75 | protected void onPause() {
76 | super.onPause();
77 | viewHolder.scabBtn.setOnClickListener(null);
78 | }
79 |
80 | @Override
81 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
82 | super.onActivityResult(requestCode, resultCode, data);
83 | if (requestCode == REQUEST_CODE_SCAN && resultCode == Activity.RESULT_OK) {
84 | String imgPath = data.getStringExtra(ScanActivity.RESULT_IMAGE_PATH);
85 | Bitmap bitmap = Utils.getBitmapFromLocation(imgPath);
86 | viewHolder.image.setImageBitmap(bitmap);
87 | // Uri uri = data.getExtras().getParcelable(ScanConstants.SCANNED_RESULT);
88 | // Bitmap bitmap = null;
89 | // try {
90 | // bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
91 | // getContentResolver().delete(uri, null, null);
92 | // viewHolder.image.setImageBitmap(bitmap);
93 | // } catch (IOException e) {
94 | // e.printStackTrace();
95 | // }
96 | }
97 | }
98 |
99 | @Override
100 | protected void onSaveInstanceState(Bundle outState) {
101 | super.onSaveInstanceState(outState);
102 | outState.putString(SAVED_SCANNED_HHOTO, scannedPhoto);
103 | }
104 |
105 | // ===========================================================
106 | // Methods
107 | // ===========================================================
108 |
109 | private void onScanButtonClicked() {
110 | Intent intent = new Intent(this, ScanActivity.class);
111 | intent.putExtra(ScanActivity.EXTRA_BRAND_IMG_RES, R.drawable.ic_crop_white_24dp);
112 | intent.putExtra(ScanActivity.EXTRA_TITLE, "Crop Document");
113 | intent.putExtra(ScanActivity.EXTRA_ACTION_BAR_COLOR, R.color.green);
114 | intent.putExtra(ScanActivity.EXTRA_LANGUAGE, "en");
115 | startActivityForResult(intent, REQUEST_CODE_SCAN);
116 | }
117 |
118 |
119 | // ===========================================================
120 | // Inner and Anonymous Classes
121 | // ===========================================================
122 |
123 | private static class ViewHolder {
124 |
125 | ImageView image;
126 | View scabBtn;
127 |
128 | void prepare(View parent) {
129 | image = (ImageView) parent.findViewById(R.id.image);
130 | scabBtn = parent.findViewById(R.id.scan);
131 | }
132 | }
133 |
134 | }
135 |
--------------------------------------------------------------------------------
/ScanDemoExample/app/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/app/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ScanDemoExample/app/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/app/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ScanDemoExample/app/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/app/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ScanDemoExample/app/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/app/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ScanDemoExample/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
18 |
19 |
24 |
25 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/ScanDemoExample/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/ScanDemoExample/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/ScanDemoExample/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #006600
4 |
5 |
--------------------------------------------------------------------------------
/ScanDemoExample/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 750dp
6 | 750dp
7 |
8 |
--------------------------------------------------------------------------------
/ScanDemoExample/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ScanDemoExample
5 | Hello world!
6 | Settings
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ScanDemoExample/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ScanDemoExample/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:1.3.0'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | maven {
19 | url 'https://raw.github.com/vRallev/mvn-repo/master/'
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ScanDemoExample/build/intermediates/dex-cache/cache.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
9 |
10 |
11 | -
16 |
17 |
18 | -
23 |
24 |
25 | -
30 |
31 |
32 | -
37 |
38 |
39 | -
44 |
45 |
46 | -
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/ScanDemoExample/build/intermediates/model_data.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/build/intermediates/model_data.bin
--------------------------------------------------------------------------------
/ScanDemoExample/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 | android.useDeprecatedNdk=true
--------------------------------------------------------------------------------
/ScanDemoExample/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/ScanDemoExample/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Apr 10 15:27:10 PDT 2013
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
7 |
--------------------------------------------------------------------------------
/ScanDemoExample/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/ScanDemoExample/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 |
4 | android {
5 | compileSdkVersion 23
6 | buildToolsVersion "23.0.2"
7 |
8 | defaultConfig {
9 | minSdkVersion 14
10 | targetSdkVersion 22
11 | versionCode 4
12 | versionName "4.0"
13 | ndk
14 | {
15 | moduleName "Scanner"
16 | }
17 | }
18 | // sourceSets.main
19 | // {
20 | // jni.srcDirs = []
21 | // jniLibs.srcDir 'src/main/libs'
22 | // }
23 | buildTypes {
24 | release {
25 | minifyEnabled false
26 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
27 | }
28 | }
29 | }
30 |
31 |
32 | repositories {
33 | maven {
34 | url 'https://raw.github.com/vRallev/mvn-repo/master/'
35 | }
36 | }
37 |
38 |
39 | dependencies {
40 | compile fileTree(dir: 'libs', include: ['*.jar'])
41 | //Library for Image View That have Ability to zoom...
42 | compile 'com.davemorrissey.labs:subsampling-scale-image-view:2.3.0'
43 | compile 'org.opencv:opencv-android:2.4.8'
44 | }
45 |
46 | apply from: 'https://raw.github.com/andrejlukasevic/gradle-mvn-push/master/gradle-mvn-push.gradle'
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/jhansi/softwares/adt-bundle-mac-x86_64-20130729/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/androidTest/java/com/scanlibrary/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.scanlibrary;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/java/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/java/.DS_Store
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/java/com/scanlibrary/ExifInterfaceUtils.java:
--------------------------------------------------------------------------------
1 | package com.scanlibrary;
2 |
3 | import android.media.ExifInterface;
4 |
5 | import java.io.IOException;
6 | import java.util.ArrayList;
7 | import java.util.Arrays;
8 | import java.util.List;
9 |
10 | public class ExifInterfaceUtils {
11 |
12 | public static void copyExifAttribute(ExifInterface source, ExifInterface target, String tag) {
13 | String value = source.getAttribute(tag);
14 | if (value == null) {
15 | return;
16 | }
17 | target.setAttribute(tag, value);
18 | }
19 |
20 | public static void copyExifAttributes(ExifInterface source, ExifInterface target, String... tags) {
21 | for (String tag : tags) {
22 | copyExifAttribute(source, target, tag);
23 | }
24 | }
25 |
26 | public static void copyExifAttributes(ExifInterface source, ExifInterface target, List tags) {
27 | for (String tag : tags) {
28 | copyExifAttribute(source, target, tag);
29 | }
30 | }
31 |
32 | public static void removeTag(String pathToFile, String tag) {
33 | copyExifInterface(pathToFile, pathToFile, tag);
34 | }
35 |
36 | public static void copyExifInterface(String sourceLocation, String targetLocation, String excludeExifTag) {
37 | ExifInterface sourceExif = null;
38 | try {
39 | sourceExif = new ExifInterface(sourceLocation);
40 | } catch (IOException e) {
41 | e.printStackTrace();
42 | return;
43 | }
44 |
45 | ExifInterface targetExif = null;
46 | try {
47 | targetExif = new ExifInterface(targetLocation);
48 | } catch (IOException e) {
49 | e.printStackTrace();
50 | return;
51 | }
52 |
53 | final String[] EXIF_ATTRIBUTES_TO_COPY = {
54 | ExifInterface.TAG_DATETIME,
55 | ExifInterface.TAG_MAKE,
56 | ExifInterface.TAG_MODEL,
57 | ExifInterface.TAG_FLASH,
58 | ExifInterface.TAG_GPS_LATITUDE,
59 | ExifInterface.TAG_GPS_LONGITUDE,
60 | ExifInterface.TAG_GPS_LATITUDE_REF,
61 | ExifInterface.TAG_GPS_LONGITUDE_REF,
62 | ExifInterface.TAG_EXPOSURE_TIME,
63 | ExifInterface.TAG_APERTURE,
64 | ExifInterface.TAG_ISO,
65 | ExifInterface.TAG_GPS_ALTITUDE,
66 | ExifInterface.TAG_GPS_ALTITUDE_REF,
67 | ExifInterface.TAG_GPS_TIMESTAMP,
68 | ExifInterface.TAG_GPS_DATESTAMP,
69 | ExifInterface.TAG_WHITE_BALANCE,
70 | ExifInterface.TAG_GPS_PROCESSING_METHOD,
71 | ExifInterface.TAG_ORIENTATION
72 | };
73 |
74 | List exifAttributesToCopyList = new ArrayList<>(Arrays.asList(EXIF_ATTRIBUTES_TO_COPY));
75 | if (!Utils.isStringEmpty(excludeExifTag)) {
76 | exifAttributesToCopyList.remove(excludeExifTag);
77 | }
78 |
79 |
80 | ExifInterfaceUtils.copyExifAttributes(sourceExif, targetExif, exifAttributesToCopyList);
81 |
82 | try {
83 | targetExif.saveAttributes();
84 | } catch (IOException e) {
85 | e.printStackTrace();
86 | }
87 |
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/java/com/scanlibrary/ImageResizer.java:
--------------------------------------------------------------------------------
1 | package com.scanlibrary;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Matrix;
5 | import android.graphics.RectF;
6 |
7 | import com.scanlibrary.ScalingUtilities.ScalingLogic;
8 |
9 | import java.io.IOException;
10 |
11 | public class ImageResizer {
12 |
13 | // ===========================================================
14 | // Constants
15 | // ===========================================================
16 |
17 | // ===========================================================
18 | // Fields
19 | // ===========================================================
20 |
21 | // ===========================================================
22 | // Constructors
23 | // ===========================================================
24 |
25 | // ===========================================================
26 | // Getter & Setter
27 | // ===========================================================
28 |
29 | // ===========================================================
30 | // Methods for/from SuperClass/Interfaces
31 | // ===========================================================
32 |
33 | // ===========================================================
34 | // Methods
35 | // ===========================================================
36 |
37 | /**
38 | * @return Resized Bitmap Image, If image was resized, old bitmap is recycled
39 | */
40 | public static Bitmap resizeImage(Bitmap unscaledBitmap, int desiredWidth, int desiredHeight, boolean recycleOldOne) throws IOException {
41 |
42 | try {
43 | // Part 1: Decode image
44 | if (!(unscaledBitmap.getWidth() <= desiredWidth && unscaledBitmap.getHeight() <= desiredHeight)) {
45 | // Part 2: Scale image
46 | Bitmap scaledBitmap = ScalingUtilities.createScaledBitmap(unscaledBitmap, desiredWidth, desiredHeight, ScalingLogic.FIT);
47 | if (recycleOldOne) unscaledBitmap.recycle();
48 | return scaledBitmap;
49 | } else {
50 |
51 | return unscaledBitmap;
52 | }
53 |
54 | } catch (Throwable e) {
55 | throw new IOException(e);
56 | }
57 | }
58 |
59 | public static Bitmap resizeImage(Bitmap unscaledBitmap, int desiredWidth, int desiredHeight) throws IOException {
60 | return resizeImage(unscaledBitmap, desiredWidth, desiredHeight, true);
61 | }
62 |
63 | public static Bitmap scaleBitmap(Bitmap bitmap, int width, int height) {
64 | Matrix m = new Matrix();
65 | m.setRectToRect(new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight()), new RectF(0, 0, width, height), Matrix.ScaleToFit.CENTER);
66 | return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true);
67 | }
68 |
69 | // ===========================================================
70 | // Inner and Anonymous Classes
71 | // ===========================================================
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/java/com/scanlibrary/PolygonView.java:
--------------------------------------------------------------------------------
1 | package com.scanlibrary;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.graphics.PointF;
7 | import android.util.AttributeSet;
8 | import android.view.MotionEvent;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.FrameLayout;
12 | import android.widget.ImageView;
13 |
14 | import java.util.ArrayList;
15 | import java.util.HashMap;
16 | import java.util.List;
17 | import java.util.Map;
18 |
19 | /**
20 | * Created by jhansi on 28/03/15.
21 | */
22 | public class PolygonView extends FrameLayout {
23 |
24 | protected Context context;
25 | private Paint paint;
26 | private ImageView pointer1;
27 | private ImageView pointer2;
28 | private ImageView pointer3;
29 | private ImageView pointer4;
30 | private ImageView midPointer13;
31 | private ImageView midPointer12;
32 | private ImageView midPointer34;
33 | private ImageView midPointer24;
34 | private PolygonView polygonView;
35 |
36 | public PolygonView(Context context) {
37 | super(context);
38 | this.context = context;
39 | init();
40 | }
41 |
42 | public PolygonView(Context context, AttributeSet attrs) {
43 | super(context, attrs);
44 | this.context = context;
45 | init();
46 | }
47 |
48 | public PolygonView(Context context, AttributeSet attrs, int defStyleAttr) {
49 | super(context, attrs, defStyleAttr);
50 | this.context = context;
51 | init();
52 | }
53 |
54 | private void init() {
55 | polygonView = this;
56 | pointer1 = getImageView(0, 0);
57 | pointer2 = getImageView(getWidth(), 0);
58 | pointer3 = getImageView(0, getHeight());
59 | pointer4 = getImageView(getWidth(), getHeight());
60 | midPointer13 = getImageView(0, getHeight() / 2);
61 | midPointer13.setOnTouchListener(new MidPointTouchListenerImpl(pointer1, pointer3));
62 |
63 | midPointer12 = getImageView(0, getWidth() / 2);
64 | midPointer12.setOnTouchListener(new MidPointTouchListenerImpl(pointer1, pointer2));
65 |
66 | midPointer34 = getImageView(0, getHeight() / 2);
67 | midPointer34.setOnTouchListener(new MidPointTouchListenerImpl(pointer3, pointer4));
68 |
69 | midPointer24 = getImageView(0, getHeight() / 2);
70 | midPointer24.setOnTouchListener(new MidPointTouchListenerImpl(pointer2, pointer4));
71 |
72 | addView(pointer1);
73 | addView(pointer2);
74 | addView(midPointer13);
75 | addView(midPointer12);
76 | addView(midPointer34);
77 | addView(midPointer24);
78 | addView(pointer3);
79 | addView(pointer4);
80 | initPaint();
81 | }
82 |
83 | @Override
84 | protected void attachViewToParent(View child, int index, ViewGroup.LayoutParams params) {
85 | super.attachViewToParent(child, index, params);
86 | }
87 |
88 | private void initPaint() {
89 | paint = new Paint();
90 | paint.setColor(getResources().getColor(R.color.blue));
91 | paint.setStrokeWidth(2);
92 | paint.setAntiAlias(true);
93 | }
94 |
95 | public Map getPoints() {
96 |
97 | List points = new ArrayList();
98 | points.add(new PointF(pointer1.getX(), pointer1.getY()));
99 | points.add(new PointF(pointer2.getX(), pointer2.getY()));
100 | points.add(new PointF(pointer3.getX(), pointer3.getY()));
101 | points.add(new PointF(pointer4.getX(), pointer4.getY()));
102 |
103 | return getOrderedPoints(points);
104 | }
105 |
106 | public static Map getOrderedPoints(List points) {
107 |
108 | PointF centerPoint = new PointF();
109 | int size = points.size();
110 | for (PointF pointF : points) {
111 | centerPoint.x += pointF.x / size;
112 | centerPoint.y += pointF.y / size;
113 | }
114 | Map orderedPoints = new HashMap<>();
115 | for (PointF pointF : points) {
116 | int index = -1;
117 | if (pointF.x < centerPoint.x && pointF.y < centerPoint.y) {
118 | index = 0;
119 | } else if (pointF.x > centerPoint.x && pointF.y < centerPoint.y) {
120 | index = 1;
121 | } else if (pointF.x < centerPoint.x && pointF.y > centerPoint.y) {
122 | index = 2;
123 | } else if (pointF.x > centerPoint.x && pointF.y > centerPoint.y) {
124 | index = 3;
125 | }
126 | orderedPoints.put(index, pointF);
127 | }
128 | return orderedPoints;
129 | }
130 |
131 | public void setPoints(Map pointFMap) {
132 | if (pointFMap.size() == 4) {
133 | setPointsCoordinates(pointFMap);
134 | }
135 | }
136 |
137 | private void setPointsCoordinates(Map pointFMap) {
138 | pointer1.setX(pointFMap.get(0).x);
139 | pointer1.setY(pointFMap.get(0).y);
140 |
141 | pointer2.setX(pointFMap.get(1).x);
142 | pointer2.setY(pointFMap.get(1).y);
143 |
144 | pointer3.setX(pointFMap.get(2).x);
145 | pointer3.setY(pointFMap.get(2).y);
146 |
147 | pointer4.setX(pointFMap.get(3).x);
148 | pointer4.setY(pointFMap.get(3).y);
149 | }
150 |
151 | @Override
152 | protected void dispatchDraw(Canvas canvas) {
153 | super.dispatchDraw(canvas);
154 | canvas.drawLine(pointer1.getX() + (pointer1.getWidth() / 2), pointer1.getY() + (pointer1.getHeight() / 2), pointer3.getX() + (pointer3.getWidth() / 2), pointer3.getY() + (pointer3.getHeight() / 2), paint);
155 | canvas.drawLine(pointer1.getX() + (pointer1.getWidth() / 2), pointer1.getY() + (pointer1.getHeight() / 2), pointer2.getX() + (pointer2.getWidth() / 2), pointer2.getY() + (pointer2.getHeight() / 2), paint);
156 | canvas.drawLine(pointer2.getX() + (pointer2.getWidth() / 2), pointer2.getY() + (pointer2.getHeight() / 2), pointer4.getX() + (pointer4.getWidth() / 2), pointer4.getY() + (pointer4.getHeight() / 2), paint);
157 | canvas.drawLine(pointer3.getX() + (pointer3.getWidth() / 2), pointer3.getY() + (pointer3.getHeight() / 2), pointer4.getX() + (pointer4.getWidth() / 2), pointer4.getY() + (pointer4.getHeight() / 2), paint);
158 | midPointer13.setX(pointer3.getX() - ((pointer3.getX() - pointer1.getX()) / 2));
159 | midPointer13.setY(pointer3.getY() - ((pointer3.getY() - pointer1.getY()) / 2));
160 | midPointer24.setX(pointer4.getX() - ((pointer4.getX() - pointer2.getX()) / 2));
161 | midPointer24.setY(pointer4.getY() - ((pointer4.getY() - pointer2.getY()) / 2));
162 | midPointer34.setX(pointer4.getX() - ((pointer4.getX() - pointer3.getX()) / 2));
163 | midPointer34.setY(pointer4.getY() - ((pointer4.getY() - pointer3.getY()) / 2));
164 | midPointer12.setX(pointer2.getX() - ((pointer2.getX() - pointer1.getX()) / 2));
165 | midPointer12.setY(pointer2.getY() - ((pointer2.getY() - pointer1.getY()) / 2));
166 | }
167 |
168 | private ImageView getImageView(int x, int y) {
169 | ImageView imageView = new ImageView(context);
170 | LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
171 | imageView.setLayoutParams(layoutParams);
172 | imageView.setImageResource(R.drawable.circle);
173 | imageView.setX(x);
174 | imageView.setY(y);
175 | imageView.setOnTouchListener(new TouchListenerImpl());
176 | return imageView;
177 | }
178 |
179 | private class MidPointTouchListenerImpl implements OnTouchListener {
180 |
181 | PointF DownPT = new PointF(); // Record Mouse Position When Pressed Down
182 | PointF StartPT = new PointF(); // Record Start Position of 'img'
183 |
184 | private ImageView mainPointer1;
185 | private ImageView mainPointer2;
186 |
187 | public MidPointTouchListenerImpl(ImageView mainPointer1, ImageView mainPointer2) {
188 | this.mainPointer1 = mainPointer1;
189 | this.mainPointer2 = mainPointer2;
190 | }
191 |
192 | @Override
193 | public boolean onTouch(View v, MotionEvent event) {
194 | int eid = event.getAction();
195 | switch (eid) {
196 | case MotionEvent.ACTION_MOVE:
197 | PointF mv = new PointF(event.getX() - DownPT.x, event.getY() - DownPT.y);
198 |
199 | if (Math.abs(mainPointer1.getX() - mainPointer2.getX()) > Math.abs(mainPointer1.getY() - mainPointer2.getY())) {
200 | if (((mainPointer2.getY() + mv.y + v.getHeight() < polygonView.getHeight()) && (mainPointer2.getY() + mv.y > 0))) {
201 | v.setX((int) (StartPT.y + mv.y));
202 | StartPT = new PointF(v.getX(), v.getY());
203 | mainPointer2.setY((int) (mainPointer2.getY() + mv.y));
204 | }
205 | if (((mainPointer1.getY() + mv.y + v.getHeight() < polygonView.getHeight()) && (mainPointer1.getY() + mv.y > 0))) {
206 | v.setX((int) (StartPT.y + mv.y));
207 | StartPT = new PointF(v.getX(), v.getY());
208 | mainPointer1.setY((int) (mainPointer1.getY() + mv.y));
209 | }
210 | } else {
211 | if ((mainPointer2.getX() + mv.x + v.getWidth() < polygonView.getWidth()) && (mainPointer2.getX() + mv.x > 0)) {
212 | v.setX((int) (StartPT.x + mv.x));
213 | StartPT = new PointF(v.getX(), v.getY());
214 | mainPointer2.setX((int) (mainPointer2.getX() + mv.x));
215 | }
216 | if ((mainPointer1.getX() + mv.x + v.getWidth() < polygonView.getWidth()) && (mainPointer1.getX() + mv.x > 0)) {
217 | v.setX((int) (StartPT.x + mv.x));
218 | StartPT = new PointF(v.getX(), v.getY());
219 | mainPointer1.setX((int) (mainPointer1.getX() + mv.x));
220 | }
221 | }
222 |
223 | break;
224 | case MotionEvent.ACTION_DOWN:
225 | DownPT.x = event.getX();
226 | DownPT.y = event.getY();
227 | StartPT = new PointF(v.getX(), v.getY());
228 | break;
229 | case MotionEvent.ACTION_UP:
230 | int color = 0;
231 | if (isValidShape(getPoints())) {
232 | color = getResources().getColor(R.color.blue);
233 | } else {
234 | color = getResources().getColor(R.color.orange);
235 | }
236 | paint.setColor(color);
237 | break;
238 | default:
239 | break;
240 | }
241 | polygonView.invalidate();
242 | return true;
243 | }
244 | }
245 |
246 | @Override
247 | public boolean onTouchEvent(MotionEvent event) {
248 | return super.onTouchEvent(event);
249 | }
250 |
251 | public static boolean isValidShape(Map pointFMap) {
252 | return pointFMap.size() == 4;
253 | }
254 |
255 | private class TouchListenerImpl implements OnTouchListener {
256 |
257 | PointF DownPT = new PointF(); // Record Mouse Position When Pressed Down
258 | PointF StartPT = new PointF(); // Record Start Position of 'img'
259 |
260 | @Override
261 | public boolean onTouch(View v, MotionEvent event) {
262 | int eid = event.getAction();
263 | switch (eid) {
264 | case MotionEvent.ACTION_MOVE:
265 | PointF mv = new PointF(event.getX() - DownPT.x, event.getY() - DownPT.y);
266 | if (((StartPT.x + mv.x + v.getWidth()) < polygonView.getWidth() && (StartPT.y + mv.y + v.getHeight() < polygonView.getHeight())) && ((StartPT.x + mv.x) > 0 && StartPT.y + mv.y > 0)) {
267 | v.setX((int) (StartPT.x + mv.x));
268 | v.setY((int) (StartPT.y + mv.y));
269 | StartPT = new PointF(v.getX(), v.getY());
270 | }
271 | break;
272 | case MotionEvent.ACTION_DOWN:
273 | DownPT.x = event.getX();
274 | DownPT.y = event.getY();
275 | StartPT = new PointF(v.getX(), v.getY());
276 | break;
277 | case MotionEvent.ACTION_UP:
278 | int color = 0;
279 | if (isValidShape(getPoints())) {
280 | color = getResources().getColor(R.color.blue);
281 | } else {
282 | color = getResources().getColor(R.color.orange);
283 | }
284 | paint.setColor(color);
285 | break;
286 | default:
287 | break;
288 | }
289 | polygonView.invalidate();
290 | return true;
291 | }
292 |
293 | }
294 |
295 |
296 | }
297 |
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/java/com/scanlibrary/ProgressDialogFragment.java:
--------------------------------------------------------------------------------
1 | package com.scanlibrary;
2 |
3 | import android.app.Dialog;
4 | import android.app.DialogFragment;
5 | import android.app.ProgressDialog;
6 | import android.os.Bundle;
7 |
8 | public class ProgressDialogFragment extends DialogFragment {
9 | // ===========================================================
10 | // Constants
11 | // ===========================================================
12 | public static final String EXTRA_MESSAGE = "message";
13 | // ===========================================================
14 | // Methods for/from SuperClass/Interfaces
15 | // ===========================================================
16 |
17 | @Override
18 | public Dialog onCreateDialog(Bundle savedInstanceState) {
19 | ProgressDialog dialog = new ProgressDialog(getActivity());
20 | if (getArguments() != null && getArguments().containsKey(EXTRA_MESSAGE)) {
21 | dialog.setMessage(getArguments().getString(EXTRA_MESSAGE));
22 | }
23 |
24 | return dialog;
25 | }
26 |
27 | // ===========================================================
28 | // Methods
29 | // ===========================================================
30 |
31 | // ===========================================================
32 | // Inner and Anonymous Classes
33 | // ===========================================================
34 | }
35 |
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/java/com/scanlibrary/ScalingUtilities.java:
--------------------------------------------------------------------------------
1 | package com.scanlibrary;
2 |
3 | import android.content.res.Resources;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.graphics.BitmapFactory.Options;
7 | import android.graphics.Canvas;
8 | import android.graphics.Paint;
9 | import android.graphics.Rect;
10 |
11 | public class ScalingUtilities {
12 | /**
13 | * ScalingLogic defines how scaling should be carried out if source and destination image has different aspect ratio.
14 | *
15 | * CROP: Scales the image the minimum amount while making sure that at least one of the two dimensions fit inside the requested destination area. Parts of the source image will
16 | * be cropped to realize this.
17 | *
18 | * FIT: Scales the image the minimum amount while making sure both dimensions fit inside the requested destination area. The resulting destination dimensions might be adjusted
19 | * to a smaller size than requested.
20 | */
21 | public static enum ScalingLogic {
22 | CROP, FIT
23 | }
24 |
25 | /**
26 | * Utility function for decoding an image resource. The decoded bitmap will be optimized for further scaling to the requested destination dimensions and scaling logic.
27 | *
28 | * @param res
29 | * The resources object containing the image data
30 | * @param resId
31 | * The resource id of the image data
32 | * @param dstWidth
33 | * Width of destination area
34 | * @param dstHeight
35 | * Height of destination area
36 | * @param scalingLogic
37 | * Logic to use to avoid image stretching
38 | * @return Decoded bitmap
39 | */
40 | public static Bitmap decodeResource(Resources res, int resId, int dstWidth, int dstHeight, ScalingLogic scalingLogic) {
41 | Options options = new Options();
42 | options.inJustDecodeBounds = true;
43 | BitmapFactory.decodeResource(res, resId, options);
44 | options.inJustDecodeBounds = false;
45 | options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, dstWidth, dstHeight, scalingLogic);
46 | Bitmap unscaledBitmap = BitmapFactory.decodeResource(res, resId, options);
47 |
48 | return unscaledBitmap;
49 | }
50 |
51 | public static Bitmap decodeFile(String path, int dstWidth, int dstHeight, ScalingLogic scalingLogic) {
52 | Options options = new Options();
53 | options.inJustDecodeBounds = true;
54 | BitmapFactory.decodeFile(path, options);
55 | options.inJustDecodeBounds = false;
56 | options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, dstWidth, dstHeight, scalingLogic);
57 | Bitmap unscaledBitmap = BitmapFactory.decodeFile(path, options);
58 |
59 | return unscaledBitmap;
60 | }
61 |
62 | /**
63 | * Utility function for creating a scaled version of an existing bitmap
64 | *
65 | * @param unscaledBitmap
66 | * Bitmap to scale
67 | * @param dstWidth
68 | * Wanted width of destination bitmap
69 | * @param dstHeight
70 | * Wanted height of destination bitmap
71 | * @param scalingLogic
72 | * Logic to use to avoid image stretching
73 | * @return New scaled bitmap object
74 | */
75 | public static Bitmap createScaledBitmap(Bitmap unscaledBitmap, int dstWidth, int dstHeight, ScalingLogic scalingLogic) {
76 | Rect srcRect = calculateSrcRect(unscaledBitmap.getWidth(), unscaledBitmap.getHeight(), dstWidth, dstHeight, scalingLogic);
77 | Rect dstRect = calculateDstRect(unscaledBitmap.getWidth(), unscaledBitmap.getHeight(), dstWidth, dstHeight, scalingLogic);
78 | Bitmap scaledBitmap = Bitmap.createBitmap(dstRect.width(), dstRect.height(), Bitmap.Config.ARGB_8888);
79 | Canvas canvas = new Canvas(scaledBitmap);
80 | canvas.drawBitmap(unscaledBitmap, srcRect, dstRect, new Paint(Paint.FILTER_BITMAP_FLAG));
81 |
82 | return scaledBitmap;
83 | }
84 |
85 | /**
86 | * Calculate optimal down-sampling factor given the dimensions of a source image, the dimensions of a destination area and a scaling logic.
87 | *
88 | * @param srcWidth
89 | * Width of source image
90 | * @param srcHeight
91 | * Height of source image
92 | * @param dstWidth
93 | * Width of destination area
94 | * @param dstHeight
95 | * Height of destination area
96 | * @param scalingLogic
97 | * Logic to use to avoid image stretching
98 | * @return Optimal down scaling sample size for decoding
99 | */
100 | public static int calculateSampleSize(int srcWidth, int srcHeight, int dstWidth, int dstHeight, ScalingLogic scalingLogic) {
101 | if (scalingLogic == ScalingLogic.FIT) {
102 | final float srcAspect = (float) srcWidth / (float) srcHeight;
103 | final float dstAspect = (float) dstWidth / (float) dstHeight;
104 |
105 | if (srcAspect > dstAspect) {
106 | return srcWidth / dstWidth;
107 | } else {
108 | return srcHeight / dstHeight;
109 | }
110 | } else {
111 | final float srcAspect = (float) srcWidth / (float) srcHeight;
112 | final float dstAspect = (float) dstWidth / (float) dstHeight;
113 |
114 | if (srcAspect > dstAspect) {
115 | return srcHeight / dstHeight;
116 | } else {
117 | return srcWidth / dstWidth;
118 | }
119 | }
120 | }
121 |
122 | /**
123 | * Calculates source rectangle for scaling bitmap
124 | *
125 | * @param srcWidth
126 | * Width of source image
127 | * @param srcHeight
128 | * Height of source image
129 | * @param dstWidth
130 | * Width of destination area
131 | * @param dstHeight
132 | * Height of destination area
133 | * @param scalingLogic
134 | * Logic to use to avoid image stretching
135 | * @return Optimal source rectangle
136 | */
137 | public static Rect calculateSrcRect(int srcWidth, int srcHeight, int dstWidth, int dstHeight, ScalingLogic scalingLogic) {
138 | if (scalingLogic == ScalingLogic.CROP) {
139 | final float srcAspect = (float) srcWidth / (float) srcHeight;
140 | final float dstAspect = (float) dstWidth / (float) dstHeight;
141 |
142 | if (srcAspect > dstAspect) {
143 | final int srcRectWidth = (int) (srcHeight * dstAspect);
144 | final int srcRectLeft = (srcWidth - srcRectWidth) / 2;
145 | return new Rect(srcRectLeft, 0, srcRectLeft + srcRectWidth, srcHeight);
146 | } else {
147 | final int srcRectHeight = (int) (srcWidth / dstAspect);
148 | final int scrRectTop = (int) (srcHeight - srcRectHeight) / 2;
149 | return new Rect(0, scrRectTop, srcWidth, scrRectTop + srcRectHeight);
150 | }
151 | } else {
152 | return new Rect(0, 0, srcWidth, srcHeight);
153 | }
154 | }
155 |
156 | /**
157 | * Calculates destination rectangle for scaling bitmap
158 | *
159 | * @param srcWidth
160 | * Width of source image
161 | * @param srcHeight
162 | * Height of source image
163 | * @param dstWidth
164 | * Width of destination area
165 | * @param dstHeight
166 | * Height of destination area
167 | * @param scalingLogic
168 | * Logic to use to avoid image stretching
169 | * @return Optimal destination rectangle
170 | */
171 | public static Rect calculateDstRect(int srcWidth, int srcHeight, int dstWidth, int dstHeight, ScalingLogic scalingLogic) {
172 | if (scalingLogic == ScalingLogic.FIT) {
173 | final float srcAspect = (float) srcWidth / (float) srcHeight;
174 | final float dstAspect = (float) dstWidth / (float) dstHeight;
175 |
176 | if (srcAspect > dstAspect) {
177 | return new Rect(0, 0, dstWidth, (int) (dstWidth / srcAspect));
178 | } else {
179 | return new Rect(0, 0, (int) (dstHeight * srcAspect), dstHeight);
180 | }
181 | } else {
182 | return new Rect(0, 0, dstWidth, dstHeight);
183 | }
184 | }
185 |
186 | }
187 |
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/java/com/scanlibrary/ScanActivity.java:
--------------------------------------------------------------------------------
1 | package com.scanlibrary;
2 |
3 | import android.app.Activity;
4 | import android.app.Fragment;
5 | import android.app.FragmentManager;
6 | import android.app.FragmentTransaction;
7 | import android.content.res.Configuration;
8 | import android.graphics.drawable.ColorDrawable;
9 | import android.os.Bundle;
10 |
11 | import java.util.Locale;
12 |
13 | public class ScanActivity extends Activity {
14 |
15 | public static final String EXTRA_BRAND_IMG_RES = "title_img_res";
16 | public static final String EXTRA_TITLE = "title";
17 | public static final String EXTRA_LANGUAGE = "language";
18 | public static final String EXTRA_ACTION_BAR_COLOR = "ab_color";
19 | public static final String RESULT_IMAGE_PATH = ScanFragment.RESULT_IMAGE_PATH;
20 |
21 | @Override
22 | protected void onCreate(Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 | setContentView(R.layout.activity_scan);
25 |
26 | int titleImgRes = getIntent().getExtras().getInt(EXTRA_BRAND_IMG_RES);
27 | int abColor = getIntent().getExtras().getInt(EXTRA_ACTION_BAR_COLOR);
28 | String title = getIntent().getExtras().getString(EXTRA_TITLE);
29 | String locale = getIntent().getExtras().getString(EXTRA_LANGUAGE);
30 |
31 | if (locale != null) {
32 | Locale l = new Locale(locale);
33 | Locale.setDefault(l);
34 | Configuration config = new Configuration();
35 | config.locale = l;
36 | getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
37 | }
38 |
39 | if (title != null) setTitle(title);
40 | if (titleImgRes != 0) getActionBar().setLogo(titleImgRes);
41 |
42 | if (abColor != 0) {
43 | getActionBar().setBackgroundDrawable(new ColorDrawable(getResources().getColor(abColor)));
44 | getActionBar().setDisplayShowTitleEnabled(false);
45 | getActionBar().setDisplayShowTitleEnabled(true);
46 | }
47 |
48 | if (savedInstanceState == null) {
49 | Bundle args = new Bundle();
50 | if (getIntent().getExtras() != null) {
51 | args.putAll(getIntent().getExtras());
52 | }
53 |
54 | FragmentManager fragMan = getFragmentManager();
55 | Fragment f = new ScanFragment();
56 | f.setArguments(args);
57 | FragmentTransaction fragTransaction = fragMan.beginTransaction();
58 | fragTransaction.replace(R.id.contaner, f, "scan_frag").commit();
59 | }
60 | }
61 |
62 | @Override
63 | public void onBackPressed() {
64 | ScanFragment scanFragment = (ScanFragment) getFragmentManager().findFragmentByTag("scan_frag");
65 | if (scanFragment != null) {
66 | boolean exit = scanFragment.onBackPressed();
67 | if (exit) {
68 | super.onBackPressed();
69 | }
70 | } else {
71 | super.onBackPressed();
72 | }
73 | }
74 |
75 | }
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/java/com/scanlibrary/ScanFragment.java:
--------------------------------------------------------------------------------
1 | package com.scanlibrary;
2 |
3 | import android.app.Activity;
4 | import android.app.Fragment;
5 | import android.content.Intent;
6 | import android.graphics.Bitmap;
7 | import android.graphics.Bitmap.CompressFormat;
8 | import android.graphics.BitmapFactory;
9 | import android.graphics.PointF;
10 | import android.graphics.drawable.BitmapDrawable;
11 | import android.net.Uri;
12 | import android.os.AsyncTask;
13 | import android.os.Bundle;
14 | import android.provider.MediaStore;
15 | import android.util.Log;
16 | import android.view.Gravity;
17 | import android.view.LayoutInflater;
18 | import android.view.Menu;
19 | import android.view.MenuInflater;
20 | import android.view.MenuItem;
21 | import android.view.View;
22 | import android.view.ViewGroup;
23 | import android.widget.FrameLayout;
24 | import android.widget.ImageView;
25 | import android.widget.Toast;
26 |
27 | import com.davemorrissey.labs.subscaleview.ScaleImageView;
28 |
29 | import org.opencv.core.Point;
30 |
31 | import java.io.File;
32 | import java.io.FileOutputStream;
33 | import java.io.IOException;
34 | import java.util.ArrayList;
35 | import java.util.HashMap;
36 | import java.util.List;
37 | import java.util.Map;
38 |
39 | public class ScanFragment extends Fragment {
40 |
41 |
42 | // ===========================================================
43 | // Constants
44 | // ===========================================================
45 |
46 | public static final String RESULT_IMAGE_PATH = "imgPath";
47 |
48 | private static final int TAKE_PHOTO_REQUEST_CODE = 815;
49 | private static final String SAVED_ARG_TAKEN_PHOTO_LOCATION = "taken_photo_loc";
50 |
51 | private static final int MODE_NONE = 0;
52 | private static final int MODE_BLACK_AND_WHITE = 1;
53 | private static final int MODE_MAGIC = 2;
54 |
55 | // ===========================================================
56 | // Fields
57 | // ===========================================================
58 |
59 | private ViewHolder viewHolder = new ViewHolder();
60 | private ProgressDialogFragment progressDialogFragment;
61 |
62 | private String takenPhotoLocation;
63 | private Bitmap takenPhotoBitmap;
64 | private Bitmap documentBitmap;
65 | private Bitmap documentColoredBitmap;
66 |
67 | private Map points;
68 |
69 | private boolean isCropMode = false;
70 | private int currentMode = MODE_MAGIC;
71 |
72 | private int previousOreantation = -1;
73 |
74 | // ===========================================================
75 | // Constructors
76 | // ===========================================================
77 |
78 | // ===========================================================
79 | // Getters & Setters
80 | // ===========================================================
81 |
82 | // ===========================================================
83 | // Methods for/from SuperClass/Interfaces
84 | // ===========================================================
85 |
86 |
87 | @Override
88 | public void onCreate(Bundle savedInstanceState) {
89 | super.onCreate(savedInstanceState);
90 | setRetainInstance(true);
91 | setHasOptionsMenu(true);
92 | }
93 |
94 | @Override
95 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
96 | return inflater.inflate(R.layout.fragment_scan, container, false);
97 | }
98 |
99 | @Override
100 | public void onViewCreated(View view, Bundle savedInstanceState) {
101 | viewHolder.prepare(view);
102 | super.onViewCreated(view, savedInstanceState);
103 |
104 | int currentOreantation = Utils.getScreenOrientation(getActivity());
105 | if (previousOreantation == -1) {
106 | previousOreantation = currentOreantation;
107 | } else if (previousOreantation != currentOreantation) {
108 | points = null;
109 | }
110 |
111 | if (takenPhotoLocation == null) {
112 | takePhoto();
113 | } else {
114 | if (documentBitmap != null) {
115 | updateViewsWithNewBitmap();
116 | viewHolder.sourceImageView.setVisibility(View.INVISIBLE);
117 | viewHolder.scaleImageView.setVisibility(View.VISIBLE);
118 | }
119 | }
120 |
121 | if (isCropMode) {
122 | viewHolder.sourceFrame.post(new Runnable() {
123 | @Override
124 | public void run() {
125 | Bitmap scaledBitmap = ImageResizer.scaleBitmap(takenPhotoBitmap, viewHolder.sourceFrame.getWidth(), viewHolder.sourceFrame.getHeight());
126 | viewHolder.sourceImageView.setImageBitmap(scaledBitmap);
127 |
128 | Bitmap tempBitmap = ((BitmapDrawable) viewHolder.sourceImageView.getDrawable()).getBitmap();
129 | viewHolder.polygonView.setVisibility(View.VISIBLE);
130 | int padding = (int) getResources().getDimension(R.dimen.scanPadding);
131 | FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(tempBitmap.getWidth() + 2 * padding, tempBitmap.getHeight() + 2 * padding);
132 | layoutParams.gravity = Gravity.CENTER;
133 | viewHolder.polygonView.setLayoutParams(layoutParams);
134 |
135 | if (points == null) {
136 | points = getOutlinePoints(tempBitmap);
137 | }
138 | viewHolder.polygonView.setPoints(points);
139 | }
140 | });
141 | }
142 | }
143 |
144 | @Override
145 | public void onActivityResult(int requestCode, int resultCode, Intent intent) {
146 | if (requestCode == TAKE_PHOTO_REQUEST_CODE) {
147 | if (resultCode == Activity.RESULT_OK) {
148 | onPhotoTaken();
149 | } else if (resultCode == Activity.RESULT_CANCELED) {
150 | removeFile(takenPhotoLocation);
151 | getActivity().setResult(Activity.RESULT_CANCELED);
152 | getActivity().finish();
153 | }
154 | }
155 | }
156 |
157 | @Override
158 | public void onSaveInstanceState(Bundle outState) {
159 | outState.putString(SAVED_ARG_TAKEN_PHOTO_LOCATION, takenPhotoLocation);
160 | super.onSaveInstanceState(outState);
161 | }
162 |
163 | @Override
164 | public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
165 | inflater.inflate(R.menu.scan_menu, menu);
166 |
167 | MenuItem cropBtn = menu.findItem(R.id.crop);
168 | MenuItem rotateBtn = menu.findItem(R.id.rotate);
169 | MenuItem modeBtn = menu.findItem(R.id.colors);
170 | MenuItem modeNone = menu.findItem(R.id.mode_none);
171 | MenuItem modeBlackAndWhite = menu.findItem(R.id.mode_black_and_white);
172 | MenuItem modeMagic = menu.findItem(R.id.mode_magic);
173 |
174 | if (currentMode == MODE_NONE) {
175 | modeNone.setChecked(true);
176 | } else if (currentMode == MODE_BLACK_AND_WHITE) {
177 | modeBlackAndWhite.setChecked(true);
178 | } else if (currentMode == MODE_MAGIC) {
179 | modeMagic.setChecked(true);
180 | }
181 |
182 | cropBtn.setVisible(!isCropMode);
183 | rotateBtn.setVisible(!isCropMode);
184 | modeBtn.setVisible(!isCropMode);
185 |
186 | super.onCreateOptionsMenu(menu, inflater);
187 | }
188 |
189 |
190 | @Override
191 | public boolean onOptionsItemSelected(MenuItem item) {
192 |
193 | if (item.getItemId() == R.id.crop) {
194 | onCropButtonClicked();
195 | return true;
196 | } else if (item.getItemId() == R.id.done) {
197 | onDoneButtonClicked();
198 | return true;
199 | } else if (item.getItemId() == R.id.rotate) {
200 | onRotateButtonClicked();
201 | return true;
202 | } else if (item.getItemId() == R.id.mode_none) {
203 | if (!item.isChecked()) {
204 | currentMode = MODE_NONE;
205 | item.setChecked(true);
206 | onNoneModeChosen();
207 | }
208 |
209 | return true;
210 | } else if (item.getItemId() == R.id.mode_black_and_white) {
211 | if (!item.isChecked()) {
212 | currentMode = MODE_BLACK_AND_WHITE;
213 | item.setChecked(true);
214 | onBlackAndWhiteModeChosen();
215 | }
216 |
217 | return true;
218 | } else if (item.getItemId() == R.id.mode_magic) {
219 | if (!item.isChecked()) {
220 | currentMode = MODE_MAGIC;
221 | item.setChecked(true);
222 | onMagicModeChosen();
223 | }
224 |
225 | return true;
226 | }
227 | return super.onOptionsItemSelected(item);
228 | }
229 |
230 |
231 | // ===========================================================
232 | // Methods
233 | // ===========================================================
234 | private void onMagicModeChosen() {
235 | showProgressDialog();
236 | new ModeChangingTask(MODE_MAGIC, documentBitmap).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
237 | }
238 |
239 | private void onBlackAndWhiteModeChosen() {
240 | showProgressDialog();
241 | new ModeChangingTask(MODE_BLACK_AND_WHITE, documentBitmap).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
242 | }
243 |
244 | private void onNoneModeChosen() {
245 | if (documentColoredBitmap != null) {
246 | documentColoredBitmap.recycle();
247 | documentColoredBitmap = null;
248 | }
249 | updateViewsWithNewBitmap();
250 | }
251 |
252 | private void updateViewsWithNewBitmap() {
253 | Bitmap tmp = documentColoredBitmap != null ? documentColoredBitmap : documentBitmap;
254 | int width = viewHolder.sourceFrame.getWidth();
255 | int height = viewHolder.sourceFrame.getHeight();
256 | if (width <= 0 || height <= 0) {
257 | width = 2048;
258 | height = 2048;
259 | }
260 |
261 | tmp = ImageResizer.scaleBitmap(tmp, width, height);
262 | viewHolder.sourceImageView.setImageBitmap(tmp);
263 | viewHolder.scaleImageView.setImageBitmap(tmp);
264 | }
265 |
266 | /**
267 | * Called From activity
268 | */
269 | public boolean onBackPressed() {
270 | if (isCropMode) {
271 | updateViewsWithNewBitmap();
272 | viewHolder.sourceImageView.setVisibility(View.INVISIBLE);
273 | viewHolder.scaleImageView.setVisibility(View.VISIBLE);
274 | viewHolder.polygonView.setVisibility(View.GONE);
275 |
276 | getActivity().invalidateOptionsMenu();
277 | isCropMode = false;
278 | return false;
279 | }
280 |
281 | releaseAllBitmaps();
282 | if (takenPhotoLocation != null) {
283 | removeFile(takenPhotoLocation);
284 | }
285 |
286 | return true;
287 | }
288 |
289 | private void releaseAllBitmaps() {
290 | if (takenPhotoBitmap != null) takenPhotoBitmap.recycle();
291 | if (documentBitmap != null) documentBitmap.recycle();
292 | }
293 |
294 | private void onCropButtonClicked() {
295 | getActivity().invalidateOptionsMenu();
296 | isCropMode = true;
297 |
298 | if (points == null) {
299 | showProgressDialog();
300 | new CropTask(takenPhotoBitmap).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
301 | return;
302 | }
303 | CropTaskResult result = new CropTaskResult();
304 | result.points = points;
305 | onCropTaskFinished(result);
306 | }
307 |
308 | private void onRotateButtonClicked() {
309 | showProgressDialog();
310 | new RotatingTask(takenPhotoBitmap, documentBitmap, documentColoredBitmap).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
311 | }
312 |
313 | private void onDoneButtonClicked() {
314 | if (isCropMode) {
315 | isCropMode = false;
316 | getActivity().invalidateOptionsMenu();
317 |
318 | Map points = viewHolder.polygonView.getPoints();
319 | if (isScanPointsValid(points)) {
320 | upScalePoints(points, takenPhotoBitmap, viewHolder.sourceImageView.getWidth(), viewHolder.sourceImageView.getHeight());
321 | new DocumentFromBitmapTask(takenPhotoBitmap, points, currentMode).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
322 | } else {
323 | showErrorDialog();
324 | }
325 |
326 | } else {
327 | File scannedDocFile = createImageFile("scanned_doc");
328 |
329 | Bitmap tmp = documentColoredBitmap != null ? documentColoredBitmap : documentBitmap;
330 |
331 | try {
332 | tmp = ImageResizer.resizeImage(tmp, 2048, 2048);
333 | } catch (IOException e) {
334 | throw new RuntimeException("Not able to resize image");
335 | }
336 |
337 | saveBitmapToFile(scannedDocFile, tmp);
338 |
339 | takenPhotoBitmap.recycle();
340 | documentBitmap.recycle();
341 | if (documentColoredBitmap != null) documentColoredBitmap.recycle();
342 |
343 | removeFile(takenPhotoLocation);
344 | releaseAllBitmaps();
345 |
346 | Intent intent = new Intent();
347 | intent.putExtra(RESULT_IMAGE_PATH, scannedDocFile.getAbsolutePath());
348 | getActivity().setResult(Activity.RESULT_OK, intent);
349 | getActivity().finish();
350 | }
351 | }
352 |
353 | private boolean saveBitmapToFile(File file, Bitmap bitmap) {
354 | FileOutputStream out = null;
355 | try {
356 | out = new FileOutputStream(file);
357 | bitmap.compress(CompressFormat.JPEG, 100, out);
358 | // PNG is a lossless format, the compression factor (100) is ignored
359 | } catch (Exception e) {
360 | e.printStackTrace();
361 | return false;
362 | } finally {
363 | try {
364 | if (out != null) {
365 | out.close();
366 | }
367 | } catch (IOException e) {
368 | e.printStackTrace();
369 | return false;
370 | }
371 | }
372 |
373 | return true;
374 | }
375 |
376 | private void onPhotoTaken() {
377 | takenPhotoBitmap = getBitmapFromLocation(takenPhotoLocation);
378 |
379 | DocumentFromBitmapTask documentFromBitmapTask = new DocumentFromBitmapTask(takenPhotoBitmap, null, currentMode);
380 | documentFromBitmapTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
381 | }
382 |
383 | private static Bitmap cropDocumentFromBitmap(Bitmap bitmap, Map points) {
384 | float x1 = (points.get(0).x);
385 | float x2 = (points.get(1).x);
386 | float x3 = (points.get(2).x);
387 | float x4 = (points.get(3).x);
388 | float y1 = (points.get(0).y);
389 | float y2 = (points.get(1).y);
390 | float y3 = (points.get(2).y);
391 | float y4 = (points.get(3).y);
392 | Log.d("", "POints(" + x1 + "," + y1 + ")(" + x2 + "," + y2 + ")(" + x3 + "," + y3 + ")(" + x4 + "," + y4 + ")");
393 | return ScanUtils.getScannedBitmap(bitmap, x1, y1, x2, y2, x3, y3, x4, y4);
394 | }
395 |
396 | private static void upScalePoints(Map points, Bitmap original, int scaledImgWidth, int scaledImgHeight) {
397 | int width = original.getWidth();
398 | int height = original.getHeight();
399 | float xRatio = (float) width / scaledImgWidth;
400 | float yRatio = (float) height / scaledImgHeight;
401 |
402 | points.get(0).x = (points.get(0).x) * xRatio;
403 | points.get(1).x = (points.get(1).x) * xRatio;
404 | points.get(2).x = (points.get(2).x) * xRatio;
405 | points.get(3).x = (points.get(3).x) * xRatio;
406 | points.get(0).y = (points.get(0).y) * yRatio;
407 | points.get(1).y = (points.get(1).y) * yRatio;
408 | points.get(2).y = (points.get(2).y) * yRatio;
409 | points.get(3).y = (points.get(3).y) * yRatio;
410 | }
411 |
412 | private static Map downScalePoints(Map points, Bitmap original, int targetImgWidth, int targetImgHeight) {
413 | int width = original.getWidth();
414 | int height = original.getHeight();
415 | float xRatio = (float) width / targetImgWidth;
416 | float yRatio = (float) height / targetImgHeight;
417 |
418 | Map scaledPoints = new HashMap<>(4);
419 | scaledPoints.put(0, new PointF((points.get(0).x) / xRatio, (points.get(0).y) / yRatio));
420 | scaledPoints.put(1, new PointF((points.get(1).x) / xRatio, (points.get(1).y) / yRatio));
421 | scaledPoints.put(2, new PointF((points.get(2).x) / xRatio, (points.get(2).y) / yRatio));
422 | scaledPoints.put(3, new PointF((points.get(3).x) / xRatio, (points.get(3).y) / yRatio));
423 | return scaledPoints;
424 | }
425 |
426 | private void takePhoto() {
427 | Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
428 |
429 | File photoFile = createImageFile("takendocphoto");
430 | takenPhotoLocation = photoFile.getAbsolutePath();
431 |
432 | takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
433 | startActivityForResult(takePictureIntent, TAKE_PHOTO_REQUEST_CODE);
434 | }
435 |
436 | private File createImageFile(String fileName) {
437 | File storageDir = getActivity().getExternalFilesDir("images");
438 | if (storageDir == null) {
439 | throw new RuntimeException("Not able to get to External storage");
440 | }
441 | File image = new File(storageDir, fileName + ".jpg");
442 |
443 | return image;
444 | }
445 |
446 | private void removeFile(String absoluteLocation) {
447 | if (absoluteLocation == null) return;
448 |
449 | File f = new File(absoluteLocation);
450 | if (f.exists()) {
451 | f.delete();
452 | }
453 | }
454 |
455 | private Bitmap getBitmapFromLocation(String absLocation) {
456 | BitmapFactory.Options options = new BitmapFactory.Options();
457 | options.inPreferredConfig = Bitmap.Config.ARGB_8888;
458 | return BitmapFactory.decodeFile(absLocation, options);
459 | }
460 |
461 | private static Map getEdgePoints(Bitmap tempBitmap) {
462 | List pointFs = getContourEdgePoints(tempBitmap);
463 | Map orderedPoints = orderedValidEdgePoints(tempBitmap, pointFs);
464 | return orderedPoints;
465 | }
466 |
467 | private static List getContourEdgePoints(Bitmap tempBitmap) {
468 | // float[] points = ScanActivity.getPoints(tempBitmap);
469 | // float x1 = points[0];
470 | // float x2 = points[1];
471 | // float x3 = points[2];
472 | // float x4 = points[3];
473 | //
474 | // float y1 = points[4];
475 | // float y2 = points[5];
476 | // float y3 = points[6];
477 | // float y4 = points[7];
478 | //
479 | // List pointFs = new ArrayList<>();
480 | // pointFs.add(new PointF(x1, y1));
481 | // pointFs.add(new PointF(x2, y2));
482 | // pointFs.add(new PointF(x3, y3));
483 | // pointFs.add(new PointF(x4, y4));
484 | // return pointFs;
485 | List points = ScanUtils.getPoints(tempBitmap);
486 |
487 | List p = new ArrayList<>();
488 | p.add(new PointF((float) points.get(0).x, (float) points.get(0).y));
489 | p.add(new PointF((float) points.get(1).x, (float) points.get(1).y));
490 | p.add(new PointF((float) points.get(2).x, (float) points.get(2).y));
491 | p.add(new PointF((float) points.get(3).x, (float) points.get(3).y));
492 |
493 | return p;
494 | }
495 |
496 | private static Map getOutlinePoints(Bitmap tempBitmap) {
497 | Map outlinePoints = new HashMap<>();
498 | outlinePoints.put(0, new PointF(0, 0));
499 | outlinePoints.put(1, new PointF(tempBitmap.getWidth(), 0));
500 | outlinePoints.put(2, new PointF(0, tempBitmap.getHeight()));
501 | outlinePoints.put(3, new PointF(tempBitmap.getWidth(), tempBitmap.getHeight()));
502 | return outlinePoints;
503 | }
504 |
505 | private static Map getOutlinePoints(View view) {
506 | Map outlinePoints = new HashMap<>();
507 | outlinePoints.put(0, new PointF(0, 0));
508 | outlinePoints.put(1, new PointF(view.getWidth(), 0));
509 | outlinePoints.put(2, new PointF(0, view.getHeight()));
510 | outlinePoints.put(3, new PointF(view.getWidth(), view.getHeight()));
511 | return outlinePoints;
512 | }
513 |
514 | private static Map orderedValidEdgePoints(Bitmap tempBitmap, List pointFs) {
515 | Map orderedPoints = PolygonView.getOrderedPoints(pointFs);
516 | if (!PolygonView.isValidShape(orderedPoints)) {
517 | orderedPoints = getOutlinePoints(tempBitmap);
518 | }
519 | return orderedPoints;
520 | }
521 |
522 | private void showErrorDialog() {
523 | Toast.makeText(getActivity(), R.string.cant_crop, Toast.LENGTH_LONG).show();
524 | }
525 |
526 | private boolean isScanPointsValid(Map points) {
527 | return points.size() == 4;
528 | }
529 |
530 |
531 | protected void showProgressDialog() {
532 | Bundle args = new Bundle();
533 | args.putString(ProgressDialogFragment.EXTRA_MESSAGE, getString(R.string.transforming));
534 | progressDialogFragment = new ProgressDialogFragment();
535 | progressDialogFragment.setArguments(args);
536 | progressDialogFragment.show(getFragmentManager(), "progress_dialog");
537 | }
538 |
539 | protected void dismissDialog() {
540 | ProgressDialogFragment progressDialogFragment = (ProgressDialogFragment) getFragmentManager().findFragmentByTag("progress_dialog");
541 | if (progressDialogFragment != null) progressDialogFragment.dismissAllowingStateLoss();
542 | }
543 |
544 | private void onDocumentFromBitmapTaskFinished(DocumentFromBitmapTaskResult result) {
545 | if (documentBitmap != null) documentBitmap.recycle();
546 | documentBitmap = result.bitmap;
547 | if (documentColoredBitmap != null) documentColoredBitmap.recycle();
548 | documentColoredBitmap = result.coloredBitmap;
549 | points = result.points;
550 |
551 | Bitmap tmp = documentColoredBitmap != null ? documentColoredBitmap : documentBitmap;
552 |
553 | Bitmap scaledBitmap = ImageResizer.scaleBitmap(tmp, viewHolder.sourceFrame.getWidth(), viewHolder.sourceFrame.getHeight());
554 | viewHolder.sourceImageView.setImageBitmap(scaledBitmap);
555 | viewHolder.sourceImageView.setVisibility(View.INVISIBLE);
556 | viewHolder.scaleImageView.setImageBitmap(scaledBitmap);
557 | viewHolder.scaleImageView.setVisibility(View.VISIBLE);
558 |
559 | viewHolder.polygonView.setVisibility(View.GONE);
560 | }
561 |
562 | private void onRotatingTaskFinished(RotatingTaskResult rotatingTaskResult) {
563 | takenPhotoBitmap = rotatingTaskResult.takenPhotoBitmap;
564 | documentBitmap = rotatingTaskResult.documentBitmap;
565 | documentColoredBitmap = rotatingTaskResult.documentColoredBitmap;
566 |
567 | Bitmap scaledBitmap = ImageResizer.scaleBitmap(documentColoredBitmap != null ? documentColoredBitmap : documentBitmap, viewHolder.sourceFrame.getWidth(), viewHolder.sourceFrame.getHeight());
568 | viewHolder.sourceImageView.setImageBitmap(scaledBitmap);
569 | viewHolder.sourceImageView.setVisibility(View.INVISIBLE);
570 | viewHolder.scaleImageView.setImageBitmap(scaledBitmap);
571 | viewHolder.scaleImageView.setVisibility(View.VISIBLE);
572 |
573 | points = null;
574 | }
575 |
576 |
577 | private void onModeChangingTaskFinished(ModeChangingTaskResult modeChangingTaskResult) {
578 | if (documentColoredBitmap != null) documentColoredBitmap.recycle();
579 | documentColoredBitmap = modeChangingTaskResult.bitmap;
580 | updateViewsWithNewBitmap();
581 | }
582 |
583 | private void onCropTaskFinished(CropTaskResult cropTaskResult) {
584 | points = cropTaskResult.points;
585 |
586 | Bitmap scaledBitmap = ImageResizer.scaleBitmap(takenPhotoBitmap, viewHolder.sourceFrame.getWidth(), viewHolder.sourceFrame.getHeight());
587 | viewHolder.sourceImageView.setImageBitmap(scaledBitmap);
588 | viewHolder.sourceImageView.setVisibility(View.VISIBLE);
589 | viewHolder.scaleImageView.setVisibility(View.GONE);
590 |
591 | Bitmap tempBitmap = ((BitmapDrawable) viewHolder.sourceImageView.getDrawable()).getBitmap();
592 | viewHolder.polygonView.setVisibility(View.VISIBLE);
593 |
594 | Map pointsToUse = null;
595 | if (points == null) {
596 | pointsToUse = getEdgePoints(tempBitmap);
597 | } else {
598 | pointsToUse = downScalePoints(points, takenPhotoBitmap, tempBitmap.getWidth(), tempBitmap.getHeight());
599 | }
600 |
601 | viewHolder.polygonView.setPoints(pointsToUse);
602 | int padding = (int) getResources().getDimension(R.dimen.scanPadding);
603 | FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(tempBitmap.getWidth() + 2 * padding, tempBitmap.getHeight() + 2 * padding);
604 | layoutParams.gravity = Gravity.CENTER;
605 | viewHolder.polygonView.setLayoutParams(layoutParams);
606 | }
607 | // ===========================================================
608 | // Inner and Anonymous Classes
609 | // ===========================================================
610 |
611 | private class DocumentFromBitmapTask extends AsyncTask {
612 |
613 | private final Bitmap bitmap;
614 | private final Map points;
615 | private int mode;
616 |
617 | public DocumentFromBitmapTask(Bitmap bitmap, Map points, int mode) {
618 | this.bitmap = bitmap;
619 | this.points = points;
620 | this.mode = mode;
621 | }
622 |
623 | @Override
624 | protected void onPreExecute() {
625 | showProgressDialog();
626 | }
627 |
628 | @Override
629 | protected DocumentFromBitmapTaskResult doInBackground(Void... params) {
630 | System.gc();
631 | DocumentFromBitmapTaskResult result = new DocumentFromBitmapTaskResult();
632 |
633 | if (points != null) {
634 | result.points = points;
635 | } else {
636 | try {
637 | Bitmap scaledBmp = ImageResizer.resizeImage(bitmap, 400, 400, false);
638 | result.points = getEdgePoints(scaledBmp);
639 | upScalePoints(result.points, bitmap, scaledBmp.getWidth(), scaledBmp.getHeight());
640 | scaledBmp.recycle();
641 | } catch (IOException e) {
642 | throw new RuntimeException("Not able to resize image", e);
643 | }
644 | }
645 |
646 | result.bitmap = cropDocumentFromBitmap(bitmap, result.points);
647 | try {
648 | result.bitmap = ImageResizer.resizeImage(result.bitmap, 2048, 2048, false);
649 | } catch (IOException e) {
650 | throw new RuntimeException("Not able to resize image", e);
651 | }
652 |
653 | if (mode == MODE_MAGIC) {
654 | result.coloredBitmap = ScanUtils.getMagicColorBitmap(result.bitmap);
655 | } else if (mode == MODE_BLACK_AND_WHITE) {
656 | result.coloredBitmap = ScanUtils.getGrayBitmap(result.bitmap);
657 | }
658 |
659 | return result;
660 | }
661 |
662 | @Override
663 | protected void onPostExecute(DocumentFromBitmapTaskResult documentFromBitmapTaskResult) {
664 | onDocumentFromBitmapTaskFinished(documentFromBitmapTaskResult);
665 | dismissDialog();
666 | }
667 | }
668 |
669 | private class RotatingTask extends AsyncTask {
670 |
671 | private final Bitmap takenPhotoBitmap;
672 | private final Bitmap documentBitmap;
673 | private final Bitmap documentColoredBitmap;
674 |
675 | public RotatingTask(Bitmap takenPhotoBitmap, Bitmap documentBitmap, Bitmap documentColoredBitmap) {
676 | this.takenPhotoBitmap = takenPhotoBitmap;
677 | this.documentBitmap = documentBitmap;
678 | this.documentColoredBitmap = documentColoredBitmap;
679 | }
680 |
681 | @Override
682 | protected RotatingTaskResult doInBackground(Void... params) {
683 | RotatingTaskResult result = new RotatingTaskResult();
684 |
685 | result.takenPhotoBitmap = Utils.rotateBitmap(takenPhotoBitmap, -90);
686 | takenPhotoBitmap.recycle();
687 |
688 | result.documentBitmap = Utils.rotateBitmap(documentBitmap, -90);
689 | documentBitmap.recycle();
690 |
691 | if (documentColoredBitmap != null) {
692 | result.documentColoredBitmap = Utils.rotateBitmap(documentColoredBitmap, -90);
693 | documentColoredBitmap.recycle();
694 | }
695 |
696 | return result;
697 | }
698 |
699 | @Override
700 | protected void onPostExecute(RotatingTaskResult rotatingTaskResult) {
701 | onRotatingTaskFinished(rotatingTaskResult);
702 | dismissDialog();
703 | }
704 | }
705 |
706 | private class ModeChangingTask extends AsyncTask {
707 |
708 | private final int mode;
709 | private final Bitmap bitmap;
710 |
711 | public ModeChangingTask(int mode, Bitmap bitmap) {
712 | this.mode = mode;
713 | this.bitmap = bitmap;
714 | }
715 |
716 | @Override
717 | protected ModeChangingTaskResult doInBackground(Void... params) {
718 | ModeChangingTaskResult result = new ModeChangingTaskResult();
719 | result.mode = mode;
720 |
721 | if (mode == MODE_MAGIC) {
722 | result.bitmap = ScanUtils.getMagicColorBitmap(bitmap);
723 | } else if (mode == MODE_BLACK_AND_WHITE) {
724 | result.bitmap = ScanUtils.getGrayBitmap(bitmap);
725 | }
726 |
727 | return result;
728 | }
729 |
730 | @Override
731 | protected void onPostExecute(ModeChangingTaskResult modeChangingTaskResult) {
732 | onModeChangingTaskFinished(modeChangingTaskResult);
733 | dismissDialog();
734 | }
735 | }
736 |
737 | private class CropTask extends AsyncTask {
738 |
739 | private final Bitmap bitmap;
740 |
741 | public CropTask(Bitmap bitmap) {
742 | this.bitmap = bitmap;
743 | }
744 |
745 | @Override
746 | protected CropTaskResult doInBackground(Void... params) {
747 | CropTaskResult result = new CropTaskResult();
748 | try {
749 | Bitmap scaledBmp = ImageResizer.resizeImage(bitmap, 400, 400, false);
750 | result.points = getEdgePoints(scaledBmp);
751 | upScalePoints(result.points, bitmap, scaledBmp.getWidth(), scaledBmp.getHeight());
752 | scaledBmp.recycle();
753 | } catch (IOException e) {
754 | throw new RuntimeException("Not able to resize image", e);
755 | }
756 | return result;
757 | }
758 |
759 | @Override
760 | protected void onPostExecute(CropTaskResult cropTaskResult) {
761 | onCropTaskFinished(cropTaskResult);
762 | dismissDialog();
763 | }
764 | }
765 |
766 | private static class CropTaskResult {
767 | Map points;
768 | }
769 |
770 | private static class ModeChangingTaskResult {
771 | private int mode;
772 | private Bitmap bitmap;
773 | }
774 |
775 | private static class RotatingTaskResult {
776 |
777 | private Bitmap takenPhotoBitmap;
778 | private Bitmap documentBitmap;
779 | private Bitmap documentColoredBitmap;
780 |
781 | }
782 |
783 | private static class DocumentFromBitmapTaskResult {
784 | private Bitmap bitmap;
785 | private Bitmap coloredBitmap;
786 | private Map points;
787 | }
788 |
789 | private static class ViewHolder {
790 | private ImageView sourceImageView;
791 | private ScaleImageView scaleImageView;
792 | private FrameLayout sourceFrame;
793 | private PolygonView polygonView;
794 |
795 | void prepare(View parent) {
796 | sourceImageView = (ImageView) parent.findViewById(R.id.sourceImageView);
797 | scaleImageView = (ScaleImageView) parent.findViewById(R.id.scaleImage);
798 | sourceFrame = (FrameLayout) parent.findViewById(R.id.sourceFrame);
799 | polygonView = (PolygonView) parent.findViewById(R.id.polygonView);
800 | }
801 | }
802 |
803 |
804 | }
805 |
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/java/com/scanlibrary/ScanUtils.java:
--------------------------------------------------------------------------------
1 | package com.scanlibrary;
2 |
3 |
4 | import android.graphics.Bitmap;
5 | import android.os.Environment;
6 |
7 | import org.opencv.core.Core;
8 | import org.opencv.core.CvException;
9 | import org.opencv.core.CvType;
10 | import org.opencv.core.Mat;
11 | import org.opencv.core.MatOfInt;
12 | import org.opencv.core.MatOfPoint;
13 | import org.opencv.core.MatOfPoint2f;
14 | import org.opencv.core.Point;
15 | import org.opencv.core.Scalar;
16 | import org.opencv.core.Size;
17 | import org.opencv.highgui.Highgui;
18 | import org.opencv.imgproc.Imgproc;
19 |
20 | import java.util.ArrayList;
21 | import java.util.List;
22 |
23 | public class ScanUtils {
24 |
25 | // ===========================================================
26 | // Constants
27 | // ===========================================================
28 |
29 | static {
30 | System.loadLibrary("opencv_java");
31 | }
32 |
33 | private static final String LOG_TAG = ScanUtils.class.getSimpleName();
34 | private static final boolean DEBUG = false;
35 |
36 | // ===========================================================
37 | // Fields
38 | // ===========================================================
39 |
40 | // ===========================================================
41 | // Constructors
42 | // ===========================================================
43 |
44 | // ===========================================================
45 | // Getters & Setters
46 | // ===========================================================
47 |
48 | public static MatOfPoint2f computePoint(int p1, int p2) {
49 |
50 | MatOfPoint2f pt = new MatOfPoint2f();
51 | pt.fromArray(new Point(p1, p2));
52 | return pt;
53 | }
54 |
55 | //
56 | public static List getPoints(Bitmap bitmap) {
57 | List squares = findSquares(bitmap);
58 | return findBiggestSquere(squares, bitmap);
59 | }
60 |
61 | public static List findBiggestSquere(List squares, Bitmap image) {
62 | double width = image.getWidth();
63 | double height = image.getHeight();
64 |
65 | double largest_area = -1;
66 | int largest_contour_index = 0;
67 | for (int i = 0; i < squares.size(); i++) {
68 | double a = Imgproc.contourArea(squares.get(i), false);
69 | if (a > largest_area) {
70 | largest_area = a;
71 | largest_contour_index = i;
72 | }
73 | }
74 |
75 | List points = new ArrayList<>();
76 | if (squares.size() > 0) {
77 | points = squares.get(largest_contour_index).toList();
78 | } else {
79 | points.add(new Point(0, 0));
80 | points.add(new Point(width, 0));
81 | points.add(new Point(0, height));
82 | points.add(new Point(width, height));
83 | }
84 | return points;
85 | }
86 |
87 | public static List findSquares(Bitmap bitmap) {
88 | Mat image = new Mat();
89 | Bitmap bmp32 = bitmap.copy(Bitmap.Config.ARGB_8888, true);
90 | org.opencv.android.Utils.bitmapToMat(bmp32, image);
91 |
92 | if (DEBUG) Highgui.imwrite(Environment.getExternalStorageDirectory().getAbsolutePath() + "/mat.png", image);
93 |
94 | Mat image_proc = image.clone();
95 | List squares = new ArrayList<>();
96 | // blur will enhance edge detection
97 | Mat blurred = image_proc.clone();
98 |
99 | if (DEBUG) Highgui.imwrite(Environment.getExternalStorageDirectory().getAbsolutePath() + "/blured.png", blurred);
100 |
101 | // Bluring Image
102 | Imgproc.medianBlur(image_proc, blurred, 9);
103 | Mat gray0 = new Mat(blurred.size(), CvType.CV_8U);
104 | Mat gray = new Mat();
105 |
106 | if (DEBUG) Highgui.imwrite(Environment.getExternalStorageDirectory().getAbsolutePath() + "/gray0-1.png", gray0);
107 | if (DEBUG) Highgui.imwrite(Environment.getExternalStorageDirectory().getAbsolutePath() + "/blured-0.png", blurred);
108 |
109 | List contours = new ArrayList<>();
110 |
111 | // find squares in every color plane of the image
112 | for (int c = 0; c < 3; c++) {
113 | int ch[] = {c, 0};
114 | List bluredList = new ArrayList<>(1);
115 | bluredList.add(blurred);
116 | List grayList = new ArrayList<>(1);
117 | grayList.add(gray0);
118 | Core.mixChannels(bluredList, grayList, new MatOfInt(ch));
119 | if (DEBUG) Highgui.imwrite(Environment.getExternalStorageDirectory().getAbsolutePath() + "/gray0-2-" + c + ".png", gray0);
120 |
121 | // try several threshold levels
122 | final int threshold_level = 2;
123 |
124 | for (int l = 0; l < threshold_level; l++) {
125 | // Use Canny instead of zero threshold level!
126 | // Canny helps to catch squares with gradient shading
127 | if (l == 0) {
128 | Imgproc.Canny(gray0, gray, 10, 30, 3, true);
129 | if (DEBUG) Highgui.imwrite(Environment.getExternalStorageDirectory().getAbsolutePath() + "/gray-1-" + c + ".png", gray);
130 | Imgproc.dilate(gray, gray, new Mat(), new Point(-1, -1), 1);
131 | if (DEBUG) Highgui.imwrite(Environment.getExternalStorageDirectory().getAbsolutePath() + "/gray-2-" + c + ".png", gray);
132 | } else {
133 | double val = (l + 1) * 255 / threshold_level;
134 | Core.compare(gray0, new Scalar(val), gray, Core.CMP_GE);
135 | }
136 |
137 | // Find contours and store them in a list
138 | Imgproc.findContours(gray, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
139 |
140 | if (DEBUG) Highgui.imwrite(Environment.getExternalStorageDirectory().getAbsolutePath() + "/gray-4-" + c + ".png", gray);
141 | if (DEBUG) Highgui.imwrite(Environment.getExternalStorageDirectory().getAbsolutePath() + "/gray0-3-" + c + ".png", gray0);
142 |
143 | // Test contours
144 | MatOfPoint2f approx = new MatOfPoint2f();
145 | for (int i = 0; i < contours.size(); i++) {
146 | // approximate contour with accuracy proportional
147 | // to the contour perimeter
148 | MatOfPoint2f curve = new MatOfPoint2f(contours.get(i).toArray());
149 | double epsilon = Imgproc.arcLength(curve, true) * 0.02;
150 | Imgproc.approxPolyDP(curve, approx, epsilon, true);
151 |
152 | // Note: absolute value of an area is used because
153 | // area may be positive or negative - in accordance with the
154 | // contour orientation
155 |
156 | double val1 = Math.abs(Imgproc.contourArea(approx));
157 | boolean val2 = Imgproc.isContourConvex(new MatOfPoint(approx.toArray()));
158 | if (approx.toArray().length == 4 && val1 > 1000 && val2) {
159 | double maxCosine = 0;
160 |
161 | for (int j = 2; j < 5; j++) {
162 | Point[] approxArray = approx.toArray();
163 | double cosine = Math.abs(angle(approxArray[j % 4], approxArray[j - 2], approxArray[j - 1]));
164 | maxCosine = Math.max(maxCosine, cosine);
165 | }
166 |
167 | if (maxCosine < 0.3) {
168 | squares.add(new MatOfPoint(approx.toArray()));
169 | }
170 | }
171 | }
172 | }
173 | }
174 | return squares;
175 | }
176 |
177 | public static Bitmap getScannedBitmap(Bitmap bitmap, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
178 | Mat mbgra = bitmapToMat(bitmap);
179 | // init our output image
180 | Mat dst = scan(mbgra, x1, y1, x2, y2, x3, y3, x4, y4);
181 | Bitmap result = matToBitmap(dst);
182 | return result;
183 | }
184 |
185 | public static double angle(Point pt1, Point pt2, Point pt0) {
186 | double dx1 = pt1.x - pt0.x;
187 | double dy1 = pt1.y - pt0.y;
188 | double dx2 = pt2.x - pt0.x;
189 | double dy2 = pt2.y - pt0.y;
190 |
191 | return (dx1 * dx2 + dy1 * dy2) / Math.sqrt((dx1 * dx1 + dy1 * dy1) * (dx2 * dx2 + dy2 * dy2) + 1e-10);
192 | }
193 |
194 | private static Mat scan(Mat img, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
195 |
196 | // define the destination image size:
197 |
198 | float w1 = (float) Math.sqrt(Math.pow(x4 - x3, 2) + Math.pow(x4 - x3, 2));
199 | float w2 = (float) Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(x2 - x1, 2));
200 | float h1 = (float) Math.sqrt(Math.pow(y2 - y4, 2) + Math.pow(y2 - y4, 2));
201 | float h2 = (float) Math.sqrt(Math.pow(y1 - y3, 2) + Math.pow(y1 - y3, 2));
202 |
203 | float maxWidth = (w1 < w2) ? w1 : w2;
204 | float maxHeight = (h1 < h2) ? h1 : h2;
205 |
206 | Mat dst = Mat.zeros(new Size(maxWidth, maxHeight), CvType.CV_8UC3);
207 |
208 | // corners of destination image with the sequence [tl, tr, bl, br]
209 | List dst_pts = new ArrayList<>();
210 | List img_pts = new ArrayList<>();
211 | dst_pts.add(new Point(0, 0));
212 | dst_pts.add(new Point(maxWidth - 1, 0));
213 | dst_pts.add(new Point(0, maxHeight - 1));
214 | dst_pts.add(new Point(maxWidth - 1, maxHeight - 1));
215 |
216 | img_pts.add(new Point(x1, y1));
217 | img_pts.add(new Point(x2, y2));
218 | img_pts.add(new Point(x3, y3));
219 | img_pts.add(new Point(x4, y4));
220 |
221 | // get transformation matrix
222 | MatOfPoint2f source = new MatOfPoint2f(img_pts.get(0), img_pts.get(1), img_pts.get(2), img_pts.get(3));
223 | MatOfPoint2f dest = new MatOfPoint2f(dst_pts.get(0), dst_pts.get(1), dst_pts.get(2), dst_pts.get(3));
224 | Mat transmtx = Imgproc.getPerspectiveTransform(source, dest);
225 | // apply perspective transformation
226 | Imgproc.warpPerspective(img, dst, transmtx, dst.size());
227 | // warpPerspective(img, dst, transmtx, dst.size());
228 |
229 | return dst;
230 | }
231 |
232 | private static Mat bitmapToMat(Bitmap bitmap) {
233 | System.gc();
234 | Mat image = new Mat();
235 | Bitmap bmp32 = bitmap.copy(Bitmap.Config.ARGB_8888, true);
236 | org.opencv.android.Utils.bitmapToMat(bmp32, image);
237 | return image;
238 | }
239 |
240 | private static Bitmap matToBitmap(Mat mat) {
241 | try {
242 | Bitmap bmp = Bitmap.createBitmap(mat.width(), mat.height(), Bitmap.Config.ARGB_8888);
243 | org.opencv.android.Utils.matToBitmap(mat, bmp);
244 | return bmp;
245 | } catch (CvException e) {
246 | throw new RuntimeException("Not able to convert mat to bitmap", e);
247 | }
248 | }
249 |
250 | public static Bitmap getGrayBitmap(Bitmap bitmap) {
251 | Mat mbgra = bitmapToMat(bitmap);
252 | Mat dst = mbgra.clone();
253 |
254 | Imgproc.cvtColor(mbgra, dst, Imgproc.COLOR_RGB2GRAY);
255 |
256 | return matToBitmap(dst);
257 | }
258 |
259 | public static Bitmap getMagicColorBitmap(Bitmap bitmap) {
260 | Mat mbgra = bitmapToMat(bitmap);
261 | Mat dst = mbgra.clone();
262 | // init our output image
263 | float alpha = 1.9f;
264 | float beta = -80;
265 | dst.convertTo(dst, -1, alpha, beta);
266 |
267 | return matToBitmap(dst);
268 | }
269 |
270 | // ===========================================================
271 | // Methods for/from SuperClass/Interfaces
272 | // ===========================================================
273 |
274 | // ===========================================================
275 | // Methods
276 | // ===========================================================
277 |
278 | // ===========================================================
279 | // Inner and Anonymous Classes
280 | // ===========================================================
281 |
282 | }
283 |
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/java/com/scanlibrary/Utils.java:
--------------------------------------------------------------------------------
1 | package com.scanlibrary;
2 |
3 | import android.app.Activity;
4 | import android.content.pm.ActivityInfo;
5 | import android.graphics.Bitmap;
6 | import android.graphics.BitmapFactory;
7 | import android.graphics.Matrix;
8 | import android.util.DisplayMetrics;
9 | import android.util.Log;
10 | import android.view.Surface;
11 |
12 | import java.io.File;
13 | import java.io.FileInputStream;
14 | import java.io.FileOutputStream;
15 | import java.io.IOException;
16 | import java.io.InputStream;
17 | import java.io.OutputStream;
18 |
19 | public class Utils {
20 |
21 | private static final String TAG = Utils.class.getSimpleName();
22 |
23 | private Utils() {
24 |
25 | }
26 |
27 | public static Bitmap getBitmapFromLocation(String absLocation) {
28 | BitmapFactory.Options options = new BitmapFactory.Options();
29 | options.inPreferredConfig = Bitmap.Config.ARGB_8888;
30 | return BitmapFactory.decodeFile(absLocation, options);
31 | }
32 |
33 |
34 | public static int getScreenOrientation(Activity activity) {
35 | int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
36 | DisplayMetrics dm = new DisplayMetrics();
37 | activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
38 | int width = dm.widthPixels;
39 | int height = dm.heightPixels;
40 | int orientation;
41 | // if the device's natural orientation is portrait:
42 | if ((rotation == Surface.ROTATION_0
43 | || rotation == Surface.ROTATION_180) && height > width ||
44 | (rotation == Surface.ROTATION_90
45 | || rotation == Surface.ROTATION_270) && width > height) {
46 | switch (rotation) {
47 | case Surface.ROTATION_0:
48 | orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
49 | break;
50 | case Surface.ROTATION_90:
51 | orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
52 | break;
53 | case Surface.ROTATION_180:
54 | orientation =
55 | ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
56 | break;
57 | case Surface.ROTATION_270:
58 | orientation =
59 | ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
60 | break;
61 | default:
62 | Log.e(TAG, "Unknown screen orientation. Defaulting to " +
63 | "portrait.");
64 | orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
65 | break;
66 | }
67 | }
68 | // if the device's natural orientation is landscape or if the device
69 | // is square:
70 | else {
71 | switch (rotation) {
72 | case Surface.ROTATION_0:
73 | orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
74 | break;
75 | case Surface.ROTATION_90:
76 | orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
77 | break;
78 | case Surface.ROTATION_180:
79 | orientation =
80 | ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
81 | break;
82 | case Surface.ROTATION_270:
83 | orientation =
84 | ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
85 | break;
86 | default:
87 | Log.e(TAG, "Unknown screen orientation. Defaulting to " +
88 | "landscape.");
89 | orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
90 | break;
91 | }
92 | }
93 |
94 | return orientation;
95 | }
96 |
97 | public static Bitmap rotateBitmap(Bitmap source, float angle) {
98 | System.gc();
99 | Matrix matrix = new Matrix();
100 | matrix.postRotate(angle);
101 | return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);
102 | }
103 |
104 | public static boolean isStringEmpty(String value) {
105 | return value == null || value.isEmpty();
106 | }
107 |
108 | public static String cropPathFromFullPath(String path) {
109 | return path.substring(0, path.lastIndexOf(File.separator));
110 | }
111 |
112 | public static void copyFileUsingStream(File source, File dest) throws IOException {
113 | InputStream is = null;
114 | OutputStream os = null;
115 | try {
116 | is = new FileInputStream(source);
117 | os = new FileOutputStream(dest);
118 | byte[] buffer = new byte[1024];
119 | int length;
120 | while ((length = is.read(buffer)) > 0) {
121 | os.write(buffer, 0, length);
122 | }
123 | } finally {
124 | is.close();
125 | os.close();
126 | }
127 | }
128 | }
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/libs/armeabi-v7a/libopencv_java.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/libs/armeabi-v7a/libopencv_java.so
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable-hdpi/ic_color_lens_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/res/drawable-hdpi/ic_color_lens_white_24dp.png
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable-hdpi/ic_crop_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/res/drawable-hdpi/ic_crop_white_24dp.png
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable-hdpi/ic_done_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/res/drawable-hdpi/ic_done_white_24dp.png
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable-hdpi/ic_rotate_90_degrees_ccw_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/res/drawable-hdpi/ic_rotate_90_degrees_ccw_white_24dp.png
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable-mdpi/ic_color_lens_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/res/drawable-mdpi/ic_color_lens_white_24dp.png
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable-mdpi/ic_crop_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/res/drawable-mdpi/ic_crop_white_24dp.png
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable-mdpi/ic_done_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/res/drawable-mdpi/ic_done_white_24dp.png
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable-mdpi/ic_rotate_90_degrees_ccw_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/res/drawable-mdpi/ic_rotate_90_degrees_ccw_white_24dp.png
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable-xhdpi/ic_color_lens_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/res/drawable-xhdpi/ic_color_lens_white_24dp.png
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable-xhdpi/ic_crop_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/res/drawable-xhdpi/ic_crop_white_24dp.png
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable-xhdpi/ic_done_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/res/drawable-xhdpi/ic_done_white_24dp.png
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable-xhdpi/ic_rotate_90_degrees_ccw_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/res/drawable-xhdpi/ic_rotate_90_degrees_ccw_white_24dp.png
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable-xxhdpi/ic_color_lens_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/res/drawable-xxhdpi/ic_color_lens_white_24dp.png
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable-xxhdpi/ic_crop_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/res/drawable-xxhdpi/ic_crop_white_24dp.png
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable-xxhdpi/ic_done_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/res/drawable-xxhdpi/ic_done_white_24dp.png
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable-xxhdpi/ic_rotate_90_degrees_ccw_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/res/drawable-xxhdpi/ic_rotate_90_degrees_ccw_white_24dp.png
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable-xxxhdpi/ic_color_lens_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/res/drawable-xxxhdpi/ic_color_lens_white_24dp.png
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable-xxxhdpi/ic_crop_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/res/drawable-xxxhdpi/ic_crop_white_24dp.png
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable-xxxhdpi/ic_done_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/res/drawable-xxxhdpi/ic_done_white_24dp.png
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable-xxxhdpi/ic_rotate_90_degrees_ccw_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/scanlibrary/src/main/res/drawable-xxxhdpi/ic_rotate_90_degrees_ccw_white_24dp.png
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/drawable/circle.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
14 |
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/layout/activity_scan.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/layout/fragment_scan.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
20 |
21 |
27 |
28 |
29 |
30 |
36 |
37 |
38 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/menu/scan_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/values-lt/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Baigti
4 | Transformuoti
5 | Pasukti
6 | Negalima iškirpti dokumento, pakeiskite taškus
7 | Transformuojama...
8 |
9 | Joks
10 | Juodai baltas
11 | Magiškas
12 |
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/values-ru/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Завершить
4 | Обрезать
5 | Повернуть
6 | Не можем обрезать изображение, меняйте пункты
7 | Трансформируется...
8 |
9 | Никакой
10 | Черно-белый
11 | Волшебный
12 |
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #ff59a9ff
5 | #ffff900d
6 | #ffc4c2bc
7 |
8 | #ff59a9ff
9 | #90e5f8ff
10 |
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 | 16dp
7 | 32dp
8 | 2dp
9 |
10 |
11 | 15dp
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/ScanDemoExample/scanlibrary/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ScanLibrary
5 | Finish
6 | Crop
7 | Rotate
8 | Can\'t crop the image, change the points
9 | Transforming...
10 |
11 | None
12 | Black & White
13 | Magic
14 |
15 |
--------------------------------------------------------------------------------
/ScanDemoExample/screenshots/a.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/screenshots/a.png
--------------------------------------------------------------------------------
/ScanDemoExample/screenshots/b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/screenshots/b.png
--------------------------------------------------------------------------------
/ScanDemoExample/screenshots/c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/screenshots/c.png
--------------------------------------------------------------------------------
/ScanDemoExample/screenshots/d.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/screenshots/d.png
--------------------------------------------------------------------------------
/ScanDemoExample/screenshots/e.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/screenshots/e.png
--------------------------------------------------------------------------------
/ScanDemoExample/screenshots/f.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/screenshots/f.png
--------------------------------------------------------------------------------
/ScanDemoExample/screenshots/g.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liltof/android-scanner/898dc1335a577533b0c9ef32126fda32a761f5fc/ScanDemoExample/screenshots/g.png
--------------------------------------------------------------------------------
/ScanDemoExample/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':scanlibrary'
2 |
--------------------------------------------------------------------------------