Compare commits

...

219 Commits

Author SHA1 Message Date
Ralf Kistner f54615e43a Update metadata and scripts for Maven Central. 2021-03-15 11:18:34 +02:00
Ralf Kistner 1acc3a6f4f Merge pull request #605 from devzeze/devzeze-fix556
fix #556 add licenses to POM
2021-02-12 10:32:22 +02:00
Ralf Kistner 73093b9b4f Merge pull request #587 from ANewGalaxy/master
Fixing MediaPlayer warnings;
2021-02-12 10:30:32 +02:00
Ralf Kistner db85816b3d Merge pull request #609 from nodh/feature/preventclassclash
Move CameraConfigurationUtils to prevent clash with other ZXing libraries
2021-02-12 10:29:37 +02:00
Christian Kollmann 6a3d857019 Move CameraConfigurationUtils to prevent clash with com.google.zxing:android-core 2021-02-12 07:26:12 +01:00
devzeze 6c8ea7e3fd fix #556 2021-02-03 16:07:17 +01:00
ANewGalaxy 35ee545f8b Replaced setAudioStreamType method call with setAudioAttributes call to fix warnings about stream types. Also removed unused import. 2020-12-11 22:00:01 -05:00
ANewGalaxy 5d9e2c3756 Added reset calls before each release call 2020-12-11 21:57:36 -05:00
ANewGalaxy cce2a300ef Fixing MediaPlayer warnings; 2020-12-11 21:48:29 -05:00
Ralf Kistner 13440ad875 Update readme. 2020-02-11 09:55:51 +02:00
Ralf Kistner 5a2470f034 Merge branch 'master' of github.com:journeyapps/zxing-android-embedded 2020-02-11 09:52:51 +02:00
Ralf Kistner 545672bb8f Update README.md
Fix the version.
2020-01-31 17:24:51 +02:00
Ralf Kistner 542dc66090 Merge pull request #530 from lukassos/master
correct release version 4.0.2
2020-01-17 14:44:06 +02:00
Lukassos ea414a236e correct release version 4.0.2
After two days of headaches, looked into CHANGES.md where 4.0.0 is marked as broken - we should use 4.0.2
2020-01-17 12:14:32 +01:00
Ralf Kistner 1ba1ded540 Merge pull request #529 from hannesa2/ReduceTravisBuilds
Reduce Travis builds
2020-01-08 17:05:52 +02:00
Ralf Kistner 4893bcc194 Merge pull request #528 from hannesa2/AndroidStudio-3.5.3
Android Studio 3.5.3
2020-01-08 10:13:54 +02:00
Hannes Achleitner 6498fad6a7 Reduce Travis builds 2020-01-07 19:52:44 +01:00
Hannes Achleitner f7f779d7ac Android Studio 3.5.3 2020-01-07 19:49:36 +01:00
Ralf Kistner c555ffbe05 Release 4.1.0 2020-01-07 14:24:05 +02:00
Andre Ippisch e0d340952d Camera permission behaviour (#505)
* Remove unused else

* Remove SDK version check since we are on minimum 24 already

* Add intent key about missing camera permission

* Add boolean value to show error dialog

- always true for the beginning to keep current behaviour

* Make displaying an error dialog for missing camera permissions optional

* Add intent extras for Intent initialisation

* Initialise camera permission dialog visibility from Intent

* Add original intent to IntentResult

* Use original intent in IntentResult

* Make onRequestPermissionResult public again

* Forward onRequestPermissionResult

* React on Intent information

- for example on the missing camera permission

* Revert "Remove SDK version check since we are on minimum 24 already"

This reverts commit 8f79f56a

* Add missing imports
2020-01-07 14:00:51 +02:00
Ralf Kistner 9b924e6875 Merge pull request #511 from hannesa2/AndroidStudio-3.5.2
Android Studio 3.5.2
2020-01-07 13:55:48 +02:00
Ralf Kistner 8df0cb554a Merge pull request #527 from yands11/master
Apply space convention after 'if'
2020-01-07 13:54:55 +02:00
Ralf Kistner 09d992ddfa Merge pull request #519 from bekabot/master
Remove unneeded type casts & fix comment typo in DecoratedBarcodeView.java
2020-01-07 13:54:27 +02:00
Ralf Kistner 95cd4a3a4d Merge pull request #512 from hannesa2/SomeCodeCleanup
some code cleanup
2020-01-07 13:54:02 +02:00
youngseok 7e5bbd2997 Apply space convention after 'if' 2020-01-07 14:37:28 +09:00
bekabot 8d8cd7d445 remove unneeded type casts 2019-12-10 13:40:43 +06:00
bekabot bd857c68eb fix comment 2019-12-10 13:35:13 +06:00
Hannes Achleitner 0fdfbce9fb some code cleanup 2019-11-17 13:49:59 +01:00
Hannes Achleitner eba457ee9e Android Studio 3.5.2 2019-11-17 13:42:27 +01:00
Ralf Kistner 9319110de8 Merge pull request #504 from anipp100/default-method
Use default annotation for optional interface method
2019-10-24 09:33:31 +02:00
Ralf Kistner ee4b181f23 Merge pull request #506 from anipp100/lambda-runnable
Use lambda for standalone Runnables
2019-10-24 09:33:12 +02:00
Ralf Kistner a4f751500c Fix instructions for min-sdk 14.
Fixes #507.
2019-10-22 10:05:01 +02:00
Andre Ippisch 75b4aa57f5 Use lambda for standalone Runnables 2019-10-18 14:39:58 +02:00
Andre Ippisch eb4cbd5095 Use default annotation for optional interface method 2019-10-18 12:48:47 +02:00
Andre Ippisch aa4fca5dae Let visibility of "laser scanner" be set (#503)
* Add laser visibility attribute

* Add laser visibility attribute usage to sample project

- set to true to stick with the current behavior
- included for didactic reasons

* Add laser visibility attribute to ViewfinderView and set it accordingly

* Draw "laser scanner" only if wanted

* Let laser visibility be changed programmatically

* Add sample code to show how laser visibility can be changed programmatically
2019-10-17 14:21:23 +02:00
Ralf Kistner d22b581cd3 Merge pull request #495 from journeyapps/release-401
Release v4.0.2
2019-09-07 19:04:30 +02:00
Ralf Kistner 9349b31ab3 v4.0.2. 2019-09-07 19:01:10 +02:00
Ralf Kistner c0c8363f85 More publishing fixes. 2019-09-07 19:00:32 +02:00
Ralf Kistner 27c22e8ffe v4.0.1 2019-09-07 18:53:01 +02:00
Ralf Kistner d77fa29226 Fix bintray publishing issues. 2019-09-07 18:51:44 +02:00
Ralf Kistner 9aa5381c2b Merge pull request #494 from journeyapps/upgrade-zxing
v4.0.0
2019-09-07 18:17:40 +02:00
Ralf Kistner 3a7612661a v4.0.0.; update docs. 2019-09-07 18:03:30 +02:00
Ralf Kistner 70f4bbb8de Merge pull request #493 from journeyapps/preview-sizing
Fix preview sizing & fix ResultPoints for front camera
2019-09-07 17:57:56 +02:00
Ralf Kistner 363ad77540 Upgrade zxing; min sdk of 24. 2019-09-07 17:52:00 +02:00
Ralf Kistner 62547c10d7 Internal support for downscaling before decoding.
Fix ResultPoint transformation for mirrored camera.
2019-09-07 17:43:08 +02:00
Ralf Kistner 0de49aa7c8 Check scaled preview size even earlier. 2019-09-07 15:49:52 +02:00
Ralf Kistner 1a1c8d0e66 Merge pull request #463 from lterminiello/fixArithmeticException
fix ArithmeticException in CameraPreview
2019-09-07 15:45:25 +02:00
Ralf Kistner 31332404fc Merge pull request #492 from journeyapps/upgrade
Upgrade tooling
2019-09-07 15:35:48 +02:00
Ralf Kistner 28c646b9e2 Merge pull request #469 from adrianoluis/more-extras-array
Adds support to send Arrays as more extras.
2019-09-07 15:35:33 +02:00
Ralf Kistner c0c0fcd745 More tooling upgrades. 2019-09-07 15:28:29 +02:00
Ralf Kistner 98d00abcbd Remove sample-nosupport. 2019-09-07 15:28:18 +02:00
Ralf Kistner 7042d74f4a Merge pull request #438 from journeyapps/androidx
Switch to androidx
2019-09-07 15:12:11 +02:00
Adriano Rocha 94ae63d857 Adds support to send Arrays as more extras. 2019-04-01 22:18:04 -07:00
Lucas Marcelo Terminiello 0e7413e1f7 fix ArithmeticException in CameraPreview 2019-02-27 14:37:57 -03:00
Ralf Kistner 82e8f9afcc Switch to androidx. 2018-11-02 20:21:21 +02:00
Ralf Kistner 6556f81558 Merge branch 'akallabeth-about_library_support' 2018-11-02 20:17:58 +02:00
Ralf Kistner 5983e2fb8e Add AboutLibaries to the sample app. 2018-11-02 20:16:13 +02:00
Ralf Kistner be04a7587f Merge branch 'about_library_support' of https://github.com/akallabeth/zxing-android-embedded into akallabeth-about_library_support 2018-11-02 19:49:31 +02:00
Ralf Kistner 330cb2b63b Merge branch 'master' of github.com:journeyapps/zxing-android-embedded 2018-11-02 19:47:32 +02:00
Ralf Kistner 6b49e8774f Merge branch 'amadeu01-change-mask-color-programmatically' 2018-11-02 19:46:50 +02:00
Ralf Kistner 209e1c9a04 Remove redundant call; set random color on every launch in sample. 2018-11-02 19:46:24 +02:00
Ralf Kistner c7a143499e Merge branch 'change-mask-color-programmatically' of https://github.com/amadeu01/zxing-android-embedded into amadeu01-change-mask-color-programmatically 2018-11-02 19:42:19 +02:00
Ralf Kistner a4dfc2fca4 Merge pull request #406 from reusch/feature/torch-at-startup
Torch at camera startup
2018-11-02 19:39:56 +02:00
Ralf Kistner 98b69af339 Merge pull request #412 from greatjack1/master
changed compile to implementation in README.md
2018-11-02 19:39:03 +02:00
Ralf Kistner 40010a62df Merge pull request #383 from CaramelHeaven/feature/update_sample
Updated sample
2018-11-02 19:38:39 +02:00
Ralf Kistner 5927f38a1d Merge branch 'MicroSpecWes-master' 2018-11-02 19:37:24 +02:00
Ralf Kistner f2a477b647 More updates and fixes. 2018-11-02 19:37:12 +02:00
Amadeu Cavalcante 65aaa93a27 Added support for programmatically change ViewfinderView mask color 2018-10-15 21:24:58 -03:00
Armin Novak 1e1b075f13 Added resource strings for AboutLibraries 2018-08-13 13:57:06 +02:00
greatjack1 224bfd6ea2 changed compile to implementation in README.md 2018-07-16 14:58:28 -04:00
Thomas Reusch c1a3786c17 torch at camera startup
add option to enable the torch at camera startup when using IntentIntegrator
2018-06-17 21:56:24 +02:00
Wesley Crick 2af07f95cf Gradle Update and SDK 27
Updated android support dependencies to 27.1.1.

Update deprecated "compile" to "implementation".
2018-05-30 16:35:42 -04:00
CaramelHeaven 4e7ee70b73 Updated sample 2018-03-30 23:11:59 +04:00
Ralf Kistner aec45c4f10 Add note on hardware acceleration.
Fixes #373.
2018-03-06 15:05:41 +02:00
Ralf Kistner c1918b32fb Fix and clarify Android SDK version notes. 2018-03-05 19:15:42 +02:00
Ralf Kistner a2774c7bde Use newer bintray plugin. 2018-03-04 14:20:58 +02:00
Ralf Kistner 2f061a1da5 zxing:core 3.3.2; notes on downgrading; bump to 3.6.0. 2018-03-04 14:10:44 +02:00
Ralf Kistner 70f4159e3a Include more licenses for Travis. 2018-03-04 13:55:54 +02:00
Ralf Kistner 295193d613 Add notes on threading. 2018-03-04 13:40:32 +02:00
Ralf Kistner d8f3157e97 Make it easier to specify barcode formats. 2018-03-04 13:34:54 +02:00
Ralf Kistner a7d91dbed6 Cleanup. 2018-03-04 13:19:06 +02:00
Ralf Kistner e8257760da Merge pull request #327 from alpbak/master
Added a mixed scan mode to scan inverted and normal barcodes at the same time.
2018-03-04 13:14:00 +02:00
Ralf Kistner 2f022c0a70 Make requestCode non-static. 2018-03-04 13:05:59 +02:00
Ralf Kistner 3fd9354cd7 Merge branch 'master' of github.com:journeyapps/zxing-android-embedded 2018-03-04 12:53:53 +02:00
Ralf Kistner c98a04a407 Merge pull request #287 from tingbob/master
Redefine the default activity request code for using switch statement.
2018-03-04 12:53:43 +02:00
Ralf Kistner 8e90d15117 Merge pull request #328 from bossbossk20/master
add generate barcode example
2018-03-04 12:52:07 +02:00
Ralf Kistner fa2cd1562d Merge pull request #324 from HomerSp/master
Request the camera preview on the main ui thread
2018-03-04 12:50:50 +02:00
Ralf Kistner 11073ddcf9 Simplify code with new minimum SDK version. 2018-03-04 12:47:53 +02:00
Ralf Kistner 17ad61674e Update build tools. 2018-03-04 12:37:20 +02:00
Ralf Kistner 8a67d852e4 Fix some lint issues. 2018-03-04 12:29:04 +02:00
Ralf Kistner 68f17bde30 Merge pull request #313 from 84d010m08/Added_camera_setting_change
Added camera setting change.
2018-03-04 11:42:22 +02:00
Ralf Kistner 34788e0fbc Merge pull request #360 from matpag/update_zxing_core
Update zxing core to 3.3.1
2018-03-04 11:39:57 +02:00
Mattia Pagini 45756b51b2 Update zxing core to 3.3.1 2018-01-05 10:12:16 +01:00
thanawat adee48a8ae add generate barcode example 2017-09-16 01:59:50 +07:00
alpbak e48229ac3f Revert "Added a mixed scan mode to enable scanning inverted and normal barcodes"
This reverts commit cf4c0dce50.

# Conflicts:
#	sample/src/main/java/example/zxing/MainActivity.java
#	zxing-android-embedded/src/com/google/zxing/client/android/Intents.java
#	zxing-android-embedded/src/com/journeyapps/barcodescanner/DecoratedBarcodeView.java
#	zxing-android-embedded/src/com/journeyapps/barcodescanner/DefaultDecoderFactory.java
2017-09-15 10:03:10 +03:00
alpbak 2ff44f91b5 Revert "Added a mixed scan mode to enable scanning inverted and normal barcodes"
This reverts commit cf4c0dce50.
2017-09-15 09:45:19 +03:00
alpbak f30ea0c955 Added a mixed scan mode to enable scanning inverted and normal barcodes 2017-09-15 09:34:42 +03:00
alpbak cf4c0dce50 Added a mixed scan mode to enable scanning inverted and normal barcodes 2017-09-14 20:12:06 +03:00
HomerSp 670000698d Request the camera preview on the main thread, in case the camera instance is being closed at the same time. 2017-09-07 15:10:08 +02:00
橋度朋弥 60ed28ee06 Add to Javadoc. 2017-07-30 13:49:28 +09:00
橋度朋弥 c83b2c5a5c Added camera setting change. 2017-07-24 02:08:26 +09:00
Ralf Kistner eaeddd23af Merge pull request #290 from Palatis/alloc_in_ondraw_fix
Alloc in ondraw fix
2017-06-23 15:18:38 +02:00
Victor Tseng 0fd028b9e2 just don't add the result points when exceeding maximum
just don't add at the first place, instead of add and then trim.
2017-04-10 15:19:17 +08:00
Victor Tseng e2892cad8f don't allocate in ViewfinderView.onDraw()
don't alloc in onDraw() as indicated by lint, avoids GC.
we now draw lastPossibleResultPoints before possibleResultPoints,
however it doesn't really matter which one is drawn first, since current
ones are going to cover last ones anyway.
2017-04-10 15:17:38 +08:00
nlv 1817bb198b Redefine the default activity request code for using switch statement.
Add a construct function to set customized activity request code.
2017-04-01 16:57:11 +08:00
Ralf Kistner 29848b2b4f v3.5.0 2017-03-20 15:04:25 +02:00
Ralf Kistner 9ec92a5101 Add explicit check for null preview data.
Fixes #268.
2017-03-20 13:40:46 +02:00
Ralf Kistner 2cf4b60f0f Handle getParameters() failure in onPreviewFrame(). 2017-03-20 13:18:24 +02:00
Ralf Kistner e3aed41746 Add error handling for CameraManager#setTorch. 2017-03-20 13:12:31 +02:00
Ralf Kistner 09571d3813 Add tabbed scanner example. 2017-03-20 13:07:55 +02:00
Ralf Kistner 6c250faeb6 Close camera before finishing Activity. 2017-03-20 13:05:36 +02:00
Ralf Kistner 4955c18a87 Fix leak with scan timeout.
Fixes #283.
2017-03-20 10:32:08 +02:00
Ralf Kistner 6f73f470c9 Squashed commit of the following:
commit 01acef10132802b22e104c411122c2790673afdc
Author: Ralf Kistner <ralf@journeyapps.com>
Date:   Sun Mar 12 13:08:09 2017 +0200

    Try again.

commit 4c0605282d13df59c856fd64945d096d5789f227
Author: Ralf Kistner <ralf@journeyapps.com>
Date:   Sun Mar 12 13:01:58 2017 +0200

    Show lint output on build failure.

commit e9af0670c8b2e07a4bd2418042b0487e97fb9389
Author: Ralf Kistner <ralf@journeyapps.com>
Date:   Sun Mar 12 12:50:11 2017 +0200

    Handle missing local.properties file.

commit e3976102e7342d3008c7b88cb3f75397397e3d95
Author: Ralf Kistner <ralf@journeyapps.com>
Date:   Sun Mar 12 12:36:47 2017 +0200

    Update SDK components installed on Travis.

commit e50952cc89cc83720e146838debf6a22bb1d9c2a
Author: Ralf Kistner <ralf@journeyapps.com>
Date:   Sun Mar 12 12:28:18 2017 +0200

    More tool upgrades.

     * Gradle 3.4.1
     * Android Gra dle plugin 2.3.0
     * Remove android-sdk-manager (no longer required).
     * Ignore lint for translation issues in sample apps.
2017-03-12 13:14:17 +02:00
Ralf Kistner 5d48d2ff35 Merge branch 'PaulWoitaschek-master' 2017-03-12 11:58:17 +02:00
Paul Woitaschek 92f6acf3d6 Suppress cast warning and removed unused parameters 2017-01-14 09:57:46 +01:00
Paul Woitaschek 5309388c7a Build with jdk8 to resolve major-minor-version build issue 2017-01-14 09:48:49 +01:00
Paul Woitaschek b3a2c1e07e Dependencies updated. Most noteworthy: zxing:core 3.3.0 2017-01-14 08:29:21 +01:00
Ralf Kistner 958bbb1491 Add copyright notice to readme
Fixes #226.
2017-01-03 11:50:45 +02:00
Ralf Kistner 34aca80673 Merge pull request #244 from rocboronat/patch-1
Fix a typo in the Catalan translation
2016-11-28 10:26:11 +02:00
Roc Boronat 0b774fba7c Fix a typo in the Catalan translation 2016-11-26 18:08:44 +01:00
Ralf Kistner 81effea784 Merge pull request #235 from Mikejleighton/master
Added support for an inverted scan
2016-11-04 09:16:32 +02:00
Ralf Kistner b4a88bfb56 Merge pull request #234 from renatosc/master
allowing change of REQUEST_CODE value
2016-11-04 09:14:57 +02:00
Leighton, Michael 6c3e917b15 Adding an InvertedDecoder 2016-11-02 22:39:43 -04:00
Leighton, Michael f484d21af8 Added support for inverted scans. 2016-11-02 22:34:20 -04:00
Dev Team d3330c9663 allowing change of REQUEST_CODE value 2016-11-02 01:51:56 -07:00
Ralf Kistner 964dbab7b2 Bump sample app to 3.4.0. 2016-10-16 21:03:28 +02:00
Ralf Kistner 76861f8683 v3.4.0 2016-10-16 20:51:48 +02:00
Ralf Kistner 42d09b810b Merge branch 'handle-resolution-mismatch' 2016-10-16 20:36:14 +02:00
Ralf Kistner c4cbaba495 Handle case where resolution does not match the preview data. 2016-10-16 20:35:56 +02:00
Ralf Kistner 2373c88ca9 Merge pull request #181 from campelo/patch-1
lock orientation doesn't work
2016-10-16 19:55:58 +02:00
Ralf Kistner 4f311133ee Merge branch 'roxma-master' 2016-10-16 19:53:25 +02:00
Ralf Kistner 531918d28a Remove 150ms delay for beeps. 2016-10-16 19:52:39 +02:00
Ralf Kistner e3d3950d9a Simplify BeepManager. Beep based on media volume only. 2016-10-16 19:47:54 +02:00
roxma 1b8fbe60cc Create new MediaPlayer everytime when need a beep. For fixing issue #155 2016-10-08 21:43:38 +08:00
Ralf Kistner a2cc8e2756 Merge pull request #204 from jstemberger/Issue202
Added a check to see if the textureview is available and if it is the…
2016-10-05 09:35:31 +02:00
Ralf Kistner a7c8046c22 Merge pull request #210 from abickerton/master
Fixed deprecation
2016-10-05 09:34:49 +02:00
Alec Bickerton 8992dc5710 Fixed deprecation 2016-09-04 19:18:04 +02:00
John Stemberger 33b88ed645 Added a check to see if the textureview is available and if it is then trigger the onSurfaceTextureAvailable listener 2016-08-26 12:08:04 -04:00
Ralf Kistner 50e3acce09 Suppress lint warning. 2016-07-14 17:35:55 +02:00
Ralf Kistner a8305bd237 Merge pull request #185 from Servus7/analyze-code-inspection
FEATURE: applies some "Analyze code inspection" advises
2016-07-14 17:29:02 +02:00
Ralf Kistner 11de8f4288 Merge pull request #184 from sschuberth/master
Simplify integration instructions
2016-07-14 17:28:30 +02:00
Servus7 c9e3bf415f adds missing 'NonNull' annotations 2016-07-04 14:16:44 +02:00
Servus7 e27bef5518 surpresses warning of checked deprecation 2016-07-04 14:14:11 +02:00
Servus7 9ea3e3ef08 replaces 'StringBuilder' with 'String'
Reduces number of method calls to make the code more efficent.
2016-07-04 14:08:45 +02:00
Servus7 03ab7214f6 adds missing javadoc 2016-07-04 14:05:16 +02:00
Servus7 0b07174be9 removes unnecessary namespace declarations 2016-07-04 13:53:09 +02:00
Servus7 50ea6bb48a replaces explicit type argument 2016-07-04 13:51:14 +02:00
Servus7 aefdceaffd removes unnecessary new lines 2016-07-04 13:50:08 +02:00
Servus7 91157bc907 simplifies if statement 2016-07-04 13:42:16 +02:00
Servus7 4c745d697b deletes unnecessary imports 2016-07-04 13:39:03 +02:00
Sebastian Schuberth 2219087d6a Simplify integration instructions
The @aar syntax is only required if there also is a jar artifact to
distinguish from. It also has the side-effect to trigger "artifact only"
mode for Gradle, resulting in transitive module dependencies not being
resolved automatically, which was previously worked around by eitehr
specifying "transitive = true" or "com.google.zxing:core:3.2.1" directly.
neitehr is required if we just omit the @aar, which works fine as there is
no competing jar artifact.
2016-07-04 13:38:54 +02:00
Flavio Campelo c81ea8e6eb lock orientation doesn't work
this if condiction is inside lockOrientation method (right)
with this change, now the orientation will work when set manually.
2016-06-22 13:48:11 -03:00
Ralf Kistner 8ae76eaac6 Prepare release for 3.3.0. 2016-06-05 19:14:01 +02:00
Ralf Kistner 7d47cd352a Merge branch 'next-cleanup' 2016-06-05 19:09:20 +02:00
Ralf Kistner 3e12faa379 Add sample release config. 2016-06-05 19:08:59 +02:00
Ralf Kistner cac37cb7e5 Update documentation and add issue template. 2016-06-05 18:58:54 +02:00
Ralf Kistner 39430544f6 Update gradle tools. 2016-06-05 17:51:28 +02:00
Ralf Kistner e7fce1f563 Merge pull request #170 from gmazzo/patch-1
Update README.md
2016-05-31 18:35:12 +02:00
Guillermo Mazzola 5ad456a5d0 Update README.md
Improving the 'Changing the orientation' documentation
2016-05-24 15:33:16 -03:00
Ralf Kistner 905e4a33d6 Merge pull request #161 from lucasddaniel/master
Creating a timeout to finish the scan screen
2016-05-11 20:18:46 +02:00
Lucas Daniel d34fa22d7a create a new constructor to finish the barcode scan on predefined finish time. 2016-05-10 22:25:35 -03:00
Lucas Daniel cb68383a4c create a new constructor to finish the barcode scan on predefined finish time. 2016-05-10 21:59:47 -03:00
Ralf Kistner 842895f0c4 Add some docs and expose more camera internals. 2016-03-29 12:21:50 +02:00
Ralf Kistner 0aebd7b795 Rename CompoundBarcodeView -> DecoratedBarcodeView. 2016-03-29 12:04:00 +02:00
Ralf Kistner 706c407351 v3.2.0; Readme updates. 2016-02-06 16:42:37 +02:00
Ralf Kistner 962592eab4 Update readme instructions. 2016-02-06 16:31:53 +02:00
Ralf Kistner 2e5ec9e78c Improve sample with margin for older devices. 2016-02-06 16:27:10 +02:00
Ralf Kistner 7d2c96ffad Merge pull request #135 from journeyapps/refactor-preview-scaling
New preview scaling logic
2016-02-06 16:12:50 +02:00
Ralf Kistner db3962cd3c Merge branch 'master' into refactor-preview-scaling 2016-02-06 16:03:47 +02:00
Ralf Kistner 3bb383147d Slight adjustments to scaling strategies; add tests. 2016-02-06 16:01:26 +02:00
Ralf Kistner 062feb8453 Merge pull request #134 from journeyapps/fix/paused-viewfinder
Keep drawing viewfinder after pausing
2016-02-06 14:40:30 +02:00
Ralf Kistner 9dd1022ba2 Merge pull request #133 from journeyapps/fix/camera-initialization
Fix camera initialization
2016-02-06 14:40:21 +02:00
Ralf Kistner a7ee170653 Merge pull request #136 from journeyapps/fix/torch-control
Save torch state
2016-02-06 14:40:11 +02:00
Ralf Kistner ed3eddd357 Save torch state. 2016-02-06 14:30:45 +02:00
Ralf Kistner d0876fbd10 Merge pull request #112 from RK-IT-Media/master
more control over focus mode
2016-02-06 14:12:41 +02:00
Ralf Kistner 5cdd8fea69 Actually use safe mode when required. 2016-02-06 14:09:23 +02:00
Ralf Kistner d75b7a7c15 Fix regression. 2016-02-06 14:01:59 +02:00
Ralf Kistner 31bba74b34 Check for cameraInstance == null. 2016-02-06 14:00:53 +02:00
Ralf Kistner f0e0335ca4 Don't be too eager to reload camera on orientation change. 2016-02-06 14:00:10 +02:00
Ralf Kistner b61c0dc332 Keep drawing viewfinder after pausing. 2016-02-05 20:20:11 +02:00
Ralf Kistner 7bb0187ece Add notes on preview scaling. 2016-02-05 20:12:51 +02:00
Ralf Kistner c457d9a541 Default to using TextureView for direct usage of BarcodeView. 2016-02-05 19:28:05 +02:00
Ralf Kistner 568692eebe Extract common functionality into abstract class. 2016-02-05 19:19:59 +02:00
Ralf Kistner 3a05fc63a7 Allow specifying scaling strategies from the XML; add fitXY strategy. 2016-02-05 19:14:41 +02:00
Ralf Kistner cc0b16bc9d Different strategies for SurfaceView and TextureView. 2016-02-05 18:50:23 +02:00
Ralf Kistner 363db3cf73 New, semi-configurable scaling strategies. 2016-02-05 18:42:05 +02:00
Ralf Kistner 74d9b82a45 Merge pull request #123 from Kloudtek/master
Fixes journeyapps/zxing-android-embedded#117
2016-01-25 09:51:10 +02:00
Yannick Menager 72a90e75c1 Fixes journeyapps/zxing-android-embedded#117 2016-01-23 19:11:03 -08:00
Ralf Kistner 25b2997a51 Fix lint issues. 2015-12-29 16:42:51 +02:00
Ralf Kistner a3f546bc1a Fix build-tools issue on Travis. 2015-12-29 16:34:53 +02:00
Ralf Kistner 85a93e63b6 v3.1.0.
Update readme and changelog with details.
2015-12-29 16:15:14 +02:00
Ralf Kistner 56ef6c6dca Update sample with texture view. 2015-12-29 16:03:19 +02:00
Ralf Kistner dbf0067cf6 Merge branch 'surface-texture' 2015-12-29 15:57:08 +02:00
Ralf Kistner 77d0cdfe85 Merge branch 'upgrade/build-tools' 2015-12-29 15:56:46 +02:00
Ralf Kistner 002c8e676e Upgrade build tools and add initial runtime permission support. 2015-12-29 15:49:19 +02:00
Servus7 bcf2062b2b focus mode formatting correction 2015-12-18 14:59:06 +01:00
Servus7 878fc85f73 more control over focus mode
Provides finer control over focus mode. Now their are four modes where
you change choose from.
2015-12-18 14:46:57 +01:00
Ralf Kistner 5035f3c453 Merge branch 'master' of github.com:journeyapps/zxing-android-embedded 2015-11-01 19:03:23 +02:00
Ralf Kistner a35de1381a Merge pull request #97 from auchri/patch-1
Use newest version in README.md
2015-11-01 18:47:46 +02:00
auchri 2ace11eabc Use newest version in README.md 2015-10-31 22:56:39 +01:00
Ralf Kistner e25b6b5989 Default to SurfaceView; make useTextureView configurable. 2015-10-26 11:00:06 +02:00
Ralf Kistner e1de481ccd Improve lifecycle code. 2015-10-26 09:58:48 +02:00
Ralf Kistner 656363067d Fix centering of texture preview. 2015-10-26 09:49:35 +02:00
Ralf Kistner 4c12440dc6 Experimental SurfaceTexture support. 2015-10-23 16:51:06 +02:00
Ralf Kistner b95f7abba2 Abtract surface. 2015-10-23 15:47:38 +02:00
Ralf Kistner 856d634d5b Fix crash when framing rect is larger than the surface. 2015-10-18 20:56:24 +02:00
Ralf Kistner f0c7ad1fb3 Merge branch 'sunhoy-framing-rect' 2015-10-18 20:50:50 +02:00
Ralf Kistner ec1a4e7ccd Add getters and setters for framing rect size. 2015-10-18 20:35:50 +02:00
Ralf Kistner 8b7f5cae86 Move framing rectangle logic to CameraPreview. 2015-10-18 20:32:00 +02:00
Hanuk Lee and Sunho Yang 36962bcff1 [Issue #87] Changing size of framing rectangle in BarcodeView.
override calculateFramingRect method to make rectangle as a given size.
change parameters of initialize method so that we can set the values in the layout xml(custom_barcode_scanner.xml).
2015-10-07 14:46:22 -07:00
Ralf Kistner 4210396bd0 Update changelog. 2015-08-16 17:25:43 +02:00
Ralf Kistner b8fbbb9f60 v3.0.3 2015-08-16 17:22:07 +02:00
Ralf Kistner 5cc9968a58 Set preview FPS on Google Glass. 2015-08-16 17:20:34 +02:00
Ralf Kistner f616533466 Merge branch 'master' of github.com:journeyapps/zxing-android-embedded 2015-08-16 17:12:00 +02:00
Ralf Kistner 1deb4b55d4 Use windowTranslucentStatus in toolbar sample. 2015-08-16 17:02:27 +02:00
Ralf Kistner 624738f9ed Merge pull request #75 from commjoen/patch-1
Update ViewfinderView to be extendible
2015-08-07 09:36:03 +02:00
Jeroen Willemsen 5c97efddcf Update ViewfinderView to be extendible
We need to modify the ViewfinderView this way so that others can extend from it.
2015-08-07 08:04:54 +02:00
Ralf Kistner e9d0512b9f Merge pull request #72 from ALenfant/return_barcode_image
Add option to return the captured barcode image
2015-08-05 10:07:31 +02:00
Antonin Lenfant 0acaf239a6 Add option to return the captured barcode image 2015-08-04 18:12:35 +02:00
107 changed files with 4004 additions and 1431 deletions
+4 -1
View File
@@ -7,4 +7,7 @@ maven-repository
*.iml
local.properties
mvn-clone
*.keystore
.project
.settings
.classpath
+26 -3
View File
@@ -1,13 +1,36 @@
language: android
jdk: oraclejdk7
jdk: oraclejdk8
android:
components:
# Install tools twice to get the latest update
- tools
- tools
- platform-tools
- build-tools-28.0.3
- extra-android-support
- extra-android-m2repository
- extra-google-m2repository
licenses:
- 'android-sdk-preview-license-.+'
- 'android-sdk-license-.+'
- 'google-gdk-license-.+'
script:
- TERM=dumb ./gradlew build
branches:
only:
- master
before_install:
# Android SDK installation is a mess, especially with licenses. Not sure if all of this is required, but it works.
- echo "$ANDROID_HOME"
- mkdir -p "$ANDROID_HOME/licenses"
- echo -e "\n8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_HOME/licenses/android-sdk-license"
- echo -e "\n84831b9409646a918e30573bab4c9c91346d8abd" > "$ANDROID_HOME/licenses/android-sdk-preview-license"
- yes | sdkmanager "platforms;android-27"
script:
# build includes lint and test
- TERM=dumb ./gradlew build
after_failure:
- cat **/build/reports/lint-results.xml
+88
View File
@@ -1,3 +1,91 @@
### 4.1.0 (2020-01-07)
* Ability to hide the laser in ViewfinderView (#503).
* Make possibleResultPoints method in BarcodeCallback optional (#504).
* Ability to customize or disable the permission error dialog (#505).
### 4.0.2 (2019-09-07)
* Use androidx.
* Use zxing:core 3.4.0 by default.
* Minimum SDK version 24.
* Fix ArithmeticException.
* Fix ResultPoint locations when camera is mirrored.
### 4.0.0 / 4.0.1 (2019-09-07)
* Broken release - use 4.0.2.
### 3.6.0 (2018-03-04)
* Use zxing:core 3.3.2 by default (#360).
* Minimum SDK version 19, or 14 by using zxing:core 3.3.0.
* Fix preview race condition (#324).
* Request code can now specified per Intent, instead of globally (#287).
* More helpers to specify barcode formats.
* Allow scanning both inverted and non-inverted barcodes at the same time (alternating) (#326).
* More examples.
### 3.5.0 (2017-03-20)
* Allow changing the REQUEST_CODE value (#234).
* Add support for inverted scans (#235).
* Use zxing:core 3.3.0 by default (#265).
Fixes:
* Fix memory leak when using scan timeout (#283).
* Better handling of various camera errors (#241, #268, #270)
### 3.4.0 (2016-10-16)
Changes:
* Beep on scan is now controlled only by the media volume, and still plays
even if the device is in "silent mode", as long as the media volume is not muted.
* The 150ms delay after scanning is removed.
Fixes:
* An issue where the beep sometimes played twice is fixed (#221).
* Fix rare crash (#209)
* Fix orientation lock issue (#181)
* Fix race condition with TextureView (#204)
### 3.3.0 (2016-06-05)
* Add an optional timeout to cancel scanning. (#161)
* Rename CompoundBarcodeView to DecoratedBarcodeView.
* Add more internal documentation (comments).
### 3.2.0 (2016-02-06)
* Improved preview scaling strategies, configurable between centerCrop, fitCenter, fitXY (#135)
* Fix issues with Android 6 permission support (#123)
* Fix camera initialization issues, specifically related to orientation changes (#133)
* More control over focus mode (#112)
* Keep drawing viewfinder frame after scanning / pausing (#134)
* More control over torch state, and save the state on orientation change (#136)
### 3.1.0 (2015-12-29)
* Add support for Android 6 runtime permissions (Camera only).
* Experimental support for using TextureView instead of SurfaceView.
* Fix build issues with custom attributes.
* Support library version 23+ is now a requirement.
### 3.1.0 (2015-12-29)
* Initial Android 6 permission supoprt
### 3.0.3 (2015-08-16)
* Fix for preview on Google Glass.
* Make ViewfinderView extensible. (#75)
* Add option to return image of barcode via Intents. (#72)
### 3.0.2 (2015-07-21)
* Add helper class for encoding barcodes.
-18
View File
@@ -1,18 +0,0 @@
## Reporting an issue
Please include the following details when reporting a bug:
* Which version of the library are you using? (e.g. 3.0.0)
* Which phone/tablet are you using, and which Android version does it run? (e.g. Samsung Galaxy S5,
Android 5.0)
* Does the same happen on other devices or an emulator?
* Can you reproduce the error in the sample project included with the library? If not, can you
provide your own sample project or sample code that produces this error?
If the app crashes, please include a stacktrace. Android Studio has a log window for this,
otherwise you can use the `adb logcat` command-line tool.
Please note that any issues with the actual barcode scanning process (e.g. a specific barcode
doesn't scan, or a format is not supported), should be reported at
https://github.com/zxing/zxing/issues.
+84
View File
@@ -0,0 +1,84 @@
## Embedding BarcodeView
For more control over the UI or scanning behaviour, some components may be used directly:
* BarcodeView: Handles displaying the preview and decoding of the barcodes.
* DecoratedBarcodeView: Combines BarcodeView with a viewfinder for feedback, as well as some status /
prompt text.
* CaptureManager: Manages the InactivityTimer, BeepManager, orientation lock, and returning of the
barcode result.
These components can be used from any Activity or Fragment.
This is much more low-level than using IntentIntegrator. Your code becomes responsible for:
* Setting up the BarcodeView (doesn't have all the helpers from IntentIntegrator)*
* Requesting permission to do the Camera.
* Making sure only one Camera instance is active at a time.
* Handling the scan results.
Samples:
* [ContinuousCaptureActivity][6]: continuously scan and display results (instead of a once-off scan).
* [ToolbarCaptureActivity][8]: Same as the normal CaptureActivity, but with a Lollipop Toolbar.
## Notes on threading
For a responsive user interface, all camera operations happen on a dedicated background thread.
In most cases this doesn't matter, but it does mean that the camera is not released immediately
when the BarcodeView is paused. If you want to start using the camera for something else
immediately after scanning, use `BarcodeView#pauseAndWait()` instead of `BarcodeView#pause()`.
This will block the main thread until the camera is released.
## Notes on scaling
On each Android device, the camera has a set list of available preview sizes. When embedding the
barcode scanning along with other components on an Activity, there will almost never be a preview
size that matches up exactly, so we have to pick one and scale and/or crop it.
Also affecting this is that either SurfaceView or TextureView can be used to display the preview.
SurfaceView has better performance, but does not support cropping. TextureView is more powerful,
but has some performance overhead, and is only supported on Android API 14+. We use SurfaceView by
default.
To avoid aspect ratio distortion, we can crop the preview. However, in some combinations of
SurfaceView and other components, the camera preview may end up displaying outside the SurfaceView,
and over other components. This happens especially when:
1. Placing the scanner inside a dialog, or:
2. Other components are placed before the (Decorated)BarcodeView, resulting in a lower z-order.
For these cases we have two solutions:
1. Use TextureView instead of SurfaceView. This may have a performance impact, but solves the above
issues. Note that this is only available with Android API 14+.
2. Use either `fitCenter` or `fitXY` for scaling, instead of `centerCrop`. Note that `fitCenter` may
result in black bars next to the preview, and `fitXY` may distort the aspect ratio.
The default is to:
1. Use TextureView on Android API 14+, SurfaceView on lower versions.
2. Use `centerCrop` scaling when TextureView is used.
3. Use `fitCenter` if SurfaceView is used.
You can override these options:
```xml
<com.journeyapps.barcodescanner.DecoratedBarcodeView
android:layout_width="..."
android:layout_height="..."
app:zxing_use_texture_view="false" (defaults to true, only has an effect on Android API 14+)
app:zxing_preview_scaling_strategy="centerCrop"/> (or fitCenter / fitXY)
```
For a full-screen barcode scanner with no Toolbar, the recommended options are:
```
app:zxing_use_texture_view="false"
app:zxing_preview_scaling_strategy="centerCrop"
```
[8]: sample/src/main/java/example/zxing/ToolbarCaptureActivity.java
[6]: sample/src/main/java/example/zxing/ContinuousCaptureActivity.java
+33
View File
@@ -0,0 +1,33 @@
#### Description of the problem:
**Which library version are you using?** E.g. 3.2.0.
**Which phone/tablet are you using, and which Android version does it run? (e.g. Samsung Galaxy S5,
Android 5.0)**
**Does the same happen on other devices or an emulator?**
**Can you reproduce the issue in the sample project included with the library? If not, can you
provide your own sample project or sample code that produces this error?**
**In the case of an error do you have a stack trace or adb logs?**
<!--
If you have an issue scanning specific barcodes, please test with the Barcode Scanner application,
available on the Play Store: https://play.google.com/store/apps/details?id=com.google.zxing.client.android&hl=en
If you are also not able to scan the barcode there, the issue may be with the underlying zxing library,
in which case you should report the issue here: https://github.com/zxing/zxing/issues
-->
<!--
If you have a question on usage of the library, please check the documentation and sample
application first:
https://github.com/journeyapps/zxing-android-embedded/blob/master/README.md
https://github.com/journeyapps/zxing-android-embedded/blob/master/EMBEDDING.md
https://github.com/journeyapps/zxing-android-embedded/tree/master/sample/src/main/java/example/zxing
-->
+120 -55
View File
@@ -2,7 +2,7 @@
Barcode scanning library for Android, using [ZXing][2] for decoding.
The project is loosly based on the [ZXing Android Barcode Scanner application][2], but is not affiliated with the official ZXing project.
The project is loosely based on the [ZXing Android Barcode Scanner application][2], but is not affiliated with the official ZXing project.
Features:
@@ -11,47 +11,91 @@ Features:
3. Scanning can be performed in landscape or portrait mode.
4. Camera is managed in a background thread, for fast startup time.
## Version 3
A sample application is available in [Releases](https://github.com/journeyapps/zxing-android-embedded/releases).
Where [version 2][4] was essentially just a stripped-down version of the [Barcode Scanner application][2],
version 3 is a rewrite of a large part of the codebase, making it more versatile and customizable.
With the rewrite, many APIs for UI customization were removed. Instead, it is now recommended
to create a custom Activity using the lower-level components directly
(see [Customization](#customization) for details).
Other notable changes:
* The camera is now loaded in a background thread, making the activity start faster.
* The camera preview and decoding now function correctly in any orientation.
By default, Android SDK 24+ is required because of `zxing:core` 3.4.0. To support SDK 14+, see [Older SDK versions](#older-sdk-versions).
## Adding aar dependency with Gradle
From version 3 this is a single library, supporting Gingerbread and later versions of Android
(API level 9+). If you need support for earlier Android versions, use [version 2][4].
From version 4.x, only Android SDK 24+ is supported by default, and androidx is required.
Add the following to your build.gradle file:
Add the following to your `build.gradle` file:
```groovy
repositories {
jcenter()
mavenCentral()
}
dependencies {
compile 'com.journeyapps:zxing-android-embedded:3.0.2@aar'
compile 'com.google.zxing:core:3.2.0'
implementation 'com.journeyapps:zxing-android-embedded:4.2.0'
implementation 'androidx.appcompat:appcompat:1.0.2'
}
android {
buildToolsVersion '28.0.3' // Older versions may give compile errors
}
```
## Usage with Maven
## Older SDK versions
Maven is not supported currently, but it is possible that the aar can be used. Pull requests are
welcome.
For Android SDK versions < 24, you can downgrade `zxing:core` to 3.3.0 or earlier for Android 14+ support:
```groovy
repositories {
mavenCentral()
}
dependencies {
implementation('com.journeyapps:zxing-android-embedded:4.2.0') { transitive = false }
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'com.google.zxing:core:3.3.0'
}
android {
buildToolsVersion '28.0.3'
}
```
You'll also need this in your Android manifest:
```xml
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />
```
No guarantees are made on support for older SDK versions - you'll have to test to make sure it's compatible.
## Hardware Acceleration
Hardware acceleration is required since TextureView is used.
Make sure it is enabled in your manifest file:
```xml
<application android:hardwareAccelerated="true" ... >
```
## Usage with IntentIntegrator
Launch the intent with the default options:
```java
new IntentIntegrator(this).initiateScan(); // `this` is the current Activity
// Get the results:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if(result != null) {
if(result.getContents() == null) {
Toast.makeText(this, "Cancelled", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, "Scanned: " + result.getContents(), Toast.LENGTH_LONG).show();
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
```
Use from a Fragment:
@@ -68,60 +112,68 @@ integrator.setDesiredBarcodeFormats(IntentIntegrator.ONE_D_CODE_TYPES);
integrator.setPrompt("Scan a barcode");
integrator.setCameraId(0); // Use a specific camera of the device
integrator.setBeepEnabled(false);
integrator.setBarcodeImageEnabled(true);
integrator.initiateScan();
```
See [IntentIntegrator][5] for more options.
### Generate Barcode example
While this is not the primary purpose of this library, it does include basic support for
generating some barcode types:
```java
try {
BarcodeEncoder barcodeEncoder = new BarcodeEncoder();
Bitmap bitmap = barcodeEncoder.encodeBitmap("content", BarcodeFormat.QR_CODE, 400, 400);
ImageView imageViewQrCode = (ImageView) findViewById(R.id.qrCode);
imageViewQrCode.setImageBitmap(bitmap);
} catch(Exception e) {
}
No customization of the image is currently supported, including changing colors or padding. If you
require more customization, copy and modify the source for the encoder.
```
### Changing the orientation
To change the orientation, create a new Activity extending CaptureActivity, and specify the
orientation in your `AndroidManifest.xml`.
To change the orientation, specify the orientation in your `AndroidManifest.xml` and let the `ManifestMerger` to update the Activity's definition.
Sample:
```java
public class CaptureActivityAnyOrientation extends CaptureActivity {
}
```
```xml
<activity android:name=".CaptureActivityAnyOrientation"
android:screenOrientation="fullSensor"
android:stateNotNeeded="true"
android:theme="@style/zxing_CaptureTheme"
android:windowSoftInputMode="stateAlwaysHidden">
</activity>
<activity
android:name="com.journeyapps.barcodescanner.CaptureActivity"
android:screenOrientation="fullSensor"
tools:replace="screenOrientation" />
```
```java
IntentIntegrator integrator = new IntentIntegrator(this);
integrator.setCaptureActivity(CaptureActivityAnyOrientation.class);
integrator.setOrientationLocked(false);
integrator.initiateScan();
```
The previous API for `integrator.setOrientation()` was removed. It caused the Activity to be created
in landscape orientation, then destroyed and re-created in the requested orientation, which creates
a bad user experience. The only way around this is to specify the orientation in the manifest.
### Customization and advanced options
### Customization
See [EMBEDDING](EMBEDDING.md).
For more control over the UI or scanning behaviour, some components may be used directly:
For more advanced options, look at the [Sample Application](https://github.com/journeyapps/zxing-android-embedded/blob/master/sample/src/main/java/example/zxing/MainActivity.java),
and browse the source code of the library.
* BarcodeView: Handles displaying the preview and decoding of the barcodes.
* CompoundBarcodeView: Combines BarcodeView with a viewfinder for feedback, as well as some status /
prompt text.
* CaptureManager: Manages the InactivityTimer, BeepManager, orientation lock, and returning of the
barcode result.
This is considered advanced usage, and is not well-documented or supported.
These components can be used from any Activity.
## Android Permissions
Samples:
* [ContinuousCaptureActivity][6]: continuously scan and display results (instead of a once-off scan).
* [ToolbarCaptureActivity][8]: Same as the normal CaptureActivity, but with a Lollipop Toolbar.
The camera permission is required for barcode scanning to function. It is automatically included as
part of the library. On Android 6 it is requested at runtime when the barcode scanner is first opened.
When using BarcodeView directly (instead of via IntentIntegrator / CaptureActivity), you have to
request the permission manually before calling `BarcodeView#resume()`, otherwise the camera will
fail to open.
## Building locally
@@ -139,12 +191,27 @@ You can then use your local version by specifying in your `build.gradle` file:
## Sponsored by
[Journey][1] - Build enterprise mobile apps for iOS and Android. Work in the cloud, code in JavaScript and forget about back-end development.
[JourneyApps][1] - Creating business solutions with mobile apps. Fast.
## License
[Apache License 2.0][7]
Licensed under the [Apache License 2.0][7]
Copyright (C) 2012-2018 ZXing authors, Journey Mobile
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
[1]: http://journeyapps.com
@@ -152,6 +219,4 @@ You can then use your local version by specifying in your `build.gradle` file:
[3]: https://github.com/zxing/zxing/wiki/Scanning-Via-Intent
[4]: https://github.com/journeyapps/zxing-android-embedded/blob/2.x/README.md
[5]: zxing-android-embedded/src/com/google/zxing/integration/android/IntentIntegrator.java
[6]: sample/src/main/java/example/zxing/ContinuousCaptureActivity.java
[7]: http://www.apache.org/licenses/LICENSE-2.0
[8]: sample/src/main/java/example/zxing/ToolbarCaptureActivity.java
+30 -85
View File
@@ -1,91 +1,36 @@
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.0.1'
classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.12.0'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0'
}
}
// Projects to be published to bintray
def PUBLISH_PROJECTS = ['zxing-android-embedded']
subprojects {
repositories {
jcenter()
}
version = '3.0.2'
group = 'com.journeyapps'
apply plugin: 'android-sdk-manager'
ext.androidBuildTools = '21.1.2'
ext.androidTargetSdk = 21
ext.zxingCore = 'com.google.zxing:core:3.2.0'
if (PUBLISH_PROJECTS.contains(project.name)) {
apply plugin: 'maven-publish'
apply plugin: 'com.jfrog.bintray'
project.ext.artifactId = project.name // Default to subproject name
afterEvaluate {
task sourceJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.srcDirs
}
publishing {
publications {
// We need this check to cover Android Studio gradle sync
if(project.tasks.findByPath('bundleRelease') != null) {
maven(MavenPublication) {
artifact bundleRelease
artifactId project.artifactId
artifact sourceJar
pom.withXml {
// HACK to add dependencies to POM.
// When maven-publish can do this automatically for Android projects,
// remove this section.
def deps = asNode().appendNode('dependencies')
project.configurations.compile.allDependencies.each { dep ->
def node = deps.appendNode('dependency')
node.appendNode('groupId', dep.group)
node.appendNode('artifactId', dep.name)
node.appendNode('version', dep.version)
node.appendNode('scope', 'compile')
}
}
}
}
}
}
// To release, place bintray_user and bintray_key properties in ~/.gradle/gradle.properties,
// and run ./gradlew clean assembleRelease bintrayUpload
if(project.hasProperty('bintray_user') && project.hasProperty('bintray_key')) {
bintray {
user = bintray_user
key = bintray_key
publications = ['maven']
publish = true
pkg {
userOrg = 'journeyapps'
repo = 'maven'
name = 'zxing-android-embedded'
}
}
google()
mavenCentral()
jcenter {
content {
// https://youtrack.jetbrains.com/issue/IDEA-261387
includeModule("org.jetbrains.trove4j", "trove4j")
}
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.3'
}
}
subprojects {
repositories {
google()
mavenLocal()
mavenCentral()
jcenter {
content {
// https://youtrack.jetbrains.com/issue/IDEA-261387
includeModule("org.jetbrains.trove4j", "trove4j")
}
}
}
version = '4.2.0'
group = 'com.journeyapps'
ext.androidTargetSdk = 28
ext.zxingCore = 'com.google.zxing:core:3.4.0'
}
+3
View File
@@ -0,0 +1,3 @@
android.enableJetifier=true
android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536M
Binary file not shown.
+2 -2
View File
@@ -1,6 +1,6 @@
#Sat Dec 20 18:56:59 SAST 2014
#Sat Sep 07 15:17:02 SAST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-all.zip
Vendored
+26 -26
View File
@@ -6,12 +6,30 @@
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
@@ -30,6 +48,7 @@ die ( ) {
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
@@ -40,31 +59,11 @@ case "`uname`" in
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -90,7 +89,7 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -114,6 +113,7 @@ fi
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
Vendored
+90
View File
@@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
-1
View File
@@ -1 +0,0 @@
/build
-24
View File
@@ -1,24 +0,0 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion project.androidTargetSdk
buildToolsVersion project.androidBuildTools
defaultConfig {
minSdkVersion 11
targetSdkVersion project.androidTargetSdk
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
}
dependencies {
compile(project(':zxing-android-embedded')) { transitive = true }
}
-17
View File
@@ -1,17 +0,0 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /home/ralf/apps/android-studio/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="example.zxing" >
<application
android:allowBackup="true"
android:icon="@drawable/icon"
android:label="@string/app_name" >
<activity
android:name="example.zxing.MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
@@ -1,107 +0,0 @@
package example.zxing;
import android.app.Activity;
import android.app.Fragment;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void scanBarcode(View view) {
new IntentIntegrator((Activity)this).initiateScan();
}
public void scanBarcodeCustomOptions(View view) {
IntentIntegrator integrator = new IntentIntegrator(this);
integrator.setDesiredBarcodeFormats(IntentIntegrator.ONE_D_CODE_TYPES);
integrator.initiateScan();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if(result != null) {
if(result.getContents() == null) {
Toast.makeText(this, "Cancelled", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, "Scanned: " + result.getContents(), Toast.LENGTH_LONG).show();
}
} else {
// This is important, otherwise the result will not be passed to the fragment
super.onActivityResult(requestCode, resultCode, data);
}
}
/**
* Sample of scanning from a Fragment
*/
public static class ScanFragment extends Fragment {
private String toast;
public ScanFragment() {
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
displayToast();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_scan, container, false);
Button scan = (Button) view.findViewById(R.id.scan_from_fragment);
scan.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
scanFromFragment();
}
});
return view;
}
public void scanFromFragment() {
IntentIntegrator.forFragment(this).initiateScan();
}
private void displayToast() {
if(getActivity() != null && toast != null) {
Toast.makeText(getActivity(), toast, Toast.LENGTH_LONG).show();
toast = null;
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if(result != null) {
if(result.getContents() == null) {
toast = "Cancelled from fragment";
} else {
toast = "Scanned from fragment: " + result.getContents();
}
// At this point we may or may not have a reference to the activity
displayToast();
}
}
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

@@ -1,33 +0,0 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="example.zxing.MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/scan_barcode"
android:onClick="scanBarcode"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/custom_options"
android:onClick="scanBarcodeCustomOptions"/>
<fragment
android:tag="scan_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="example.zxing.MainActivity$ScanFragment" tools:layout="@layout/fragment_scan" />
</LinearLayout>
@@ -1,16 +0,0 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:context="example.zxing.MainActivity"
tools:showIn="@layout/activity_main">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/scan_from_fragment"
android:id="@+id/scan_from_fragment"/>
</LinearLayout>
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>
@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">ZXing Sample</string>
<string name="scan_barcode">Scan Barcode</string>
<string name="custom_options">Custom Options</string>
<string name="scan_from_fragment">Scan from fragment</string>
</resources>
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="android:Theme">
</style>
</resources>
-1
View File
@@ -1 +0,0 @@
/build
+53 -11
View File
@@ -2,32 +2,74 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion project.androidTargetSdk
buildToolsVersion project.androidBuildTools
defaultConfig {
minSdkVersion 9
minSdkVersion 24
targetSdkVersion project.androidTargetSdk
versionCode 1
versionName "1.0"
versionCode 411
versionName "4.1.1"
}
def validConfig
def keystoreFile
def keystorePassword
def keystoreAlias
try {
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
keystoreFile = properties.getProperty('keystore.file')
keystorePassword = properties.getProperty('keystore.password')
keystoreAlias = properties.getProperty('keystore.alias')
validConfig = keystoreFile != null && keystorePassword != null && keystoreAlias != null
} catch (error) {
validConfig = false
}
if (validConfig) {
System.out.println("Release signing configured with " + keystoreFile)
signingConfigs {
release {
storeFile project.rootProject.file(keystoreFile)
storePassword keystorePassword
keyAlias keystoreAlias
keyPassword keystorePassword
}
}
} else {
System.out.println("Specify keystore.file, keystore.alias and keystore.password in local.properties to enable release signing.")
}
buildTypes {
release {
if (validConfig) {
signingConfig signingConfigs.release
}
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
// If you use this from an external project, use the following instead:
// compile 'com.journeyapps:zxing-android-embedded:<version>@aar'
// compile 'com.google.zxing:core:3.2.0'
compile(project(':zxing-android-embedded')) { transitive = true }
compile 'com.android.support:appcompat-v7:22.0.0'
// implementation 'com.journeyapps:zxing-android-embedded:<version>'
implementation project(':zxing-android-embedded')
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.legacy:legacy-support-v13:1.0.0'
// For development purposes only
// leakcanary is for development purposes only
// https://github.com/square/leakcanary
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
// AboutLibraries
implementation "com.mikepenz:aboutlibraries:6.2.3"
}
+32 -31
View File
@@ -1,51 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="example.zxing" >
package="example.zxing">
<application
android:allowBackup="true"
android:name=".SampleApplication"
android:allowBackup="false"
android:icon="@drawable/icon"
android:label="@string/app_name"
android:name=".SampleApplication">
android:label="@string/app_name">
<activity
android:name="example.zxing.MainActivity"
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".ContinuousCaptureActivity">
</activity>
<activity android:name=".AnyOrientationCaptureActivity"
android:screenOrientation="fullSensor"
android:stateNotNeeded="true"
android:theme="@style/zxing_CaptureTheme"
android:windowSoftInputMode="stateAlwaysHidden">
</activity>
<activity android:name=".ToolbarCaptureActivity"
android:clearTaskOnLaunch="true"
android:screenOrientation="sensorLandscape"
android:stateNotNeeded="true"
android:theme="@style/AppCompatCaptureTheme"
android:windowSoftInputMode="stateAlwaysHidden">
</activity>
<activity android:name=".ContinuousCaptureActivity"></activity>
<activity
android:name=".AnyOrientationCaptureActivity"
android:screenOrientation="fullSensor"
android:stateNotNeeded="true"
android:theme="@style/zxing_CaptureTheme"
android:windowSoftInputMode="stateAlwaysHidden"></activity>
<activity
android:name=".ToolbarCaptureActivity"
android:clearTaskOnLaunch="true"
android:screenOrientation="portrait"
android:stateNotNeeded="true"
android:theme="@style/AppCompatCaptureTheme"
android:windowSoftInputMode="stateAlwaysHidden"></activity>
<activity
android:name=".CustomScannerActivity"
android:screenOrientation="fullSensor"
android:stateNotNeeded="true"
android:theme="@style/zxing_CaptureTheme"
android:windowSoftInputMode="stateAlwaysHidden">
</activity>
android:windowSoftInputMode="stateAlwaysHidden"></activity>
<activity
android:name=".SmallCaptureActivity"
android:screenOrientation="fullSensor"
android:stateNotNeeded="true"
android:theme="@style/zxing_CaptureTheme"
android:windowSoftInputMode="stateAlwaysHidden"></activity>
<activity
android:name=".TabbedScanning"
android:theme="@style/Theme.AppCompat"
android:label="@string/title_activity_tabbed_scanning"></activity>
</application>
</manifest>
</manifest>
@@ -1,19 +1,22 @@
package example.zxing;
import android.app.Activity;
import android.content.res.Configuration;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.ImageView;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.ResultPoint;
import com.google.zxing.client.android.BeepManager;
import com.journeyapps.barcodescanner.BarcodeCallback;
import com.journeyapps.barcodescanner.BarcodeResult;
import com.journeyapps.barcodescanner.CompoundBarcodeView;
import com.journeyapps.barcodescanner.DecoratedBarcodeView;
import com.journeyapps.barcodescanner.DefaultDecoderFactory;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/**
@@ -21,17 +24,25 @@ import java.util.List;
* a barcode is scanned.
*/
public class ContinuousCaptureActivity extends Activity {
private static final String TAG = ContinuousCaptureActivity.class.getSimpleName();
private CompoundBarcodeView barcodeView;
private DecoratedBarcodeView barcodeView;
private BeepManager beepManager;
private String lastText;
private BarcodeCallback callback = new BarcodeCallback() {
@Override
public void barcodeResult(BarcodeResult result) {
if (result.getText() != null) {
barcodeView.setStatusText(result.getText());
if(result.getText() == null || result.getText().equals(lastText)) {
// Prevent duplicate scans
return;
}
lastText = result.getText();
barcodeView.setStatusText(result.getText());
beepManager.playBeepSoundAndVibrate();
//Added preview of scanned barcode
ImageView imageView = (ImageView) findViewById(R.id.barcodePreview);
ImageView imageView = findViewById(R.id.barcodePreview);
imageView.setImageBitmap(result.getBitmapWithResultPoints(Color.YELLOW));
}
@@ -46,8 +57,13 @@ public class ContinuousCaptureActivity extends Activity {
setContentView(R.layout.continuous_scan);
barcodeView = (CompoundBarcodeView) findViewById(R.id.barcode_scanner);
barcodeView = findViewById(R.id.barcode_scanner);
Collection<BarcodeFormat> formats = Arrays.asList(BarcodeFormat.QR_CODE, BarcodeFormat.CODE_39);
barcodeView.getBarcodeView().setDecoderFactory(new DefaultDecoderFactory(formats));
barcodeView.initializeFromIntent(getIntent());
barcodeView.decodeContinuous(callback);
beepManager = new BeepManager(this);
}
@Override
@@ -2,33 +2,42 @@ package example.zxing;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import androidx.annotation.NonNull;
import com.journeyapps.barcodescanner.CaptureManager;
import com.journeyapps.barcodescanner.CompoundBarcodeView;
import com.journeyapps.barcodescanner.DecoratedBarcodeView;
import com.journeyapps.barcodescanner.ViewfinderView;
import java.util.Random;
/**
* Custom Scannner Activity extending from Activity to display a custom layout form scanner view.
*/
public class CustomScannerActivity extends Activity implements
CompoundBarcodeView.TorchListener {
DecoratedBarcodeView.TorchListener {
private CaptureManager capture;
private CompoundBarcodeView barcodeScannerView;
private DecoratedBarcodeView barcodeScannerView;
private Button switchFlashlightButton;
private ViewfinderView viewfinderView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom_scanner);
barcodeScannerView = (CompoundBarcodeView)findViewById(R.id.zxing_barcode_scanner);
barcodeScannerView = findViewById(R.id.zxing_barcode_scanner);
barcodeScannerView.setTorchListener(this);
switchFlashlightButton = (Button)findViewById(R.id.switch_flashlight);
switchFlashlightButton = findViewById(R.id.switch_flashlight);
viewfinderView = findViewById(R.id.zxing_viewfinder_view);
// if the device does not have flashlight in its camera,
// then remove the switch flashlight button...
@@ -38,7 +47,11 @@ public class CustomScannerActivity extends Activity implements
capture = new CaptureManager(this, barcodeScannerView);
capture.initializeFromIntent(getIntent(), savedInstanceState);
capture.setShowMissingCameraPermissionDialog(false);
capture.decode();
changeMaskColor(null);
changeLaserVisibility(true);
}
@Override
@@ -87,6 +100,16 @@ public class CustomScannerActivity extends Activity implements
}
}
public void changeMaskColor(View view) {
Random rnd = new Random();
int color = Color.argb(100, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));
viewfinderView.setMaskColor(color);
}
public void changeLaserVisibility(boolean visible) {
viewfinderView.setLaserVisibility(visible);
}
@Override
public void onTorchOn() {
switchFlashlightButton.setText(R.string.turn_off_flashlight);
@@ -97,4 +120,8 @@ public class CustomScannerActivity extends Activity implements
switchFlashlightButton.setText(R.string.turn_on_flashlight);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
capture.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
@@ -3,8 +3,8 @@ package example.zxing;
import android.content.Intent;
import android.hardware.Camera;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
import androidx.fragment.app.Fragment;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -12,11 +12,15 @@ import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;
import com.google.zxing.client.android.Intents;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
import com.mikepenz.aboutlibraries.LibsBuilder;
public class MainActivity extends ActionBarActivity {
public class MainActivity extends AppCompatActivity {
public final int CUSTOMIZED_REQUEST_CODE = 0x0000ffff;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -28,6 +32,22 @@ public class MainActivity extends ActionBarActivity {
new IntentIntegrator(this).initiateScan();
}
public void scanBarcodeWithCustomizedRequestCode(View view) {
new IntentIntegrator(this).setRequestCode(CUSTOMIZED_REQUEST_CODE).initiateScan();
}
public void scanBarcodeInverted(View view){
IntentIntegrator integrator = new IntentIntegrator(this);
integrator.addExtra(Intents.Scan.SCAN_TYPE, Intents.Scan.INVERTED_SCAN);
integrator.initiateScan();
}
public void scanMixedBarcodes(View view){
IntentIntegrator integrator = new IntentIntegrator(this);
integrator.addExtra(Intents.Scan.SCAN_TYPE, Intents.Scan.MIXED_SCAN);
integrator.initiateScan();
}
public void scanBarcodeCustomLayout(View view) {
IntentIntegrator integrator = new IntentIntegrator(this);
integrator.setCaptureActivity(AnyOrientationCaptureActivity.class);
@@ -38,6 +58,16 @@ public class MainActivity extends ActionBarActivity {
integrator.initiateScan();
}
public void scanPDF417(View view) {
IntentIntegrator integrator = new IntentIntegrator(this);
integrator.setDesiredBarcodeFormats(IntentIntegrator.PDF_417);
integrator.setPrompt("Scan something");
integrator.setOrientationLocked(false);
integrator.setBeepEnabled(false);
integrator.initiateScan();
}
public void scanBarcodeFrontCamera(View view) {
IntentIntegrator integrator = new IntentIntegrator(this);
integrator.setCameraId(Camera.CameraInfo.CAMERA_FACING_FRONT);
@@ -54,28 +84,64 @@ public class MainActivity extends ActionBarActivity {
}
public void scanCustomScanner(View view) {
new IntentIntegrator(this).setCaptureActivity(CustomScannerActivity.class).initiateScan();
new IntentIntegrator(this).setOrientationLocked(false).setCaptureActivity(CustomScannerActivity.class).initiateScan();
}
public void scanMarginScanner(View view) {
IntentIntegrator integrator = new IntentIntegrator(this);
integrator.setOrientationLocked(false);
integrator.setCaptureActivity(SmallCaptureActivity.class);
integrator.initiateScan();
}
public void scanWithTimeout(View view) {
IntentIntegrator integrator = new IntentIntegrator(this);
integrator.setTimeout(8000);
integrator.initiateScan();
}
public void tabs(View view) {
Intent intent = new Intent(this, TabbedScanning.class);
startActivity(intent);
}
public void about(View view) {
new LibsBuilder().start(this);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if(result != null) {
if(result.getContents() == null) {
Log.d("MainActivity", "Cancelled scan");
Toast.makeText(this, "Cancelled", Toast.LENGTH_LONG).show();
} else {
Log.d("MainActivity", "Scanned");
Toast.makeText(this, "Scanned: " + result.getContents(), Toast.LENGTH_LONG).show();
}
} else {
Log.d("MainActivity", "Weird");
if (requestCode != CUSTOMIZED_REQUEST_CODE && requestCode != IntentIntegrator.REQUEST_CODE) {
// This is important, otherwise the result will not be passed to the fragment
super.onActivityResult(requestCode, resultCode, data);
return;
}
switch (requestCode) {
case CUSTOMIZED_REQUEST_CODE: {
Toast.makeText(this, "REQUEST_CODE = " + requestCode, Toast.LENGTH_LONG).show();
break;
}
default:
break;
}
IntentResult result = IntentIntegrator.parseActivityResult(resultCode, data);
if(result.getContents() == null) {
Intent originalIntent = result.getOriginalIntent();
if (originalIntent == null) {
Log.d("MainActivity", "Cancelled scan");
Toast.makeText(this, "Cancelled", Toast.LENGTH_LONG).show();
} else if(originalIntent.hasExtra(Intents.Scan.MISSING_CAMERA_PERMISSION)) {
Log.d("MainActivity", "Cancelled scan due to missing camera permission");
Toast.makeText(this, "Cancelled due to missing camera permission", Toast.LENGTH_LONG).show();
}
} else {
Log.d("MainActivity", "Scanned");
Toast.makeText(this, "Scanned: " + result.getContents(), Toast.LENGTH_LONG).show();
}
}
/**
* Sample of scanning from a Fragment
*/
@@ -96,13 +162,8 @@ public class MainActivity extends ActionBarActivity {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_scan, container, false);
Button scan = (Button) view.findViewById(R.id.scan_from_fragment);
scan.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
scanFromFragment();
}
});
Button scan = view.findViewById(R.id.scan_from_fragment);
scan.setOnClickListener(v -> scanFromFragment());
return view;
}
@@ -0,0 +1,15 @@
package example.zxing;
import com.journeyapps.barcodescanner.CaptureActivity;
import com.journeyapps.barcodescanner.DecoratedBarcodeView;
/**
* This activity has a margin.
*/
public class SmallCaptureActivity extends CaptureActivity {
@Override
protected DecoratedBarcodeView initializeContent() {
setContentView(R.layout.capture_small);
return (DecoratedBarcodeView)findViewById(R.id.zxing_barcode_scanner);
}
}
@@ -0,0 +1,236 @@
package example.zxing;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.fragment.app.FragmentTransaction;
import androidx.legacy.app.FragmentStatePagerAdapter;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.journeyapps.barcodescanner.CameraPreview;
import com.journeyapps.barcodescanner.DecoratedBarcodeView;
public class TabbedScanning extends AppCompatActivity implements ActionBar.TabListener {
/**
* The {@link PagerAdapter} that will provide
* fragments for each of the sections. We use a
* {@link FragmentPagerAdapter} derivative, which will keep every
* loaded fragment in memory. If this becomes too memory intensive, it
* may be best to switch to a
* {@link FragmentStatePagerAdapter}.
*/
private SectionsPagerAdapter mSectionsPagerAdapter;
/**
* The {@link ViewPager} that will host the section contents.
*/
private ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tabbed_scanning);
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
// Set up the action bar.
final ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// When swiping between different sections, select the corresponding
// tab. We can also use ActionBar.Tab#select() to do this if we have
// a reference to the Tab.
mViewPager.setOffscreenPageLimit(0);
mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
}
});
// For each of the sections in the app, add a tab to the action bar.
for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
// Create a tab with text corresponding to the page title defined by
// the adapter. Also specify this Activity object, which implements
// the TabListener interface, as the callback (listener) for when
// this tab is selected.
actionBar.addTab(
actionBar.newTab()
.setText(mSectionsPagerAdapter.getPageTitle(i))
.setTabListener(this));
}
}
@Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
// When the given tab is selected, switch to the corresponding page in
// the ViewPager.
mViewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
@Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
/**
* A placeholder fragment containing a simple view.
*/
public static class ScanFragment extends Fragment {
DecoratedBarcodeView barcodeView;
public ScanFragment() {
}
/**
* Returns a new instance of this fragment for the given section
* number.
*/
public static ScanFragment newInstance() {
return new ScanFragment();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_tabbed_scanning, container, false);
barcodeView = rootView.findViewById(R.id.barcode_view);
return rootView;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(barcodeView != null) {
if (isVisibleToUser) {
barcodeView.resume();
} else {
barcodeView.pauseAndWait();
}
}
}
@Override
public void onPause() {
super.onPause();
barcodeView.pauseAndWait();
}
@Override
public void onResume() {
super.onResume();
barcodeView.resume();
}
}
/**
* A placeholder fragment containing a simple view.
*/
public static class CameraFragment extends Fragment {
private CameraPreview cameraPreview;
public CameraFragment() {
}
/**
* Returns a new instance of this fragment for the given section
* number.
*/
public static CameraFragment newInstance() {
return new CameraFragment();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_tabbed_camera, container, false);
cameraPreview = rootView.findViewById(R.id.camera_preview);
return rootView;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (cameraPreview != null) {
if (isVisibleToUser) {
cameraPreview.resume();
} else {
cameraPreview.pauseAndWait();
}
}
}
@Override
public void onPause() {
super.onPause();
cameraPreview.pauseAndWait();
}
@Override
public void onResume() {
super.onResume();
}
}
/**
* A {@link FragmentPagerAdapter} that returns a fragment corresponding to
* one of the sections/tabs/pages.
*/
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
if(position == 0) {
return ScanFragment.newInstance();
} else {
return CameraFragment.newInstance();
}
}
@Override
public int getCount() {
return 2;
}
@Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "Scan";
case 1:
return "Camera";
}
return null;
}
}
}
@@ -1,31 +1,31 @@
package example.zxing;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.Toolbar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.view.KeyEvent;
import com.journeyapps.barcodescanner.CaptureManager;
import com.journeyapps.barcodescanner.CompoundBarcodeView;
import com.journeyapps.barcodescanner.DecoratedBarcodeView;
/**
* Sample Activity extending from ActionBarActivity to display a Toolbar.
*/
public class ToolbarCaptureActivity extends ActionBarActivity {
public class ToolbarCaptureActivity extends AppCompatActivity {
private CaptureManager capture;
private CompoundBarcodeView barcodeScannerView;
private DecoratedBarcodeView barcodeScannerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.capture_appcompat);
Toolbar toolbar = (Toolbar) findViewById(R.id.my_awesome_toolbar);
Toolbar toolbar = findViewById(R.id.my_awesome_toolbar);
toolbar.setTitle("Scan Barcode");
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
barcodeScannerView = (CompoundBarcodeView)findViewById(R.id.zxing_barcode_scanner);
barcodeScannerView = findViewById(R.id.zxing_barcode_scanner);
capture = new CaptureManager(this, barcodeScannerView);
capture.initializeFromIntent(getIntent(), savedInstanceState);
@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:zxing_view="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="example.zxing.CustomScannerActivity">
<com.journeyapps.barcodescanner.CompoundBarcodeView
<com.journeyapps.barcodescanner.DecoratedBarcodeView
android:id="@+id/zxing_barcode_scanner"
android:layout_width="match_parent"
android:layout_height="match_parent"
zxing_view:zxing_scanner_layout="@layout/custom_barcode_scanner">
</com.journeyapps.barcodescanner.CompoundBarcodeView>
app:zxing_scanner_layout="@layout/custom_barcode_scanner">
</com.journeyapps.barcodescanner.DecoratedBarcodeView>
<Button
android:id="@+id/switch_flashlight"
@@ -19,6 +19,30 @@
android:text="@string/scan_barcode"
android:onClick="scanBarcode"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Scan PDF417"
android:onClick="scanPDF417"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/scan_barcode_with_request_code"
android:onClick="scanBarcodeWithCustomizedRequestCode"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Scan Inverted"
android:onClick="scanBarcodeInverted"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Scan Normal and Inverted"
android:onClick="scanMixedBarcodes"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -32,6 +56,7 @@
android:onClick="scanBarcodeFrontCamera"/>
<fragment
android:id="@+id/fragment_scan"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -56,6 +81,31 @@
android:text="@string/custom_scanner"
android:onClick="scanCustomScanner"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/scanner_with_margin"
android:onClick="scanMarginScanner"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/scanner_with_timeout"
android:onClick="scanWithTimeout"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tabs"
android:onClick="tabs"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/about"
android:onClick="about"/>
</LinearLayout>
@@ -0,0 +1,6 @@
<androidx.viewpager.widget.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="example.zxing.TabbedScanning" />
@@ -14,24 +14,40 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
<androidx.appcompat.widget.Toolbar
android:id="@+id/my_awesome_toolbar"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
app:theme="@style/ThemeOverlay.AppCompat.ActionBar"/>
<com.journeyapps.barcodescanner.CompoundBarcodeView
<com.journeyapps.barcodescanner.DecoratedBarcodeView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:id="@+id/zxing_barcode_scanner"/>
</LinearLayout>
android:layout_below="@+id/my_awesome_toolbar"
android:layout_alignParentBottom="true"
android:id="@+id/zxing_barcode_scanner"
app:zxing_use_texture_view="true"/>
<!-- Sample for a footer. Also add to DecoratedBarcodeView: android:layout_above="@+id/footer" -->
<!-- <TextView
android:id="@+id/footer"
android:layout_width="match_parent"
android:layout_height="150dp"
android:text="Footer"
android:gravity="center"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"/> -->
</RelativeLayout>
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2008 ZXing authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.journeyapps.barcodescanner.DecoratedBarcodeView
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="50dp"
android:layout_marginRight="50dp"
android:layout_marginTop="150dp"
android:layout_marginBottom="150dp"
android:id="@+id/zxing_barcode_scanner"
app:zxing_use_texture_view="false"
app:zxing_preview_scaling_strategy="fitXY"/>
</RelativeLayout>
@@ -3,14 +3,14 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.journeyapps.barcodescanner.CompoundBarcodeView
<com.journeyapps.barcodescanner.DecoratedBarcodeView
android:id="@+id/barcode_scanner"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_above="@+id/buttonsLayout"
android:layout_alignParentTop="true">
</com.journeyapps.barcodescanner.CompoundBarcodeView>
</com.journeyapps.barcodescanner.DecoratedBarcodeView>
<LinearLayout
android:orientation="vertical"
@@ -1,21 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:zxing_finder="http://schemas.android.com/apk/res-auto">
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.journeyapps.barcodescanner.BarcodeView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/zxing_barcode_surface"/>
android:id="@+id/zxing_barcode_surface"
app:zxing_framing_rect_width="250dp"
app:zxing_framing_rect_height="50dp"/>
<com.journeyapps.barcodescanner.ViewfinderView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/zxing_viewfinder_view"
zxing_finder:zxing_possible_result_points="@color/zxing_custom_possible_result_points"
zxing_finder:zxing_result_view="@color/zxing_custom_result_view"
zxing_finder:zxing_viewfinder_laser="@color/zxing_custom_viewfinder_laser"
zxing_finder:zxing_viewfinder_mask="@color/zxing_custom_viewfinder_mask"/>
app:zxing_possible_result_points="@color/zxing_custom_possible_result_points"
app:zxing_result_view="@color/zxing_custom_result_view"
app:zxing_viewfinder_laser="@color/zxing_custom_viewfinder_laser"
app:zxing_viewfinder_laser_visibility="true"
app:zxing_viewfinder_mask="@color/zxing_custom_viewfinder_mask"/>
<TextView
android:id="@+id/zxing_status_view"
@@ -0,0 +1,17 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="example.zxing.TabbedScanning$CameraFragment">
<com.journeyapps.barcodescanner.CameraPreview
android:id="@+id/camera_preview"
android:layout_width="match_parent"
android:layout_height="300dp"
/>
</RelativeLayout>
@@ -0,0 +1,18 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="example.zxing.TabbedScanning$ScanFragment">
<com.journeyapps.barcodescanner.DecoratedBarcodeView
android:id="@+id/barcode_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
@@ -0,0 +1,4 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="example.zxing.TabbedScanning">
</menu>
@@ -3,5 +3,6 @@
<style name="AppCompatCaptureTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">#ffb341</item>
<item name="android:windowTranslucentStatus">true</item>
</style>
</resources>
@@ -0,0 +1,6 @@
<resources>
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>
+7 -1
View File
@@ -1,15 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_name">ZXing Sample</string>
<string name="scan_barcode">Scan Barcode</string>
<string name="scan_barcode_with_request_code">Scan Barcode with customized request code</string>
<string name="any_orientation">1D Any Orientation</string>
<string name="scan_from_fragment">Scan from fragment</string>
<string name="front_camera">Front Camera</string>
<string name="custom_activity">Continuous Scan</string>
<string name="toolbar_activity">Activity with Toolbar</string>
<string name="custom_scanner">Custom Scanner Activity</string>
<string name="scanner_with_margin">Scanner with Margin</string>
<string name="scanner_with_timeout">Finish Scan in 8 Seconds</string>
<string name="turn_on_flashlight">Turn on Flashlight</string>
<string name="turn_off_flashlight">Turn off Flashlight</string>
<string name="title_activity_tabbed_scanning">Tabbed Scanning</string>
<string name="tabs">Tabs</string>
<string name="about">About</string>
</resources>
-1
View File
@@ -1,3 +1,2 @@
include ':zxing-android-embedded'
include ':sample'
include ':sample-nosupport'
@@ -18,9 +18,6 @@
<uses-permission android:name="android.permission.CAMERA"/>
<!-- Support Android 2.3+. -->
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="21"/>
<!-- Don't require camera, as this requires a rear camera. This allows it to work on the Nexus 7 -->
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.front" android:required="false"/>
+131 -8
View File
@@ -1,27 +1,56 @@
apply plugin: 'com.android.library'
apply plugin: 'maven-publish'
apply plugin: 'signing'
ext.artifactId = 'zxing-android-embedded'
dependencies {
compile project.zxingCore
// Optional dependency.
provided 'com.android.support:support-v4:22.0.0'
// Publishing config from https://getstream.io/blog/publishing-libraries-to-mavencentral-2021/
ext["signing.keyId"] = ''
ext["signing.password"] = ''
ext["signing.secretKeyRingFile"] = ''
ext["ossrhUsername"] = ''
ext["ossrhPassword"] = ''
ext["sonatypeStagingProfileId"] = ''
File secretPropsFile = project.rootProject.file('local.properties')
if (secretPropsFile.exists()) {
Properties p = new Properties()
p.load(new FileInputStream(secretPropsFile))
p.each { name, value ->
ext[name] = value
}
} else {
ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID')
ext["signing.password"] = System.getenv('SIGNING_PASSWORD')
ext["signing.secretKeyRingFile"] = System.getenv('SIGNING_SECRET_KEY_RING_FILE')
ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME')
ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD')
ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID')
}
dependencies {
api project.zxingCore
api 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.13'
testImplementation "org.mockito:mockito-core:1.9.5"
}
android {
resourcePrefix 'zxing_'
compileSdkVersion project.androidTargetSdk
buildToolsVersion project.androidBuildTools
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src-orig', 'src']
java.srcDirs = ['src']
res.srcDirs = ['res-orig', 'res']
assets.srcDirs = ['assets']
}
test.setRoot('test');
}
// This is bad practice - we should fix the warnings instead.
@@ -32,9 +61,103 @@ android {
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
testOptions {
// We test with primitives such as Rect, and rely on their default behaviour working.
unitTests.returnDefaultValues = true
}
defaultConfig {
minSdkVersion 24
}
buildTypes {
debug {
versionNameSuffix ".debug"
resValue "string", "app_version", "${defaultConfig.versionName}${versionNameSuffix}"
}
release {
resValue "string", "app_version", "${defaultConfig.versionName}"
}
}
}
task sourceJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.srcDirs
}
project.afterEvaluate {
publishing {
publications {
maven(MavenPublication) {
artifact bundleReleaseAar
artifactId project.artifactId
artifact sourceJar
pom {
name = project.artifactId
description = 'Barcode scanner library for Android, based on the ZXing decoder'
url = 'https://github.com/journeyapps/zxing-android-embedded'
licenses {
license {
name = 'The Apache License, Version 2.0'
url = 'https://github.com/journeyapps/zxing-android-embedded/blob/master/COPYING'
}
}
developers {
developer {
id = ''
name = 'Ralf Kistner'
email = 'ralf@journeyapps.com'
organization = 'Journey Mobile, Inc'
organizationUrl = 'https://journeyapps.com'
}
}
scm {
connection = 'scm:git:github.com/journeyapps/zxing-android-embedded.git'
developerConnection = 'scm:git:ssh://github.com/journeyapps/zxing-android-embedded.git'
url = 'https://github.com/journeyapps/zxing-android-embedded'
}
}
pom.withXml {
// HACK to add dependencies to POM.
// When maven-publish can do this automatically for Android projects,
// remove this section.
def deps = asNode().appendNode('dependencies')
project.configurations.api.allDependencies.each { dep ->
def node = deps.appendNode('dependency')
node.appendNode('groupId', dep.group)
node.appendNode('artifactId', dep.name)
node.appendNode('version', dep.version)
node.appendNode('scope', 'api')
}
}
}
}
repositories {
maven {
name = "sonatype"
url = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
credentials {
username ossrhUsername
password ossrhPassword
}
}
}
}
}
signing {
sign publishing.publications
}
@@ -18,5 +18,5 @@
<string name="zxing_app_name">Barcode Scanner</string>
<string name="zxing_button_ok">D\'acord</string>
<string name="zxing_msg_camera_framework_bug">S\'ha produït un problema amb la càmera de l\'Android. Potser haureu de reiniciar el dispositiu.</string>
<string name="zxing_msg_default_status">Poseu un codi de barres dins el rectable per escanejar-lo.</string>
<string name="zxing_msg_default_status">Poseu un codi de barres dins el rectangle per escanejar-lo.</string>
</resources>
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<com.journeyapps.barcodescanner.BarcodeView
android:layout_width="match_parent"
@@ -15,11 +15,16 @@
limitations under the License.
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.journeyapps.barcodescanner.CompoundBarcodeView
<!--
This Activity is typically full-screen. Therefore we can safely use centerCrop scaling with
a SurfaceView, without fear of weird artifacts. -->
<com.journeyapps.barcodescanner.DecoratedBarcodeView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/zxing_barcode_scanner"/>
android:id="@+id/zxing_barcode_scanner"
app:zxing_preview_scaling_strategy="centerCrop"
app:zxing_use_texture_view="false"/>
</merge>
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<style name="zxing_CaptureTheme" parent="android:Theme.Holo.NoActionBar.Fullscreen">
</style>
</resources>
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="ResourceName" >
<string name="define_zxingandroidembedded" translatable="false" />
<!-- Author section -->
<string name="library_zxingandroidembedded_author" translatable="false">JourneyApps</string>
<string name="library_zxingandroidembedded_authorWebsite" translatable="false">https://journeyapps.com/</string>
<!-- Library section -->
<string name="library_zxingandroidembedded_libraryName" translatable="false">ZXing Android Embedded</string>
<string name="library_zxingandroidembedded_libraryDescription" translatable="false">Barcode scanning library for Android, using ZXing for decoding.</string>
<string name="library_zxingandroidembedded_libraryWebsite" translatable="false">https://github.com/journeyapps/zxing-android-embedded</string>
<string name="library_zxingandroidembedded_libraryVersion" translatable="false" />
<!-- OpenSource section -->
<string name="library_zxingandroidembedded_isOpenSource" translatable="false">true</string>
<string name="library_zxingandroidembedded_repositoryLink" translatable="false">https://github.com/journeyapps/zxing-android-embedded</string>
<!-- License section -->
<string name="library_zxingandroidembedded_licenseId" translatable="false">apache_2_0</string>
<!-- Custom variables section -->
</resources>
@@ -5,11 +5,23 @@
<attr name="zxing_scanner_layout" format="reference"/>
</declare-styleable>
<declare-styleable name="zxing_camera_preview">
<attr name="zxing_framing_rect_width" format="dimension" />
<attr name="zxing_framing_rect_height" format="dimension" />
<attr name="zxing_use_texture_view" format="boolean" />
<attr name="zxing_preview_scaling_strategy" format="enum">
<enum name="centerCrop" value="1" />
<enum name="fitCenter" value="2" />
<enum name="fitXY" value="3" />
</attr>
</declare-styleable>
<declare-styleable name="zxing_finder">
<attr name="zxing_possible_result_points" format="color"/>
<attr name="zxing_result_view" format="color"/>
<attr name="zxing_viewfinder_laser" format="color"/>
<attr name="zxing_viewfinder_mask" format="color"/>
<attr name="zxing_viewfinder_laser_visibility" format="boolean"/>
</declare-styleable>
</resources>
@@ -16,10 +16,12 @@
-->
<resources>
<item type="id" name="zxing_decode"/>
<item type="id" name="zxing_preview_failed"/>
<item type="id" name="zxing_decode_failed"/>
<item type="id" name="zxing_decode_succeeded"/>
<item type="id" name="zxing_possible_result_points"/>
<item type="id" name="zxing_back_button"/>
<item type="id" name="zxing_prewiew_size_ready"/>
<item type="id" name="zxing_camera_error"/>
<item type="id" name="zxing_camera_closed"/>
</resources>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<style name="zxing_CaptureTheme" parent="android:Theme.Black.NoTitleBar.Fullscreen">
<style name="zxing_CaptureTheme" parent="android:Theme.Holo.NoActionBar.Fullscreen">
</style>
</resources>
@@ -71,12 +71,7 @@ public final class AmbientLightManager implements SensorEventListener {
}
private void setTorch(final boolean on) {
handler.post(new Runnable() {
@Override
public void run() {
cameraManager.setTorch(on);
}
});
handler.post(() -> cameraManager.setTorch(on));
}
@Override
@@ -95,5 +90,4 @@ public final class AmbientLightManager implements SensorEventListener {
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// do nothing
}
}
@@ -16,39 +16,38 @@
package com.google.zxing.client.android;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Vibrator;
import android.util.Log;
import java.io.Closeable;
import java.io.IOException;
/**
* Manages beeps and vibrations.
*/
public final class BeepManager implements
MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener, Closeable {
public final class BeepManager {
private static final String TAG = BeepManager.class.getSimpleName();
private static final float BEEP_VOLUME = 0.10f;
private static final long VIBRATE_DURATION = 200L;
private final Activity activity;
private MediaPlayer mediaPlayer;
private boolean playBeep;
private final Context context;
private boolean beepEnabled = true;
private boolean vibrateEnabled = false;
public BeepManager(Activity activity) {
this.activity = activity;
this.mediaPlayer = null;
updatePrefs();
activity.setVolumeControlStream(AudioManager.STREAM_MUSIC);
// We do not keep a reference to the Activity itself, to prevent leaks
this.context = activity.getApplicationContext();
}
public boolean isBeepEnabled() {
@@ -79,45 +78,39 @@ public final class BeepManager implements
this.vibrateEnabled = vibrateEnabled;
}
public synchronized void updatePrefs() {
playBeep = shouldBeep(beepEnabled, activity);
if (playBeep && mediaPlayer == null) {
// The volume on STREAM_SYSTEM is not adjustable, and users found it too loud,
// so we now play on the music stream.
activity.setVolumeControlStream(AudioManager.STREAM_MUSIC);
mediaPlayer = buildMediaPlayer(activity);
}
}
@SuppressLint("MissingPermission")
public synchronized void playBeepSoundAndVibrate() {
if (playBeep && mediaPlayer != null) {
mediaPlayer.start();
if (beepEnabled) {
playBeepSound();
}
if (vibrateEnabled) {
Vibrator vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(VIBRATE_DURATION);
}
}
private static boolean shouldBeep(boolean beep, Context activity) {
boolean shouldPlayBeep = beep;
if (shouldPlayBeep) {
// See if sound settings overrides this
AudioManager audioService = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
shouldPlayBeep = false;
Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
if (vibrator != null) {
vibrator.vibrate(VIBRATE_DURATION);
}
}
return shouldPlayBeep;
}
private MediaPlayer buildMediaPlayer(Context activity) {
public MediaPlayer playBeepSound() {
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setAudioAttributes(new AudioAttributes.Builder().setContentType(
AudioAttributes.CONTENT_TYPE_MUSIC).build());
mediaPlayer.setOnCompletionListener(mp -> {
mp.stop();
mp.reset();
mp.release();
});
mediaPlayer.setOnErrorListener((mp, what, extra) -> {
Log.w(TAG, "Failed to beep " + what + ", " + extra);
// possibly media player error, so release and recreate
mp.stop();
mp.reset();
mp.release();
return true;
});
try {
AssetFileDescriptor file = activity.getResources().openRawResourceFd(R.raw.zxing_beep);
AssetFileDescriptor file = context.getResources().openRawResourceFd(R.raw.zxing_beep);
try {
mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());
} finally {
@@ -125,40 +118,13 @@ public final class BeepManager implements
}
mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
mediaPlayer.prepare();
mediaPlayer.start();
return mediaPlayer;
} catch (IOException ioe) {
Log.w(TAG, ioe);
mediaPlayer.reset();
mediaPlayer.release();
return null;
}
}
@Override
public void onCompletion(MediaPlayer mp) {
// When the beep has finished playing, rewind to queue up another one.
mp.seekTo(0);
}
@Override
public synchronized boolean onError(MediaPlayer mp, int what, int extra) {
if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
// we are finished, so put up an appropriate error toast if required and finish
activity.finish();
} else {
// possibly media player error, so release and recreate
mp.release();
mediaPlayer = null;
updatePrefs();
}
return true;
}
@Override
public synchronized void close() {
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
}
}
@@ -17,14 +17,12 @@
package com.google.zxing.client.android;
import android.content.Intent;
import android.net.Uri;
import com.google.zxing.BarcodeFormat;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
@@ -98,5 +96,4 @@ public final class DecodeFormatManager {
}
return null;
}
}
@@ -233,5 +233,4 @@ public final class DecodeHintManager {
Log.i(TAG, "Hints from the Intent: " + hints);
return hints;
}
}
@@ -108,12 +108,7 @@ public final class InactivityTimer {
// 0 indicates that we're on battery
final boolean onBatteryNow = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) <= 0;
// post on handler to run in main thread
handler.post(new Runnable() {
@Override
public void run() {
onBattery(onBatteryNow);
}
});
handler.post(() -> onBattery(onBatteryNow));
}
}
}
@@ -88,6 +88,13 @@ public final class Intents {
*/
public static final String CAMERA_ID = "SCAN_CAMERA_ID";
/**
* Optional parameter to switch the torch on at camera startup.
* Enables the torch on camera startup
* If provided, should be a boolean.
*/
public static final String TORCH_ENABLED = "TORCH_ENABLED";
/**
* @see com.google.zxing.DecodeHintType#CHARACTER_SET
*/
@@ -98,6 +105,30 @@ public final class Intents {
*/
public static final String BEEP_ENABLED = "BEEP_ENABLED";
/**
* Set to true to return a path to the barcode's image as it was captured. Defaults to false.
*/
public static final String BARCODE_IMAGE_ENABLED = "BARCODE_IMAGE_ENABLED";
/**
* Set the time to finish the scan screen.
*/
public static final String TIMEOUT = "TIMEOUT";
/**
* Set the time to finish the scan screen.
*/
public static final String MISSING_CAMERA_PERMISSION = "MISSING_CAMERA_PERMISSION";
/**
* Set the time to finish the scan screen.
*/
public static final String SHOW_MISSING_CAMERA_PERMISSION_DIALOG = "SHOW_MISSING_CAMERA_PERMISSION_DIALOG";
/**
* Set the time to finish the scan screen.
*/
public static final String MISSING_CAMERA_PERMISSION_DIALOG_MESSAGE = "MISSING_CAMERA_PERMISSION_DIALOG_MESSAGE";
/**
* Whether or not the orientation should be locked when the activity is first started.
@@ -162,6 +193,35 @@ public final class Intents {
*/
public static final String RESULT_BYTE_SEGMENTS_PREFIX = "SCAN_RESULT_BYTE_SEGMENTS_";
/**
* Call {@link android.content.Intent#getStringExtra(String)} with {@link #RESULT_BARCODE_IMAGE_PATH}
* to get a {@code String} path to a cropped and compressed png file of the barcode's image
* as it was displayed. Only available if
* {@link com.google.zxing.integration.android.IntentIntegrator#setBarcodeImageEnabled(boolean)}
* is called with true.
*/
public static final String RESULT_BARCODE_IMAGE_PATH = "SCAN_RESULT_IMAGE_PATH";
/**
* Define the scan type.
*/
public static final String SCAN_TYPE = "SCAN_TYPE";
/**
* Scan normal barcodes white on black
*/
public static final int NORMAL_SCAN = 0;
/**
* The scan should be inverted. White becomes black, black becomes white.
*/
public static final int INVERTED_SCAN = 1;
/**
* Scan alternating inverted and normal barcodes.
*/
public static final int MIXED_SCAN = 2;
private Scan() {
}
}
@@ -83,5 +83,4 @@ public final class OpenCameraInterface {
return Camera.open(cameraId);
}
}
}
@@ -22,8 +22,6 @@ import android.app.Fragment;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.Display;
import android.view.WindowManager;
import com.google.zxing.client.android.Intents;
import com.journeyapps.barcodescanner.CaptureActivity;
@@ -36,7 +34,6 @@ import java.util.List;
import java.util.Map;
/**
*
* @author Sean Owen
* @author Fred Lin
* @author Isaac Potoczny-Jones
@@ -47,34 +44,56 @@ import java.util.Map;
public class IntentIntegrator {
public static final int REQUEST_CODE = 0x0000c0de; // Only use bottom 16 bits
private static final String TAG = IntentIntegrator.class.getSimpleName();
// supported barcode formats
public static final Collection<String> PRODUCT_CODE_TYPES = list("UPC_A", "UPC_E", "EAN_8", "EAN_13", "RSS_14");
// Product Codes
public static final String UPC_A = "UPC_A";
public static final String UPC_E = "UPC_E";
public static final String EAN_8 = "EAN_8";
public static final String EAN_13 = "EAN_13";
public static final String RSS_14 = "RSS_14";
// Other 1D
public static final String CODE_39 = "CODE_39";
public static final String CODE_93 = "CODE_93";
public static final String CODE_128 = "CODE_128";
public static final String ITF = "ITF";
public static final String RSS_EXPANDED = "RSS_EXPANDED";
// 2D
public static final String QR_CODE = "QR_CODE";
public static final String DATA_MATRIX = "DATA_MATRIX";
public static final String PDF_417 = "PDF_417";
public static final Collection<String> PRODUCT_CODE_TYPES = list(UPC_A, UPC_E, EAN_8, EAN_13, RSS_14);
public static final Collection<String> ONE_D_CODE_TYPES =
list("UPC_A", "UPC_E", "EAN_8", "EAN_13", "CODE_39", "CODE_93", "CODE_128",
"ITF", "RSS_14", "RSS_EXPANDED");
public static final Collection<String> QR_CODE_TYPES = Collections.singleton("QR_CODE");
public static final Collection<String> DATA_MATRIX_TYPES = Collections.singleton("DATA_MATRIX");
list(UPC_A, UPC_E, EAN_8, EAN_13, RSS_14, CODE_39, CODE_93, CODE_128,
ITF, RSS_14, RSS_EXPANDED);
public static final Collection<String> ALL_CODE_TYPES = null;
private final Activity activity;
private android.app.Fragment fragment;
private android.support.v4.app.Fragment supportFragment;
private androidx.fragment.app.Fragment supportFragment;
private final Map<String, Object> moreExtras = new HashMap<String, Object>(3);
private final Map<String, Object> moreExtras = new HashMap<>(3);
private Collection<String> desiredBarcodeFormats;
private Class<?> captureActivity;
private int requestCode = REQUEST_CODE;
protected Class<?> getDefaultCaptureActivity() {
return CaptureActivity.class;
}
/**
* @param activity {@link Activity} invoking the integration
*/
public IntentIntegrator(Activity activity) {
this.activity = activity;
}
@@ -97,12 +116,27 @@ public class IntentIntegrator {
return this;
}
/**
* Change the request code that is used for the Intent. If it is changed, it is the caller's
* responsibility to check the request code from the result intent.
*
* @param requestCode the new request code
* @return this
*/
public IntentIntegrator setRequestCode(int requestCode) {
if (requestCode <= 0 || requestCode > 0x0000ffff) {
throw new IllegalArgumentException("requestCode out of range");
}
this.requestCode = requestCode;
return this;
}
/**
* @param fragment {@link Fragment} invoking the integration.
* {@link #startActivityForResult(Intent, int)} will be called on the {@link Fragment} instead
* of an {@link Activity}
*/
public static IntentIntegrator forSupportFragment(android.support.v4.app.Fragment fragment) {
public static IntentIntegrator forSupportFragment(androidx.fragment.app.Fragment fragment) {
IntentIntegrator integrator = new IntentIntegrator(fragment.getActivity());
integrator.supportFragment = fragment;
return integrator;
@@ -164,6 +198,18 @@ public class IntentIntegrator {
return this;
}
/**
* Set to true to enable initial torch
*
* @param enabled true to enable initial torch
* @return this
*/
public IntentIntegrator setTorchEnabled(boolean enabled) {
addExtra(Intents.Scan.TORCH_ENABLED, enabled);
return this;
}
/**
* Set to false to disable beep on scan.
*
@@ -175,6 +221,17 @@ public class IntentIntegrator {
return this;
}
/**
* Set to true to enable saving the barcode image and sending its path in the result Intent.
*
* @param enabled true to enable barcode image
* @return this
*/
public IntentIntegrator setBarcodeImageEnabled(boolean enabled) {
addExtra(Intents.Scan.BARCODE_IMAGE_ENABLED, enabled);
return this;
}
/**
* Set the desired barcode formats to scan.
*
@@ -186,11 +243,33 @@ public class IntentIntegrator {
return this;
}
/**
* Set the desired barcode formats to scan.
*
* @param desiredBarcodeFormats names of {@code BarcodeFormat}s to scan for
* @return this
*/
public IntentIntegrator setDesiredBarcodeFormats(String... desiredBarcodeFormats) {
this.desiredBarcodeFormats = Arrays.asList(desiredBarcodeFormats);
return this;
}
/**
* Initiates a scan for all known barcode types with the default camera.
*/
public final void initiateScan() {
startActivityForResult(createScanIntent(), REQUEST_CODE);
startActivityForResult(createScanIntent(), requestCode);
}
/**
* Initiates a scan for all known barcode types with the default camera.
* And starts a timer to finish on timeout
*
* @return Activity.RESULT_CANCELED and true on parameter TIMEOUT.
*/
public IntentIntegrator setTimeout(long timeout) {
addExtra(Intents.Scan.TIMEOUT, timeout);
return this;
}
/**
@@ -244,9 +323,7 @@ public class IntentIntegrator {
*/
protected void startActivityForResult(Intent intent, int code) {
if (fragment != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
fragment.startActivityForResult(intent, code);
}
fragment.startActivityForResult(intent, code);
} else if (supportFragment != null) {
supportFragment.startActivityForResult(intent, code);
} else {
@@ -254,12 +331,9 @@ public class IntentIntegrator {
}
}
protected void startActivity(Intent intent) {
if (fragment != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
fragment.startActivity(intent);
}
fragment.startActivity(intent);
} else if (supportFragment != null) {
supportFragment.startActivity(intent);
} else {
@@ -270,6 +344,8 @@ public class IntentIntegrator {
/**
* <p>Call this from your {@link Activity}'s
* {@link Activity#onActivityResult(int, int, Intent)} method.</p>
* <p>
* This checks that the requestCode is equal to the default REQUEST_CODE.
*
* @param requestCode request code from {@code onActivityResult()}
* @param resultCode result code from {@code onActivityResult()}
@@ -280,24 +356,39 @@ public class IntentIntegrator {
*/
public static IntentResult parseActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
String contents = intent.getStringExtra(Intents.Scan.RESULT);
String formatName = intent.getStringExtra(Intents.Scan.RESULT_FORMAT);
byte[] rawBytes = intent.getByteArrayExtra(Intents.Scan.RESULT_BYTES);
int intentOrientation = intent.getIntExtra(Intents.Scan.RESULT_ORIENTATION, Integer.MIN_VALUE);
Integer orientation = intentOrientation == Integer.MIN_VALUE ? null : intentOrientation;
String errorCorrectionLevel = intent.getStringExtra(Intents.Scan.RESULT_ERROR_CORRECTION_LEVEL);
return new IntentResult(contents,
formatName,
rawBytes,
orientation,
errorCorrectionLevel);
}
return new IntentResult();
return parseActivityResult(resultCode, intent);
}
return null;
}
/**
* Parse activity result, without checking the request code.
*
* @param resultCode result code from {@code onActivityResult()}
* @param intent {@link Intent} from {@code onActivityResult()}
* @return an {@link IntentResult} containing the result of the scan. If the user cancelled scanning,
* the fields will be null.
*/
public static IntentResult parseActivityResult(int resultCode, Intent intent) {
if (resultCode == Activity.RESULT_OK) {
String contents = intent.getStringExtra(Intents.Scan.RESULT);
String formatName = intent.getStringExtra(Intents.Scan.RESULT_FORMAT);
byte[] rawBytes = intent.getByteArrayExtra(Intents.Scan.RESULT_BYTES);
int intentOrientation = intent.getIntExtra(Intents.Scan.RESULT_ORIENTATION, Integer.MIN_VALUE);
Integer orientation = intentOrientation == Integer.MIN_VALUE ? null : intentOrientation;
String errorCorrectionLevel = intent.getStringExtra(Intents.Scan.RESULT_ERROR_CORRECTION_LEVEL);
String barcodeImagePath = intent.getStringExtra(Intents.Scan.RESULT_BARCODE_IMAGE_PATH);
return new IntentResult(contents,
formatName,
rawBytes,
orientation,
errorCorrectionLevel,
barcodeImagePath,
intent);
}
return new IntentResult(intent);
}
private static List<String> list(String... values) {
return Collections.unmodifiableList(Arrays.asList(values));
}
@@ -319,10 +410,21 @@ public class IntentIntegrator {
intent.putExtra(key, (Float) value);
} else if (value instanceof Bundle) {
intent.putExtra(key, (Bundle) value);
} else if (value instanceof int[]) {
intent.putExtra(key, (int[]) value);
} else if (value instanceof long[]) {
intent.putExtra(key, (long[]) value);
} else if (value instanceof boolean[]) {
intent.putExtra(key, (boolean[]) value);
} else if (value instanceof double[]) {
intent.putExtra(key, (double[]) value);
} else if (value instanceof float[]) {
intent.putExtra(key, (float[]) value);
} else if (value instanceof String[]) {
intent.putExtra(key, (String[]) value);
} else {
intent.putExtra(key, value.toString());
}
}
}
}
}
@@ -16,6 +16,8 @@
package com.google.zxing.integration.android;
import android.content.Intent;
/**
* <p>Encapsulates the result of a barcode scan invoked through {@link IntentIntegrator}.</p>
*
@@ -28,21 +30,31 @@ public final class IntentResult {
private final byte[] rawBytes;
private final Integer orientation;
private final String errorCorrectionLevel;
private final String barcodeImagePath;
private final Intent originalIntent;
IntentResult() {
this(null, null, null, null, null);
this(null, null, null, null, null, null, null);
}
IntentResult(Intent intent) {
this(null, null, null, null, null, null, intent);
}
IntentResult(String contents,
String formatName,
byte[] rawBytes,
Integer orientation,
String errorCorrectionLevel) {
String errorCorrectionLevel,
String barcodeImagePath,
Intent originalIntent) {
this.contents = contents;
this.formatName = formatName;
this.rawBytes = rawBytes;
this.orientation = orientation;
this.errorCorrectionLevel = errorCorrectionLevel;
this.barcodeImagePath = barcodeImagePath;
this.originalIntent = originalIntent;
}
/**
@@ -80,16 +92,29 @@ public final class IntentResult {
return errorCorrectionLevel;
}
@Override
public String toString() {
StringBuilder dialogText = new StringBuilder(100);
dialogText.append("Format: ").append(formatName).append('\n');
dialogText.append("Contents: ").append(contents).append('\n');
int rawBytesLength = rawBytes == null ? 0 : rawBytes.length;
dialogText.append("Raw bytes: (").append(rawBytesLength).append(" bytes)\n");
dialogText.append("Orientation: ").append(orientation).append('\n');
dialogText.append("EC level: ").append(errorCorrectionLevel).append('\n');
return dialogText.toString();
/**
* @return path to a temporary file containing the barcode image, if applicable, or null otherwise
*/
public String getBarcodeImagePath() {
return barcodeImagePath;
}
}
/**
* @return the original intent
*/
public Intent getOriginalIntent() {
return originalIntent;
}
@Override
public String toString() {
int rawBytesLength = rawBytes == null ? 0 : rawBytes.length;
return "Format: " + formatName + '\n' +
"Contents: " + contents + '\n' +
"Raw bytes: (" + rawBytesLength + " bytes)\n" +
"Orientation: " + orientation + '\n' +
"EC level: " + errorCorrectionLevel + '\n' +
"Barcode image: " + barcodeImagePath + '\n' +
"Original intent: " + originalIntent + '\n';
}
}
@@ -22,7 +22,10 @@ public interface BarcodeCallback {
*
* Do not depend on this being called at any specific point in the decode cycle.
*
* This is a default method and can be omitted by the implementing class.
*
* @param resultPoints points potentially identifying a barcode
*/
void possibleResultPoints(List<ResultPoint> resultPoints);
default void possibleResultPoints(List<ResultPoint> resultPoints) {
}
}
@@ -5,7 +5,6 @@ import android.graphics.Bitmap;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.Writer;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
@@ -71,8 +70,4 @@ public class BarcodeEncoder {
public Bitmap encodeBitmap(String contents, BarcodeFormat format, int width, int height, Map<EncodeHintType, ?> hints) throws WriterException {
return createBitmap(encode(contents, format, width, height, hints));
}
}
@@ -10,6 +10,10 @@ import com.google.zxing.Result;
import com.google.zxing.ResultMetadataType;
import com.google.zxing.ResultPoint;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
@@ -54,7 +58,14 @@ public class BarcodeResult {
* @see #getBitmapWithResultPoints(int)
*/
public Bitmap getBitmap() {
return sourceData.getBitmap(mScaleFactor);
return sourceData.getBitmap(null, mScaleFactor);
}
public List<ResultPoint> getTransformedResultPoints() {
if (this.mResult.getResultPoints() == null) {
return Collections.emptyList();
}
return transformResultPoints(Arrays.asList(this.mResult.getResultPoints()), this.sourceData);
}
/**
@@ -64,23 +75,23 @@ public class BarcodeResult {
public Bitmap getBitmapWithResultPoints(int color) {
Bitmap bitmap = getBitmap();
Bitmap barcode = bitmap;
ResultPoint[] points = mResult.getResultPoints();
List<ResultPoint> points = getTransformedResultPoints();
if (points != null && points.length > 0 && bitmap != null) {
if (!points.isEmpty() && bitmap != null) {
barcode = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(barcode);
canvas.drawBitmap(bitmap, 0, 0, null);
Paint paint = new Paint();
paint.setColor(color);
if (points.length == 2) {
if (points.size() == 2) {
paint.setStrokeWidth(PREVIEW_LINE_WIDTH);
drawLine(canvas, paint, points[0], points[1], mScaleFactor);
} else if (points.length == 4 &&
drawLine(canvas, paint, points.get(0), points.get(1), mScaleFactor);
} else if (points.size() == 4 &&
(mResult.getBarcodeFormat() == BarcodeFormat.UPC_A ||
mResult.getBarcodeFormat() == BarcodeFormat.EAN_13)) {
// Hacky special case -- draw two lines, for the barcode and metadata
drawLine(canvas, paint, points[0], points[1], mScaleFactor);
drawLine(canvas, paint, points[2], points[3], mScaleFactor);
drawLine(canvas, paint, points.get(0), points.get(1), mScaleFactor);
drawLine(canvas, paint, points.get(2), points.get(3), mScaleFactor);
} else {
paint.setStrokeWidth(PREVIEW_DOT_WIDTH);
for (ResultPoint point : points) {
@@ -153,4 +164,13 @@ public class BarcodeResult {
public String toString() {
return mResult.getText();
}
public static List<ResultPoint> transformResultPoints(List<ResultPoint> resultPoints, SourceData sourceData) {
List<ResultPoint> scaledPoints = new ArrayList<>(resultPoints.size());
for (ResultPoint point : resultPoints) {
scaledPoints.add(sourceData.translateResultPoint(point));
}
return scaledPoints;
}
}
@@ -60,6 +60,7 @@ public class BarcodeView extends CameraPreview {
// Failed. Next preview is automatically tried.
return true;
} else if (message.what == R.id.zxing_possible_result_points) {
//noinspection unchecked
List<ResultPoint> resultPoints = (List<ResultPoint>) message.obj;
if (callback != null && decodeMode != DecodeMode.NONE) {
callback.possibleResultPoints(resultPoints);
@@ -86,13 +87,11 @@ public class BarcodeView extends CameraPreview {
initialize();
}
private void initialize() {
decoderFactory = new DefaultDecoderFactory();
resultHandler = new Handler(resultCallback);
}
/**
* Set the DecoderFactory to use. Use this to specify the formats to decode.
*
@@ -143,7 +142,6 @@ public class BarcodeView extends CameraPreview {
startDecoderThread();
}
/**
* Continuously decode barcodes. The same barcode may be returned multiple times per second.
*
@@ -196,7 +194,6 @@ public class BarcodeView extends CameraPreview {
decoderThread = null;
}
}
/**
* Stops the live preview and decoding.
*
@@ -1,24 +1,36 @@
package com.journeyapps.barcodescanner;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.ViewGroup;
import android.view.WindowManager;
import com.google.zxing.client.android.R;
import com.journeyapps.barcodescanner.camera.CameraInstance;
import com.journeyapps.barcodescanner.camera.CameraParametersCallback;
import com.journeyapps.barcodescanner.camera.CameraSettings;
import com.journeyapps.barcodescanner.camera.CameraSurface;
import com.journeyapps.barcodescanner.camera.CenterCropStrategy;
import com.journeyapps.barcodescanner.camera.FitCenterStrategy;
import com.journeyapps.barcodescanner.camera.DisplayConfiguration;
import com.journeyapps.barcodescanner.camera.FitXYStrategy;
import com.journeyapps.barcodescanner.camera.PreviewScalingStrategy;
import java.util.ArrayList;
import java.util.List;
@@ -68,6 +80,11 @@ public class CameraPreview extends ViewGroup {
* @param error the error
*/
void cameraError(Exception error);
/**
* The camera has been closed.
*/
void cameraClosed();
}
private static final String TAG = CameraPreview.class.getSimpleName();
@@ -78,11 +95,19 @@ public class CameraPreview extends ViewGroup {
private Handler stateHandler;
private boolean useTextureView = false;
private SurfaceView surfaceView;
private TextureView textureView;
private boolean previewActive = false;
private RotationListener rotationListener;
private int openedOrientation = -1;
// Delay after rotation change is detected before we reorientate ourselves.
// This is to avoid double-reinitialization when the Activity is destroyed and recreated.
private static final int ROTATION_LISTENER_DELAY_MS = 250;
private List<StateListener> stateListeners = new ArrayList<>();
@@ -107,6 +132,43 @@ public class CameraPreview extends ViewGroup {
// Framing rectangle relative to the preview resolution
private Rect previewFramingRect = null;
// Size of the framing rectangle. If null, defaults to using a margin percentage.
private Size framingRectSize = null;
// Fraction of the width / heigth to use as a margin. This fraction is used on each size, so
// must be smaller than 0.5;
private double marginFraction = 0.1d;
private PreviewScalingStrategy previewScalingStrategy = null;
private boolean torchOn = false;
@TargetApi(14)
private TextureView.SurfaceTextureListener surfaceTextureListener() {
// Cannot initialize automatically, since we may be API < 14
return new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
onSurfaceTextureSizeChanged(surface, width, height);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
currentSurfaceSize = new Size(width, height);
startPreviewIfReady();
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
};
}
private final SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() {
@@ -135,6 +197,8 @@ public class CameraPreview extends ViewGroup {
@Override
public boolean handleMessage(Message message) {
if (message.what == R.id.zxing_prewiew_size_ready) {
// At this point, we have the camera preview size, and should have containerSize and
// surfaceRect.
previewSized((Size) message.obj);
return true;
} else if (message.what == R.id.zxing_camera_error) {
@@ -145,6 +209,8 @@ public class CameraPreview extends ViewGroup {
pause();
fireState.cameraError(error);
}
} else if (message.what == R.id.zxing_camera_closed) {
fireState.cameraClosed();
}
return false;
}
@@ -154,12 +220,7 @@ public class CameraPreview extends ViewGroup {
@Override
public void onRotationChanged(int rotation) {
// Make sure this is run on the main thread.
stateHandler.post(new Runnable() {
@Override
public void run() {
rotationChanged();
}
});
stateHandler.postDelayed(() -> rotationChanged(), ROTATION_LISTENER_DELAY_MS);
}
};
@@ -178,34 +239,76 @@ public class CameraPreview extends ViewGroup {
initialize(context, attrs, defStyleAttr, 0);
}
private void initialize(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
if (getBackground() == null) {
// Default to SurfaceView colour, so that there are less changes.
setBackgroundColor(Color.BLACK);
}
initializeAttributes(attrs);
windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
stateHandler = new Handler(stateCallback);
setupSurfaceView();
rotationListener = new RotationListener();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
setupSurfaceView();
}
/**
* Initialize from XML attributes.
*
* @param attrs the attributes
*/
protected void initializeAttributes(AttributeSet attrs) {
TypedArray styledAttributes = getContext().obtainStyledAttributes(attrs, R.styleable.zxing_camera_preview);
int framingRectWidth = (int) styledAttributes.getDimension(R.styleable.zxing_camera_preview_zxing_framing_rect_width, -1);
int framingRectHeight = (int) styledAttributes.getDimension(R.styleable.zxing_camera_preview_zxing_framing_rect_height, -1);
if (framingRectWidth > 0 && framingRectHeight > 0) {
this.framingRectSize = new Size(framingRectWidth, framingRectHeight);
}
this.useTextureView = styledAttributes.getBoolean(R.styleable.zxing_camera_preview_zxing_use_texture_view, true);
// See zxing_attrs.xml for the enum values
int scalingStrategyNumber = styledAttributes.getInteger(R.styleable.zxing_camera_preview_zxing_preview_scaling_strategy, -1);
if (scalingStrategyNumber == 1) {
previewScalingStrategy = new CenterCropStrategy();
} else if (scalingStrategyNumber == 2) {
previewScalingStrategy = new FitCenterStrategy();
} else if (scalingStrategyNumber == 3) {
previewScalingStrategy = new FitXYStrategy();
}
styledAttributes.recycle();
}
private void rotationChanged() {
pause();
resume();
// Confirm that it did actually change
if (isActive() && getDisplayRotation() != openedOrientation) {
pause();
resume();
}
}
private void setupSurfaceView() {
surfaceView = new SurfaceView(getContext());
if (Build.VERSION.SDK_INT < 11) {
surfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
if (useTextureView) {
textureView = new TextureView(getContext());
textureView.setSurfaceTextureListener(surfaceTextureListener());
addView(textureView);
} else {
surfaceView = new SurfaceView(getContext());
surfaceView.getHolder().addCallback(surfaceCallback);
addView(surfaceView);
}
surfaceView.getHolder().addCallback(surfaceCallback);
addView(surfaceView);
}
/**
@@ -246,6 +349,13 @@ public class CameraPreview extends ViewGroup {
listener.cameraError(error);
}
}
@Override
public void cameraClosed() {
for (StateListener listener : stateListeners) {
listener.cameraClosed();
}
}
};
private void calculateFrames() {
@@ -262,7 +372,13 @@ public class CameraPreview extends ViewGroup {
int width = containerSize.width;
int height = containerSize.height;
surfaceRect = displayConfiguration.scalePreview(previewSize);
Rect scaledPreview = displayConfiguration.scalePreview(previewSize);
if (scaledPreview.width() <= 0 || scaledPreview.height() <= 0) {
// Something is not ready yet - we can't start the preview.
return;
}
surfaceRect = scaledPreview;
Rect container = new Rect(0, 0, width, height);
framingRect = calculateFramingRect(container, surfaceRect);
@@ -274,7 +390,7 @@ public class CameraPreview extends ViewGroup {
frameInPreview.right * previewWidth / surfaceRect.width(),
frameInPreview.bottom * previewHeight / surfaceRect.height());
if (previewFramingRect.width() <= 0 || previewFramingRect.height() <= 0) {
if (previewFramingRect == null || previewFramingRect.width() <= 0 || previewFramingRect.height() <= 0) {
previewFramingRect = null;
framingRect = null;
Log.w(TAG, "Preview frame is too small");
@@ -289,22 +405,67 @@ public class CameraPreview extends ViewGroup {
* @param on true to turn on the torch
*/
public void setTorch(boolean on) {
torchOn = on;
if (cameraInstance != null) {
cameraInstance.setTorch(on);
}
}
/**
* Changes the settings for Camera.
* Must be called after {@link #resume()}.
*
* @param callback {@link CameraParametersCallback}
*/
public void changeCameraParameters(CameraParametersCallback callback) {
if (cameraInstance != null) {
cameraInstance.changeCameraParameters(callback);
}
}
private void containerSized(Size containerSize) {
this.containerSize = containerSize;
if (cameraInstance != null) {
if (cameraInstance.getDisplayConfiguration() == null) {
displayConfiguration = new DisplayConfiguration(getDisplayRotation(), containerSize);
displayConfiguration.setPreviewScalingStrategy(getPreviewScalingStrategy());
cameraInstance.setDisplayConfiguration(displayConfiguration);
cameraInstance.configureCamera();
if (torchOn) {
cameraInstance.setTorch(torchOn);
}
}
}
}
/**
* Override the preview scaling strategy.
*
* @param previewScalingStrategy null for the default
*/
public void setPreviewScalingStrategy(PreviewScalingStrategy previewScalingStrategy) {
this.previewScalingStrategy = previewScalingStrategy;
}
/**
* Override this to specify a different preview scaling strategy.
*/
public PreviewScalingStrategy getPreviewScalingStrategy() {
if (previewScalingStrategy != null) {
return previewScalingStrategy;
}
// If we are using SurfaceTexture, it is safe to use centerCrop.
// For SurfaceView, it's better to use fitCenter, otherwise the preview may overlap to
// other views.
if (textureView != null) {
return new CenterCropStrategy();
} else {
return new FitCenterStrategy();
}
}
private void previewSized(Size size) {
this.previewSize = size;
if (containerSize != null) {
@@ -314,10 +475,59 @@ public class CameraPreview extends ViewGroup {
}
}
/**
* Calculate transformation for the TextureView.
*
* An identity matrix would cause the preview to be scaled up/down to fill the TextureView.
*
* @param textureSize the size of the textureView
* @param previewSize the camera preview resolution
* @return the transform matrix for the TextureView
*/
protected Matrix calculateTextureTransform(Size textureSize, Size previewSize) {
float ratioTexture = (float) textureSize.width / (float) textureSize.height;
float ratioPreview = (float) previewSize.width / (float) previewSize.height;
float scaleX;
float scaleY;
// We scale so that either width or height fits exactly in the TextureView, and the other
// is bigger (cropped).
if (ratioTexture < ratioPreview) {
scaleX = ratioPreview / ratioTexture;
scaleY = 1;
} else {
scaleX = 1;
scaleY = ratioTexture / ratioPreview;
}
Matrix matrix = new Matrix();
matrix.setScale(scaleX, scaleY);
// Center the preview
float scaledWidth = textureSize.width * scaleX;
float scaledHeight = textureSize.height * scaleY;
float dx = (textureSize.width - scaledWidth) / 2;
float dy = (textureSize.height - scaledHeight) / 2;
// Perform the translation on the scaled preview
matrix.postTranslate(dx, dy);
return matrix;
}
private void startPreviewIfReady() {
if (currentSurfaceSize != null && previewSize != null && surfaceRect != null) {
if (currentSurfaceSize.equals(new Size(surfaceRect.width(), surfaceRect.height()))) {
startCameraPreview(surfaceView.getHolder());
if (surfaceView != null && currentSurfaceSize.equals(new Size(surfaceRect.width(), surfaceRect.height()))) {
startCameraPreview(new CameraSurface(surfaceView.getHolder()));
} else if (textureView != null && textureView.getSurfaceTexture() != null) {
if (previewSize != null) {
Matrix transform = calculateTextureTransform(new Size(textureView.getWidth(), textureView.getHeight()), previewSize);
textureView.setTransform(transform);
}
startCameraPreview(new CameraSurface(textureView.getSurfaceTexture()));
} else {
// Surface is not the correct size yet
}
@@ -329,12 +539,16 @@ public class CameraPreview extends ViewGroup {
protected void onLayout(boolean changed, int l, int t, int r, int b) {
containerSized(new Size(r - l, b - t));
if (surfaceRect == null) {
// Match the container, to reduce the risk of issues. The preview should never be drawn
// while the surface has this size.
surfaceView.layout(0, 0, getWidth(), getHeight());
} else {
surfaceView.layout(surfaceRect.left, surfaceRect.top, surfaceRect.right, surfaceRect.bottom);
if (surfaceView != null) {
if (surfaceRect == null) {
// Match the container, to reduce the risk of issues. The preview should never be drawn
// while the surface has this size.
surfaceView.layout(0, 0, getWidth(), getHeight());
} else {
surfaceView.layout(surfaceRect.left, surfaceRect.top, surfaceRect.right, surfaceRect.bottom);
}
} else if (textureView != null) {
textureView.layout(0, 0, getWidth(), getHeight());
}
}
@@ -362,6 +576,10 @@ public class CameraPreview extends ViewGroup {
return previewFramingRect;
}
public Size getPreviewSize() {
return previewSize;
}
/**
* @return the CameraSettings currently in use
*/
@@ -399,9 +617,15 @@ public class CameraPreview extends ViewGroup {
// The activity was paused but not stopped, so the surface still exists. Therefore
// surfaceCreated() won't be called, so init the camera here.
startPreviewIfReady();
} else {
} else if (surfaceView != null) {
// Install the callback and wait for surfaceCreated() to init the camera.
surfaceView.getHolder().addCallback(surfaceCallback);
} else if (textureView != null) {
if (textureView.isAvailable()) {
surfaceTextureListener().onSurfaceTextureAvailable(textureView.getSurfaceTexture(), textureView.getWidth(), textureView.getHeight());
} else {
textureView.setSurfaceTextureListener(surfaceTextureListener());
}
}
// To trigger surfaceSized again
@@ -409,7 +633,6 @@ public class CameraPreview extends ViewGroup {
rotationListener.listen(getContext(), rotationCallback);
}
/**
* Pause scanning and the camera preview. Typically this should be called from the Activity's
* onPause() method.
@@ -421,15 +644,21 @@ public class CameraPreview extends ViewGroup {
Util.validateMainThread();
Log.d(TAG, "pause()");
openedOrientation = -1;
if (cameraInstance != null) {
cameraInstance.close();
cameraInstance = null;
previewActive = false;
} else {
stateHandler.sendEmptyMessage(R.id.zxing_camera_closed);
}
if (currentSurfaceSize == null) {
if (currentSurfaceSize == null && surfaceView != null) {
SurfaceHolder surfaceHolder = surfaceView.getHolder();
surfaceHolder.removeCallback(surfaceCallback);
}
if (currentSurfaceSize == null && textureView != null) {
textureView.setSurfaceTextureListener(null);
}
this.containerSize = null;
this.previewSize = null;
@@ -439,6 +668,73 @@ public class CameraPreview extends ViewGroup {
fireState.previewStopped();
}
/**
* Pause scanning and preview; waiting for the Camera to be closed.
*
* This blocks the main thread.
*/
public void pauseAndWait() {
CameraInstance instance = getCameraInstance();
pause();
long startTime = System.nanoTime();
while(instance != null && !instance.isCameraClosed()) {
if (System.nanoTime() - startTime > 2000000000) {
// Don't wait for longer than 2 seconds
break;
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
break;
}
}
}
public Size getFramingRectSize() {
return framingRectSize;
}
/**
* Set an exact size for the framing rectangle. It will be centered in the view.
*
* @param framingRectSize the size
*/
public void setFramingRectSize(Size framingRectSize) {
this.framingRectSize = framingRectSize;
}
public double getMarginFraction() {
return marginFraction;
}
/**
* The the fraction of the width/height of view to be used as a margin for the framing rect.
* This is ignored if framingRectSize is specified.
*
* @param marginFraction the fraction
*/
public void setMarginFraction(double marginFraction) {
if (marginFraction >= 0.5d) {
throw new IllegalArgumentException("The margin fraction must be less than 0.5");
}
this.marginFraction = marginFraction;
}
public boolean isUseTextureView() {
return useTextureView;
}
/**
* Set to true to use TextureView instead of SurfaceView.
*
* Will only have an effect on API >= 14.
*
* @param useTextureView true to use TextureView.
*/
public void setUseTextureView(boolean useTextureView) {
this.useTextureView = useTextureView;
}
/**
* Considered active if between resume() and pause().
*
@@ -458,18 +754,33 @@ public class CameraPreview extends ViewGroup {
return;
}
cameraInstance = new CameraInstance(getContext());
cameraInstance.setCameraSettings(cameraSettings);
cameraInstance = createCameraInstance();
cameraInstance.setReadyHandler(stateHandler);
cameraInstance.open();
// Keep track of the orientation we opened at, so that we don't reopen the camera if we
// don't need to.
openedOrientation = getDisplayRotation();
}
/**
* Create a new CameraInstance.
*
* Override to use a custom CameraInstance.
*
* @return a new CameraInstance
*/
protected CameraInstance createCameraInstance() {
CameraInstance cameraInstance = new CameraInstance(getContext());
cameraInstance.setCameraSettings(cameraSettings);
return cameraInstance;
}
private void startCameraPreview(SurfaceHolder holder) {
if (!previewActive) {
private void startCameraPreview(CameraSurface surface) {
if (!previewActive && cameraInstance != null) {
Log.i(TAG, "Starting preview");
cameraInstance.setSurfaceHolder(holder);
cameraInstance.setSurface(surface);
cameraInstance.startPreview();
previewActive = true;
@@ -508,20 +819,31 @@ public class CameraPreview extends ViewGroup {
return previewActive;
}
/**
* Calculate framing rectangle, relative to the preview frame.
*
* Note that the SurfaceView may be larger than the container.
*
* Override this for more control over the framing rect calculations.
*
* @param container this container, with left = top = 0
* @param surface the SurfaceView, relative to this container
* @return the framing rect, relative to this container
*/
protected Rect calculateFramingRect(Rect container, Rect surface) {
// intersection is the part of the container that is used for the preview
Rect intersection = new Rect(container);
intersection.intersect(surface);
boolean intersects = intersection.intersect(surface);
// margin as 10% of the smaller of width, height
int margin = Math.min(intersection.width() / 10, intersection.height() / 10);
if (framingRectSize != null) {
// Specific size is specified. Make sure it's not larger than the container or surface.
int horizontalMargin = Math.max(0, (intersection.width() - framingRectSize.width) / 2);
int verticalMargin = Math.max(0, (intersection.height() - framingRectSize.height) / 2);
intersection.inset(horizontalMargin, verticalMargin);
return intersection;
}
// margin as 10% (default) of the smaller of width, height
int margin = (int)Math.min(intersection.width() * marginFraction, intersection.height() * marginFraction);
intersection.inset(margin, margin);
if (intersection.height() > intersection.width()) {
// We don't want a frame that is taller than wide.
@@ -529,4 +851,35 @@ public class CameraPreview extends ViewGroup {
}
return intersection;
}
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
Bundle myState = new Bundle();
myState.putParcelable("super", superState);
myState.putBoolean("torch", torchOn);
return myState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof Bundle)) {
super.onRestoreInstanceState(state);
return;
}
Bundle myState = (Bundle)state;
Parcelable superState = myState.getParcelable("super");
super.onRestoreInstanceState(superState);
boolean torch = myState.getBoolean("torch");
setTorch(torch);
}
/**
*
* @return true if the camera has been closed in a background thread.
*/
public boolean isCameraClosed() {
return cameraInstance == null || cameraInstance.isCameraClosed();
}
}
@@ -2,6 +2,7 @@ package com.journeyapps.barcodescanner;
import android.app.Activity;
import android.os.Bundle;
import androidx.annotation.NonNull;
import android.view.KeyEvent;
import com.google.zxing.client.android.R;
@@ -11,20 +12,29 @@ import com.google.zxing.client.android.R;
*/
public class CaptureActivity extends Activity {
private CaptureManager capture;
private CompoundBarcodeView barcodeScannerView;
private DecoratedBarcodeView barcodeScannerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.zxing_capture);
barcodeScannerView = (CompoundBarcodeView)findViewById(R.id.zxing_barcode_scanner);
barcodeScannerView = initializeContent();
capture = new CaptureManager(this, barcodeScannerView);
capture.initializeFromIntent(getIntent(), savedInstanceState);
capture.decode();
}
/**
* Override to use a different layout.
*
* @return the DecoratedBarcodeView
*/
protected DecoratedBarcodeView initializeContent() {
setContentView(R.layout.zxing_capture);
return (DecoratedBarcodeView)findViewById(R.id.zxing_barcode_scanner);
}
@Override
protected void onResume() {
super.onResume();
@@ -49,6 +59,11 @@ public class CaptureActivity extends Activity {
capture.onSaveInstanceState(outState);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
capture.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return barcodeScannerView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
@@ -1,11 +1,15 @@
package com.journeyapps.barcodescanner;
import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
@@ -14,6 +18,9 @@ import android.view.Surface;
import android.view.Window;
import android.view.WindowManager;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.google.zxing.ResultMetadataType;
import com.google.zxing.ResultPoint;
import com.google.zxing.client.android.BeepManager;
@@ -21,6 +28,9 @@ import com.google.zxing.client.android.InactivityTimer;
import com.google.zxing.client.android.Intents;
import com.google.zxing.client.android.R;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@@ -43,35 +53,33 @@ import java.util.Map;
public class CaptureManager {
private static final String TAG = CaptureManager.class.getSimpleName();
private static int cameraPermissionReqCode = 250;
private Activity activity;
private CompoundBarcodeView barcodeView;
private DecoratedBarcodeView barcodeView;
private int orientationLock = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
private static final String SAVED_ORIENTATION_LOCK = "SAVED_ORIENTATION_LOCK";
private boolean returnBarcodeImagePath = false;
private boolean showDialogIfMissingCameraPermission = true;
private String missingCameraPermissionDialogMessage = "";
private boolean destroyed = false;
// Delay long enough that the beep can be played.
// TODO: play beep in background
private static final long DELAY_BEEP = 150;
private InactivityTimer inactivityTimer;
private BeepManager beepManager;
private Handler handler;
private boolean finishWhenClosed = false;
private BarcodeCallback callback = new BarcodeCallback() {
@Override
public void barcodeResult(final BarcodeResult result) {
barcodeView.pause();
beepManager.playBeepSoundAndVibrate();
handler.postDelayed(new Runnable() {
@Override
public void run() {
returnResult(result);
}
}, DELAY_BEEP);
handler.post(() -> returnResult(result));
}
@Override
@@ -98,23 +106,30 @@ public class CaptureManager {
@Override
public void cameraError(Exception error) {
displayFrameworkBugMessageAndExit();
displayFrameworkBugMessageAndExit(
activity.getString(R.string.zxing_msg_camera_framework_bug)
);
}
@Override
public void cameraClosed() {
if (finishWhenClosed) {
Log.d(TAG, "Camera closed; finishing activity");
finish();
}
}
};
public CaptureManager(Activity activity, CompoundBarcodeView barcodeView) {
public CaptureManager(Activity activity, DecoratedBarcodeView barcodeView) {
this.activity = activity;
this.barcodeView = barcodeView;
barcodeView.getBarcodeView().addStateListener(stateListener);
handler = new Handler();
inactivityTimer = new InactivityTimer(activity, new Runnable() {
@Override
public void run() {
Log.d(TAG, "Finishing due to inactivity");
finish();
}
inactivityTimer = new InactivityTimer(activity, () -> {
Log.d(TAG, "Finishing due to inactivity");
finish();
});
beepManager = new BeepManager(activity);
@@ -137,14 +152,11 @@ public class CaptureManager {
this.orientationLock = savedInstanceState.getInt(SAVED_ORIENTATION_LOCK, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
if(intent != null) {
if (orientationLock == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
// Only lock the orientation if it's not locked to something else yet
boolean orientationLocked = intent.getBooleanExtra(Intents.Scan.ORIENTATION_LOCKED, true);
if (orientationLocked) {
lockOrientation();
}
if (intent != null) {
// Only lock the orientation if it's not locked to something else yet
boolean orientationLocked = intent.getBooleanExtra(Intents.Scan.ORIENTATION_LOCKED, true);
if (orientationLocked) {
lockOrientation();
}
if (Intents.Scan.ACTION.equals(intent.getAction())) {
@@ -153,7 +165,21 @@ public class CaptureManager {
if (!intent.getBooleanExtra(Intents.Scan.BEEP_ENABLED, true)) {
beepManager.setBeepEnabled(false);
beepManager.updatePrefs();
}
if (intent.hasExtra(Intents.Scan.SHOW_MISSING_CAMERA_PERMISSION_DIALOG)) {
setShowMissingCameraPermissionDialog(
intent.getBooleanExtra(Intents.Scan.SHOW_MISSING_CAMERA_PERMISSION_DIALOG, true),
intent.getStringExtra(Intents.Scan.MISSING_CAMERA_PERMISSION_DIALOG_MESSAGE)
);
}
if (intent.hasExtra(Intents.Scan.TIMEOUT)) {
handler.postDelayed(this::returnResultTimeout, intent.getLongExtra(Intents.Scan.TIMEOUT, 0L));
}
if (intent.getBooleanExtra(Intents.Scan.BARCODE_IMAGE_ENABLED, false)) {
returnBarcodeImagePath = true;
}
}
}
@@ -200,19 +226,61 @@ public class CaptureManager {
* Call from Activity#onResume().
*/
public void onResume() {
barcodeView.resume();
beepManager.updatePrefs();
if (Build.VERSION.SDK_INT >= 23) {
openCameraWithPermission();
} else {
barcodeView.resume();
}
inactivityTimer.start();
}
private boolean askedPermission = false;
@TargetApi(23)
private void openCameraWithPermission() {
if (ContextCompat.checkSelfPermission(this.activity, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED) {
barcodeView.resume();
} else if (!askedPermission) {
ActivityCompat.requestPermissions(this.activity,
new String[]{Manifest.permission.CAMERA},
cameraPermissionReqCode);
askedPermission = true;
} // else wait for permission result
}
/**
* Call from Activity#onRequestPermissionsResult
* @param requestCode The request code passed in {@link androidx.core.app.ActivityCompat#requestPermissions(Activity, String[], int)}.
* @param permissions The requested permissions.
* @param grantResults The grant results for the corresponding permissions
* which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
* or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
*/
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
if (requestCode == cameraPermissionReqCode) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted
barcodeView.resume();
} else {
setMissingCameraPermissionResult();
if (showDialogIfMissingCameraPermission) {
displayFrameworkBugMessageAndExit(missingCameraPermissionDialogMessage);
} else {
closeAndFinish();
}
}
}
}
/**
* Call from Activity#onPause().
*/
public void onPause() {
barcodeView.pause();
inactivityTimer.cancel();
beepManager.close();
barcodeView.pauseAndWait();
}
/**
@@ -221,6 +289,7 @@ public class CaptureManager {
public void onDestroy() {
destroyed = true;
inactivityTimer.cancel();
handler.removeCallbacksAndMessages(null);
}
/**
@@ -230,14 +299,14 @@ public class CaptureManager {
outState.putInt(SAVED_ORIENTATION_LOCK, this.orientationLock);
}
/**
* Create a intent to return as the Activity result.
*
* @param rawResult the BarcodeResult, must not be null.
* @param barcodeImagePath a path to an exported file of the Barcode Image, can be null.
* @return the Intent
*/
public static Intent resultIntent(BarcodeResult rawResult) {
public static Intent resultIntent(BarcodeResult rawResult, String barcodeImagePath) {
Intent intent = new Intent(Intents.Scan.ACTION);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
intent.putExtra(Intents.Scan.RESULT, rawResult.toString());
@@ -270,41 +339,117 @@ public class CaptureManager {
}
}
}
if (barcodeImagePath != null) {
intent.putExtra(Intents.Scan.RESULT_BARCODE_IMAGE_PATH, barcodeImagePath);
}
return intent;
}
/**
* Save the barcode image to a temporary file stored in the application's cache, and return its path.
* Only does so if returnBarcodeImagePath is enabled.
*
* @param rawResult the BarcodeResult, must not be null
* @return the path or null
*/
private String getBarcodeImagePath(BarcodeResult rawResult) {
String barcodeImagePath = null;
if (returnBarcodeImagePath) {
Bitmap bmp = rawResult.getBitmap();
try {
File bitmapFile = File.createTempFile("barcodeimage", ".jpg", activity.getCacheDir());
FileOutputStream outputStream = new FileOutputStream(bitmapFile);
bmp.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
outputStream.close();
barcodeImagePath = bitmapFile.getAbsolutePath();
} catch (IOException e) {
Log.w(TAG, "Unable to create temporary file and store bitmap! " + e);
}
}
return barcodeImagePath;
}
private void finish() {
activity.finish();
}
protected void closeAndFinish() {
if (barcodeView.getBarcodeView().isCameraClosed()) {
finish();
} else {
finishWhenClosed = true;
}
protected void returnResult(BarcodeResult rawResult) {
Intent intent = resultIntent(rawResult);
activity.setResult(Activity.RESULT_OK, intent);
finish();
barcodeView.pause();
inactivityTimer.cancel();
}
protected void displayFrameworkBugMessageAndExit() {
if (activity.isFinishing() || this.destroyed) {
private void setMissingCameraPermissionResult() {
Intent intent = new Intent(Intents.Scan.ACTION);
intent.putExtra(Intents.Scan.MISSING_CAMERA_PERMISSION, true);
activity.setResult(Activity.RESULT_CANCELED, intent);
}
protected void returnResultTimeout() {
Intent intent = new Intent(Intents.Scan.ACTION);
intent.putExtra(Intents.Scan.TIMEOUT, true);
activity.setResult(Activity.RESULT_CANCELED, intent);
closeAndFinish();
}
protected void returnResult(BarcodeResult rawResult) {
Intent intent = resultIntent(rawResult, getBarcodeImagePath(rawResult));
activity.setResult(Activity.RESULT_OK, intent);
closeAndFinish();
}
protected void displayFrameworkBugMessageAndExit(String message) {
if (activity.isFinishing() || this.destroyed || finishWhenClosed) {
return;
}
if (message.isEmpty()) {
message = activity.getString(R.string.zxing_msg_camera_framework_bug);
}
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(activity.getString(R.string.zxing_app_name));
builder.setMessage(activity.getString(R.string.zxing_msg_camera_framework_bug));
builder.setPositiveButton(R.string.zxing_button_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
finish();
}
});
builder.setMessage(message);
builder.setPositiveButton(R.string.zxing_button_ok, (dialog, which) -> finish());
builder.setOnCancelListener(dialog -> finish());
builder.show();
}
public static int getCameraPermissionReqCode() {
return cameraPermissionReqCode;
}
public static void setCameraPermissionReqCode(int cameraPermissionReqCode) {
CaptureManager.cameraPermissionReqCode = cameraPermissionReqCode;
}
/**
* If set to true, shows the default error dialog if camera permission is missing.
* <p>
* If set to false, instead the capture manager just finishes.
* <p>
* In both cases, the activity result is set to {@link Intents.Scan#MISSING_CAMERA_PERMISSION}
* and cancelled
*/
public void setShowMissingCameraPermissionDialog(boolean visible) {
setShowMissingCameraPermissionDialog(visible, "");
}
/**
* If set to true, shows the specified error dialog message if camera permission is missing.
* <p>
* If set to false, instead the capture manager just finishes.
* <p>
* In both cases, the activity result is set to {@link Intents.Scan#MISSING_CAMERA_PERMISSION}
* and cancelled
*/
public void setShowMissingCameraPermissionDialog(boolean visible, String message) {
showDialogIfMissingCameraPermission = visible;
missingCameraPermissionDialogMessage = message != null ? message : "";
}
}
@@ -1,264 +1,21 @@
package com.journeyapps.barcodescanner;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.ResultPoint;
import com.google.zxing.client.android.DecodeFormatManager;
import com.google.zxing.client.android.DecodeHintManager;
import com.google.zxing.client.android.Intents;
import com.google.zxing.client.android.R;
import com.journeyapps.barcodescanner.camera.CameraSettings;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Encapsulates BarcodeView, ViewfinderView and status text.
*
* To customize the UI, use BarcodeView and ViewfinderView directly.
* Used as an alias for DecoratedBarcodeView, for backwards-compatibility.
*/
public class CompoundBarcodeView extends FrameLayout {
private BarcodeView barcodeView;
private ViewfinderView viewFinder;
private TextView statusView;
/**
* The instance of @link TorchListener to send events callback.
*/
private TorchListener torchListener;
private class WrappedCallback implements BarcodeCallback {
private BarcodeCallback delegate;
public WrappedCallback(BarcodeCallback delegate) {
this.delegate = delegate;
}
@Override
public void barcodeResult(BarcodeResult result) {
delegate.barcodeResult(result);
}
@Override
public void possibleResultPoints(List<ResultPoint> resultPoints) {
for (ResultPoint point : resultPoints) {
viewFinder.addPossibleResultPoint(point);
}
delegate.possibleResultPoints(resultPoints);
}
}
public class CompoundBarcodeView extends DecoratedBarcodeView {
public CompoundBarcodeView(Context context) {
super(context);
initialize();
}
public CompoundBarcodeView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(attrs);
}
public CompoundBarcodeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(attrs);
}
/**
* Initialize the view with the xml configuration based on styleable attributes.
*
* @param attrs The attributes to use on view.
*/
private void initialize(AttributeSet attrs) {
// Get attributes set on view
TypedArray attributes = getContext().obtainStyledAttributes(attrs, R.styleable.zxing_view);
int scannerLayout = attributes.getResourceId(
R.styleable.zxing_view_zxing_scanner_layout, R.layout.zxing_barcode_scanner);
attributes.recycle();
inflate(getContext(), scannerLayout, this);
barcodeView = (BarcodeView) findViewById(R.id.zxing_barcode_surface);
if (barcodeView == null) {
throw new IllegalArgumentException(
"There is no a com.journeyapps.barcodescanner.BarcodeView on provided layout " +
"with the id \"zxing_barcode_surface\".");
}
viewFinder = (ViewfinderView) findViewById(R.id.zxing_viewfinder_view);
if (viewFinder == null) {
throw new IllegalArgumentException(
"There is no a com.journeyapps.barcodescanner.ViewfinderView on provided layout " +
"with the id \"zxing_viewfinder_view\".");
}
viewFinder.setCameraPreview(barcodeView);
// statusView is optional
statusView = (TextView) findViewById(R.id.zxing_status_view);
}
/**
* Initialize with no custom attributes setted.
*/
private void initialize() {
initialize(null);
}
/**
* Convenience method to initialize camera id, decode formats and prompt message from an intent.
*
* @param intent the intent, as generated by IntentIntegrator
*/
public void initializeFromIntent(Intent intent) {
// Scan the formats the intent requested, and return the result to the calling activity.
Set<BarcodeFormat> decodeFormats = DecodeFormatManager.parseDecodeFormats(intent);
Map<DecodeHintType, Object> decodeHints = DecodeHintManager.parseDecodeHints(intent);
CameraSettings settings = new CameraSettings();
if (intent.hasExtra(Intents.Scan.CAMERA_ID)) {
int cameraId = intent.getIntExtra(Intents.Scan.CAMERA_ID, -1);
if (cameraId >= 0) {
settings.setRequestedCameraId(cameraId);
}
}
String customPromptMessage = intent.getStringExtra(Intents.Scan.PROMPT_MESSAGE);
if (customPromptMessage != null) {
setStatusText(customPromptMessage);
}
String characterSet = intent.getStringExtra(Intents.Scan.CHARACTER_SET);
MultiFormatReader reader = new MultiFormatReader();
reader.setHints(decodeHints);
barcodeView.setCameraSettings(settings);
barcodeView.setDecoderFactory(new DefaultDecoderFactory(decodeFormats, decodeHints, characterSet));
}
public void setStatusText(String text) {
// statusView is optional when using a custom layout
if(statusView != null) {
statusView.setText(text);
}
}
/**
* @see BarcodeView#pause()
*/
public void pause() {
barcodeView.pause();
}
/**
* @see BarcodeView#resume()
*/
public void resume() {
barcodeView.resume();
}
public BarcodeView getBarcodeView() {
return (BarcodeView) findViewById(R.id.zxing_barcode_surface);
}
public ViewfinderView getViewFinder() {
return viewFinder;
}
public TextView getStatusView() {
return statusView;
}
/**
* @see BarcodeView#decodeSingle(BarcodeCallback)
*/
public void decodeSingle(BarcodeCallback callback) {
barcodeView.decodeSingle(new WrappedCallback(callback));
}
/**
* @see BarcodeView#decodeContinuous(BarcodeCallback)
*/
public void decodeContinuous(BarcodeCallback callback) {
barcodeView.decodeContinuous(new WrappedCallback(callback));
}
/**
* Turn on the device's flashlight.
*/
public void setTorchOn() {
barcodeView.setTorch(true);
if (torchListener != null) {
torchListener.onTorchOn();
}
}
/**
* Turn off the device's flashlight.
*/
public void setTorchOff() {
barcodeView.setTorch(false);
if (torchListener != null) {
torchListener.onTorchOff();
}
}
/**
* Handles focus, camera, volume up and volume down keys.
*
* Note that this view is not usually focused, so the Activity should call this directly.
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_FOCUS:
case KeyEvent.KEYCODE_CAMERA:
// Handle these events so they don't launch the Camera app
return true;
// Use volume up/down to turn on light
case KeyEvent.KEYCODE_VOLUME_DOWN:
setTorchOff();
return true;
case KeyEvent.KEYCODE_VOLUME_UP:
setTorchOn();
return true;
}
return super.onKeyDown(keyCode, event);
}
public void setTorchListener(TorchListener listener) {
this.torchListener = listener;
}
/**
* The Listener to torch/fflashlight events (turn on, turn off).
*/
public interface TorchListener {
void onTorchOn();
void onTorchOff();
}
}
@@ -26,7 +26,7 @@ public class DecoderResultPointCallback implements ResultPointCallback {
@Override
public void foundPossibleResultPoint(ResultPoint point) {
if(decoder != null) {
if (decoder != null) {
decoder.foundPossibleResultPoint(point);
}
}
@@ -36,6 +36,9 @@ public class DecoderThread {
public boolean handleMessage(Message message) {
if (message.what == R.id.zxing_decode) {
decode((SourceData) message.obj);
} else if (message.what == R.id.zxing_preview_failed) {
// Error already logged. Try again.
requestNextPreview();
}
return true;
}
@@ -80,7 +83,6 @@ public class DecoderThread {
requestNextPreview();
}
/**
* Stop decoding.
*
@@ -96,7 +98,6 @@ public class DecoderThread {
}
}
private final PreviewCallback previewCallback = new PreviewCallback() {
@Override
public void onPreview(SourceData sourceData) {
@@ -111,12 +112,20 @@ public class DecoderThread {
}
}
}
@Override
public void onPreviewError(Exception e) {
synchronized (LOCK) {
if (running) {
// Post to our thread.
handler.obtainMessage(R.id.zxing_preview_failed).sendToTarget();
}
}
}
};
private void requestNextPreview() {
if (cameraInstance.isOpen()) {
cameraInstance.requestPreview(previewCallback);
}
cameraInstance.requestPreview(previewCallback);
}
protected LuminanceSource createSource(SourceData sourceData) {
@@ -133,7 +142,7 @@ public class DecoderThread {
sourceData.setCropRect(cropRect);
LuminanceSource source = createSource(sourceData);
if(source != null) {
if (source != null) {
rawResult = decoder.decode(source);
}
@@ -155,11 +164,10 @@ public class DecoderThread {
}
}
if (resultHandler != null) {
List<ResultPoint> resultPoints = decoder.getPossibleResultPoints();
Message message = Message.obtain(resultHandler, R.id.zxing_possible_result_points, resultPoints);
List<ResultPoint> resultPoints = BarcodeResult.transformResultPoints(decoder.getPossibleResultPoints(), sourceData);
Message message = Message.obtain(resultHandler, R.id.zxing_possible_result_points, resultPoints);
message.sendToTarget();
}
requestNextPreview();
}
}
@@ -0,0 +1,306 @@
package com.journeyapps.barcodescanner;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.ResultPoint;
import com.google.zxing.client.android.DecodeFormatManager;
import com.google.zxing.client.android.DecodeHintManager;
import com.google.zxing.client.android.Intents;
import com.google.zxing.client.android.R;
import com.journeyapps.barcodescanner.camera.CameraParametersCallback;
import com.journeyapps.barcodescanner.camera.CameraSettings;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Encapsulates BarcodeView, ViewfinderView and status text.
*
* To customize the UI, use BarcodeView and ViewfinderView directly.
*/
public class DecoratedBarcodeView extends FrameLayout {
private BarcodeView barcodeView;
private ViewfinderView viewFinder;
private TextView statusView;
/**
* The instance of @link TorchListener to send events callback.
*/
private TorchListener torchListener;
private class WrappedCallback implements BarcodeCallback {
private BarcodeCallback delegate;
public WrappedCallback(BarcodeCallback delegate) {
this.delegate = delegate;
}
@Override
public void barcodeResult(BarcodeResult result) {
delegate.barcodeResult(result);
}
@Override
public void possibleResultPoints(List<ResultPoint> resultPoints) {
for (ResultPoint point : resultPoints) {
viewFinder.addPossibleResultPoint(point);
}
delegate.possibleResultPoints(resultPoints);
}
}
public DecoratedBarcodeView(Context context) {
super(context);
initialize();
}
public DecoratedBarcodeView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(attrs);
}
public DecoratedBarcodeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(attrs);
}
/**
* Initialize the view with the xml configuration based on styleable attributes.
*
* @param attrs The attributes to use on view.
*/
private void initialize(AttributeSet attrs) {
// Get attributes set on view
TypedArray attributes = getContext().obtainStyledAttributes(attrs, R.styleable.zxing_view);
int scannerLayout = attributes.getResourceId(
R.styleable.zxing_view_zxing_scanner_layout, R.layout.zxing_barcode_scanner);
attributes.recycle();
inflate(getContext(), scannerLayout, this);
barcodeView = findViewById(R.id.zxing_barcode_surface);
if (barcodeView == null) {
throw new IllegalArgumentException(
"There is no a com.journeyapps.barcodescanner.BarcodeView on provided layout " +
"with the id \"zxing_barcode_surface\".");
}
// Pass on any preview-related attributes
barcodeView.initializeAttributes(attrs);
viewFinder = findViewById(R.id.zxing_viewfinder_view);
if (viewFinder == null) {
throw new IllegalArgumentException(
"There is no a com.journeyapps.barcodescanner.ViewfinderView on provided layout " +
"with the id \"zxing_viewfinder_view\".");
}
viewFinder.setCameraPreview(barcodeView);
// statusView is optional
statusView = findViewById(R.id.zxing_status_view);
}
/**
* Initialize with no custom attributes set.
*/
private void initialize() {
initialize(null);
}
/**
* Convenience method to initialize camera id, decode formats and prompt message from an intent.
*
* @param intent the intent, as generated by IntentIntegrator
*/
public void initializeFromIntent(Intent intent) {
// Scan the formats the intent requested, and return the result to the calling activity.
Set<BarcodeFormat> decodeFormats = DecodeFormatManager.parseDecodeFormats(intent);
Map<DecodeHintType, Object> decodeHints = DecodeHintManager.parseDecodeHints(intent);
CameraSettings settings = new CameraSettings();
if (intent.hasExtra(Intents.Scan.CAMERA_ID)) {
int cameraId = intent.getIntExtra(Intents.Scan.CAMERA_ID, -1);
if (cameraId >= 0) {
settings.setRequestedCameraId(cameraId);
}
}
if (intent.hasExtra(Intents.Scan.TORCH_ENABLED)) {
if (intent.getBooleanExtra(Intents.Scan.TORCH_ENABLED, false)) {
this.setTorchOn();
}
}
String customPromptMessage = intent.getStringExtra(Intents.Scan.PROMPT_MESSAGE);
if (customPromptMessage != null) {
setStatusText(customPromptMessage);
}
// Check what type of scan. Default: normal scan
int scanType = intent.getIntExtra(Intents.Scan.SCAN_TYPE, 0);
String characterSet = intent.getStringExtra(Intents.Scan.CHARACTER_SET);
MultiFormatReader reader = new MultiFormatReader();
reader.setHints(decodeHints);
barcodeView.setCameraSettings(settings);
barcodeView.setDecoderFactory(new DefaultDecoderFactory(decodeFormats, decodeHints, characterSet, scanType));
}
public void setCameraSettings(CameraSettings cameraSettings) {
barcodeView.setCameraSettings(cameraSettings);
}
public void setDecoderFactory(DecoderFactory decoderFactory) {
barcodeView.setDecoderFactory(decoderFactory);
}
public DecoderFactory getDecoderFactory() {
return barcodeView.getDecoderFactory();
}
public CameraSettings getCameraSettings() {
return barcodeView.getCameraSettings();
}
public void setStatusText(String text) {
// statusView is optional when using a custom layout
if (statusView != null) {
statusView.setText(text);
}
}
/**
* @see BarcodeView#pause()
*/
public void pause() {
barcodeView.pause();
}
/**
* @see BarcodeView#pauseAndWait()
*/
public void pauseAndWait() {
barcodeView.pauseAndWait();
}
/**
* @see BarcodeView#resume()
*/
public void resume() {
barcodeView.resume();
}
public BarcodeView getBarcodeView() {
return findViewById(R.id.zxing_barcode_surface);
}
public ViewfinderView getViewFinder() {
return viewFinder;
}
public TextView getStatusView() {
return statusView;
}
/**
* @see BarcodeView#decodeSingle(BarcodeCallback)
*/
public void decodeSingle(BarcodeCallback callback) {
barcodeView.decodeSingle(new WrappedCallback(callback));
}
/**
* @see BarcodeView#decodeContinuous(BarcodeCallback)
*/
public void decodeContinuous(BarcodeCallback callback) {
barcodeView.decodeContinuous(new WrappedCallback(callback));
}
/**
* Turn on the device's flashlight.
*/
public void setTorchOn() {
barcodeView.setTorch(true);
if (torchListener != null) {
torchListener.onTorchOn();
}
}
/**
* Turn off the device's flashlight.
*/
public void setTorchOff() {
barcodeView.setTorch(false);
if (torchListener != null) {
torchListener.onTorchOff();
}
}
/**
* Changes the settings for Camera.
* Must be called after {@link #resume()}.
*
* @param callback {@link CameraParametersCallback}
*/
public void changeCameraParameters(CameraParametersCallback callback) {
barcodeView.changeCameraParameters(callback);
}
/**
* Handles focus, camera, volume up and volume down keys.
*
* Note that this view is not usually focused, so the Activity should call this directly.
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_FOCUS:
case KeyEvent.KEYCODE_CAMERA:
// Handle these events so they don't launch the Camera app
return true;
// Use volume up/down to turn on light
case KeyEvent.KEYCODE_VOLUME_DOWN:
setTorchOff();
return true;
case KeyEvent.KEYCODE_VOLUME_UP:
setTorchOn();
return true;
}
return super.onKeyDown(keyCode, event);
}
public void setTorchListener(TorchListener listener) {
this.torchListener = listener;
}
/**
* The Listener to torch/fflashlight events (turn on, turn off).
*/
public interface TorchListener {
void onTorchOn();
void onTorchOff();
}
}
@@ -15,14 +15,22 @@ public class DefaultDecoderFactory implements DecoderFactory {
private Collection<BarcodeFormat> decodeFormats;
private Map<DecodeHintType, ?> hints;
private String characterSet;
private int scanType;
public DefaultDecoderFactory() {
}
public DefaultDecoderFactory(Collection<BarcodeFormat> decodeFormats, Map<DecodeHintType, ?> hints, String characterSet) {
public DefaultDecoderFactory(Collection<BarcodeFormat> decodeFormats) {
this.decodeFormats = decodeFormats;
}
public DefaultDecoderFactory(Collection<BarcodeFormat> decodeFormats, Map<DecodeHintType, ?> hints, String characterSet, int scanType) {
this.decodeFormats = decodeFormats;
this.hints = hints;
this.characterSet = characterSet;
this.scanType = scanType;
}
@Override
@@ -31,11 +39,11 @@ public class DefaultDecoderFactory implements DecoderFactory {
hints.putAll(baseHints);
if(this.hints != null) {
if (this.hints != null) {
hints.putAll(this.hints);
}
if(this.decodeFormats != null) {
if (this.decodeFormats != null) {
hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
}
@@ -46,6 +54,19 @@ public class DefaultDecoderFactory implements DecoderFactory {
MultiFormatReader reader = new MultiFormatReader();
reader.setHints(hints);
return new Decoder(reader);
switch (scanType){
case 0:
return new Decoder(reader);
case 1:
return new InvertedDecoder(reader);
case 2:
return new MixedDecoder(reader);
default:
return new Decoder(reader);
}
}
}
@@ -0,0 +1,36 @@
package com.journeyapps.barcodescanner;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.LuminanceSource;
import com.google.zxing.Reader;
import com.google.zxing.common.HybridBinarizer;
/**
* Created by leighmd on 11/2/16.
*/
public class InvertedDecoder extends Decoder {
/**
* Create a new Decoder with the specified Reader.
* <p/>
* It is recommended to use an instance of MultiFormatReader in most cases.
*
* @param reader the reader
*/
public InvertedDecoder(Reader reader) {
super(reader);
}
/**
* Given an image source, convert to a binary bitmap.
*
* Override this to use a custom binarizer.
*
* @param source the image source
* @return a BinaryBitmap
*/
protected BinaryBitmap toBitmap(LuminanceSource source) {
return new BinaryBitmap(new HybridBinarizer(source.invert()));
}
}
@@ -0,0 +1,43 @@
package com.journeyapps.barcodescanner;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.LuminanceSource;
import com.google.zxing.Reader;
import com.google.zxing.common.HybridBinarizer;
/**
* Decoder that performs alternating scans in normal mode and inverted mode.
*/
public class MixedDecoder extends Decoder {
private boolean isInverted = true;
/**
* Create a new Decoder with the specified Reader.
* <p/>
* It is recommended to use an instance of MultiFormatReader in most cases.
*
* @param reader the reader
*/
public MixedDecoder(Reader reader) {
super(reader);
}
/**
* Given an image source, convert to a binary bitmap.
*
* Override this to use a custom binarizer.
*
* @param source the image source
* @return a BinaryBitmap
*/
protected BinaryBitmap toBitmap(LuminanceSource source) {
if (isInverted) {
isInverted = false;
return new BinaryBitmap(new HybridBinarizer(source.invert()));
} else {
isInverted = true;
return new BinaryBitmap(new HybridBinarizer(source));
}
}
}
@@ -0,0 +1,142 @@
package com.journeyapps.barcodescanner;
import android.graphics.Rect;
public class RawImageData {
private byte[] data;
private int width;
private int height;
public RawImageData(byte[] data, int width, int height) {
this.data = data;
this.width = width;
this.height = height;
}
public byte[] getData() {
return data;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public RawImageData cropAndScale(Rect cropRect, int scale) {
int width = cropRect.width() / scale;
int height = cropRect.height() / scale;
int top = cropRect.top;
int area = width * height;
byte[] matrix = new byte[area];
if (scale == 1) {
int inputOffset = top * this.width + cropRect.left;
// Copy one cropped row at a time.
for (int y = 0; y < height; y++) {
int outputOffset = y * width;
System.arraycopy(this.data, inputOffset, matrix, outputOffset, width);
inputOffset += this.width;
}
} else {
int inputOffset = top * this.width + cropRect.left;
// Copy one cropped row at a time.
for (int y = 0; y < height; y++) {
int outputOffset = y * width;
int xOffset = inputOffset;
for (int x = 0; x < width; x++) {
matrix[outputOffset] = this.data[xOffset];
xOffset += scale;
outputOffset += 1;
}
inputOffset += this.width * scale;
}
}
return new RawImageData(matrix, width, height);
}
public RawImageData rotateCameraPreview(int cameraRotation) {
switch (cameraRotation) {
case 90:
return new RawImageData(rotateCW(data, this.width, this.height), this.height, this.width);
case 180:
return new RawImageData(rotate180(data, this.width, this.height), this.width, this.height);
case 270:
return new RawImageData(rotateCCW(data, this.width, this.height), this.height, this.width);
case 0:
default:
return this;
}
}
/**
* Rotate an image by 90 degrees CW.
*
* @param data the image data, in with the first width * height bytes being the luminance data.
* @param imageWidth the width of the image
* @param imageHeight the height of the image
* @return the rotated bytes
*/
public static byte[] rotateCW(byte[] data, int imageWidth, int imageHeight) {
// Adapted from http://stackoverflow.com/a/15775173
// data may contain more than just y (u and v), but we are only interested in the y section.
byte[] yuv = new byte[imageWidth * imageHeight];
int i = 0;
for (int x = 0; x < imageWidth; x++) {
for (int y = imageHeight - 1; y >= 0; y--) {
yuv[i] = data[y * imageWidth + x];
i++;
}
}
return yuv;
}
/**
* Rotate an image by 180 degrees.
*
* @param data the image data, in with the first width * height bytes being the luminance data.
* @param imageWidth the width of the image
* @param imageHeight the height of the image
* @return the rotated bytes
*/
public static byte[] rotate180(byte[] data, int imageWidth, int imageHeight) {
int n = imageWidth * imageHeight;
byte[] yuv = new byte[n];
int i = n - 1;
for (int j = 0; j < n; j++) {
yuv[i] = data[j];
i--;
}
return yuv;
}
/**
* Rotate an image by 90 degrees CCW.
*
* @param data the image data, in with the first width * height bytes being the luminance data.
* @param imageWidth the width of the image
* @param imageHeight the height of the image
* @return the rotated bytes
*/
public static byte[] rotateCCW(byte[] data, int imageWidth, int imageHeight) {
int n = imageWidth * imageHeight;
byte[] yuv = new byte[n];
int i = n - 1;
for (int x = 0; x < imageWidth; x++) {
for (int y = imageHeight - 1; y >= 0; y--) {
yuv[i] = data[y * imageWidth + x];
i--;
}
}
return yuv;
}
}
@@ -2,7 +2,6 @@ package com.journeyapps.barcodescanner;
import android.content.Context;
import android.hardware.SensorManager;
import android.util.Log;
import android.view.OrientationEventListener;
import android.view.WindowManager;
@@ -23,7 +22,6 @@ public class RotationListener {
public RotationListener() {
}
public void listen(Context context, RotationCallback callback) {
// Stop to make sure we're not registering the listening twice.
stop();
@@ -42,7 +40,7 @@ public class RotationListener {
public void onOrientationChanged(int orientation) {
WindowManager localWindowManager = windowManager;
RotationCallback localCallback = RotationListener.this.callback;
if(windowManager != null && localCallback != null) {
if (windowManager != null && localCallback != null) {
int newRotation = localWindowManager.getDefaultDisplay().getRotation();
if (newRotation != lastRotation) {
lastRotation = newRotation;
@@ -59,7 +57,7 @@ public class RotationListener {
public void stop() {
// To reduce the effect of possible leaks, we clear any references we have to external
// objects.
if(this.orientationEventListener != null) {
if (this.orientationEventListener != null) {
this.orientationEventListener.disable();
}
this.orientationEventListener = null;
@@ -1,5 +1,7 @@
package com.journeyapps.barcodescanner;
import androidx.annotation.NonNull;
/**
*
*/
@@ -33,6 +35,39 @@ public class Size implements Comparable<Size> {
return new Size(width * n / d, height * n / d);
}
/**
* Scales the dimensions so that it fits entirely inside the parent.One of width or height will
* fit exactly. Aspect ratio is preserved.
*
* @param into the parent to fit into
* @return the scaled size
*/
public Size scaleFit(Size into) {
if (width * into.height >= into.width * height) {
// match width
return new Size(into.width, height * into.width / width);
} else {
// match height
return new Size(width * into.height / height, into.height);
}
}
/**
* Scales the size so that both dimensions will be greater than or equal to the corresponding
* dimension of the parent. One of width or height will fit exactly. Aspect ratio is preserved.
*
* @param into the parent to fit into
* @return the scaled size
*/
public Size scaleCrop(Size into) {
if (width * into.height <= into.width * height) {
// match width
return new Size(into.width, height * into.width / width);
} else {
// match height
return new Size(width * into.height / height, into.height);
}
}
/**
* Checks if both dimensions of the other size are at least as large as this size.
*
@@ -47,7 +82,7 @@ public class Size implements Comparable<Size> {
* Default sort order is ascending by size.
*/
@Override
public int compareTo(Size other) {
public int compareTo(@NonNull Size other) {
int aPixels = this.height * this.width;
int bPixels = other.height * other.width;
if (bPixels < aPixels) {
@@ -70,9 +105,7 @@ public class Size implements Comparable<Size> {
Size size = (Size) o;
if (width != size.width) return false;
return height == size.height;
return width == size.width && height == size.height;
}
@Override
@@ -7,6 +7,7 @@ import android.graphics.Rect;
import android.graphics.YuvImage;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.ResultPoint;
import java.io.ByteArrayOutputStream;
@@ -14,14 +15,7 @@ import java.io.ByteArrayOutputStream;
* Raw preview data from a camera.
*/
public class SourceData {
/** Raw YUV data */
private byte[] data;
/** Source data width */
private int dataWidth;
/** Source data height */
private int dataHeight;
private RawImageData data;
/** The format of the image data. ImageFormat.NV21 and ImageFormat.YUY2 are supported. */
private int imageFormat;
@@ -32,6 +26,13 @@ public class SourceData {
/** Crop rectangle, in display orientation. */
private Rect cropRect;
/**
* Factor by which to scale down before decoding.
*/
private int scalingFactor = 1;
private boolean previewMirrored;
/**
*
* @param data the image data
@@ -41,11 +42,12 @@ public class SourceData {
* @param rotation camera rotation relative to display rotation, in degrees (0, 90, 180 or 270).
*/
public SourceData(byte[] data, int dataWidth, int dataHeight, int imageFormat, int rotation) {
this.data = data;
this.dataWidth = dataWidth;
this.dataHeight = dataHeight;
this.data = new RawImageData(data, dataWidth, dataHeight);
this.rotation = rotation;
this.imageFormat = imageFormat;
if (dataWidth * dataHeight > data.length) {
throw new IllegalArgumentException("Image data does not match the resolution. " + dataWidth + "x" + dataHeight + " > " + data.length);
}
}
public Rect getCropRect() {
@@ -61,8 +63,24 @@ public class SourceData {
this.cropRect = cropRect;
}
public boolean isPreviewMirrored() {
return previewMirrored;
}
public void setPreviewMirrored(boolean previewMirrored) {
this.previewMirrored = previewMirrored;
}
public int getScalingFactor() {
return scalingFactor;
}
public void setScalingFactor(int scalingFactor) {
this.scalingFactor = scalingFactor;
}
public byte[] getData() {
return data;
return data.getData();
}
/**
@@ -70,7 +88,7 @@ public class SourceData {
* @return width of the data
*/
public int getDataWidth() {
return dataWidth;
return data.getWidth();
}
/**
@@ -78,7 +96,16 @@ public class SourceData {
* @return height of the data
*/
public int getDataHeight() {
return dataHeight;
return data.getHeight();
}
public ResultPoint translateResultPoint(ResultPoint point) {
float x = point.getX() * this.scalingFactor + this.cropRect.left;
float y = point.getY() * this.scalingFactor + this.cropRect.top;
if (previewMirrored) {
x = data.getWidth() - x;
}
return new ResultPoint(x, y);
}
/**
@@ -94,15 +121,11 @@ public class SourceData {
}
public PlanarYUVLuminanceSource createSource() {
byte[] rotated = rotateCameraPreview(rotation, data, dataWidth, dataHeight);
// TODO: handle mirrored (front) camera. Probably only the ResultPoints should be mirrored,
RawImageData rotated = this.data.rotateCameraPreview(rotation);
RawImageData scaled = rotated.cropAndScale(this.cropRect, this.scalingFactor);
// not the preview for decoding.
if (isRotated()) {
//noinspection SuspiciousNameCombination
return new PlanarYUVLuminanceSource(rotated, dataHeight, dataWidth, cropRect.left, cropRect.top, cropRect.width(), cropRect.height(), false);
} else {
return new PlanarYUVLuminanceSource(rotated, dataWidth, dataHeight, cropRect.left, cropRect.top, cropRect.width(), cropRect.height(), false);
}
return new PlanarYUVLuminanceSource(scaled.getData(), scaled.getWidth(), scaled.getHeight(), 0, 0, scaled.getWidth(), scaled.getHeight(), false);
}
/**
@@ -124,14 +147,16 @@ public class SourceData {
return getBitmap(cropRect, scaleFactor);
}
private Bitmap getBitmap(Rect cropRect, int scaleFactor) {
if(isRotated()) {
public Bitmap getBitmap(Rect cropRect, int scaleFactor) {
if (cropRect == null) {
cropRect = new Rect(0, 0, data.getWidth(), data.getHeight());
} else if (isRotated()) {
//noinspection SuspiciousNameCombination
cropRect = new Rect(cropRect.top, cropRect.left, cropRect.bottom, cropRect.right);
}
// TODO: there should be a way to do this without JPEG compression / decompression cycle.
YuvImage img = new YuvImage(data, imageFormat, dataWidth, dataHeight, null);
YuvImage img = new YuvImage(data.getData(), imageFormat, data.getWidth(), data.getHeight(), null);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
img.compressToJpeg(cropRect, 90, buffer);
byte[] jpegData = buffer.toByteArray();
@@ -150,84 +175,4 @@ public class SourceData {
return bitmap;
}
public static byte[] rotateCameraPreview(int cameraRotation, byte[] data, int imageWidth, int imageHeight) {
switch (cameraRotation) {
case 0:
return data;
case 90:
return rotateCW(data, imageWidth, imageHeight);
case 180:
return rotate180(data, imageWidth, imageHeight);
case 270:
return rotateCCW(data, imageWidth, imageHeight);
default:
// Should not happen
return data;
}
}
/**
* Rotate an image by 90 degrees CW.
*
* @param data the image data, in with the first width * height bytes being the luminance data.
* @param imageWidth the width of the image
* @param imageHeight the height of the image
* @return the rotated bytes
*/
public static byte[] rotateCW(byte[] data, int imageWidth, int imageHeight) {
// Adapted from http://stackoverflow.com/a/15775173
// data may contain more than just y (u and v), but we are only interested in the y section.
byte[] yuv = new byte[imageWidth * imageHeight];
int i = 0;
for (int x = 0; x < imageWidth; x++) {
for (int y = imageHeight - 1; y >= 0; y--) {
yuv[i] = data[y * imageWidth + x];
i++;
}
}
return yuv;
}
/**
* Rotate an image by 180 degrees.
*
* @param data the image data, in with the first width * height bytes being the luminance data.
* @param imageWidth the width of the image
* @param imageHeight the height of the image
* @return the rotated bytes
*/
public static byte[] rotate180(byte[] data, int imageWidth, int imageHeight) {
int n = imageWidth * imageHeight;
byte[] yuv = new byte[n];
int i = n - 1;
for (int j = 0; j < n; j++) {
yuv[i] = data[j];
i--;
}
return yuv;
}
/**
* Rotate an image by 90 degrees CCW.
*
* @param data the image data, in with the first width * height bytes being the luminance data.
* @param imageWidth the width of the image
* @param imageHeight the height of the image
* @return the rotated bytes
*/
public static byte[] rotateCCW(byte[] data, int imageWidth, int imageHeight) {
int n = imageWidth * imageHeight;
byte[] yuv = new byte[n];
int i = n - 1;
for (int x = 0; x < imageWidth; x++) {
for (int y = imageHeight - 1; y >= 0; y--) {
yuv[i] = data[y * imageWidth + x];
i--;
}
}
return yuv;
}
}
@@ -39,25 +39,31 @@ import java.util.List;
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class ViewfinderView extends View {
private static final String TAG = ViewfinderView.class.getSimpleName();
public class ViewfinderView extends View {
protected static final String TAG = ViewfinderView.class.getSimpleName();
private static final int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64};
private static final long ANIMATION_DELAY = 80L;
private static final int CURRENT_POINT_OPACITY = 0xA0;
private static final int MAX_RESULT_POINTS = 20;
private static final int POINT_SIZE = 6;
protected static final int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64};
protected static final long ANIMATION_DELAY = 80L;
protected static final int CURRENT_POINT_OPACITY = 0xA0;
protected static final int MAX_RESULT_POINTS = 20;
protected static final int POINT_SIZE = 6;
private final Paint paint;
private Bitmap resultBitmap;
private final int maskColor;
private final int resultColor;
private final int laserColor;
private final int resultPointColor;
private int scannerAlpha;
private List<ResultPoint> possibleResultPoints;
private List<ResultPoint> lastPossibleResultPoints;
private CameraPreview cameraPreview;
protected final Paint paint;
protected Bitmap resultBitmap;
protected int maskColor;
protected final int resultColor;
protected final int laserColor;
protected final int resultPointColor;
protected boolean laserVisibility;
protected int scannerAlpha;
protected List<ResultPoint> possibleResultPoints;
protected List<ResultPoint> lastPossibleResultPoints;
protected CameraPreview cameraPreview;
// Cache the framingRect and previewSize, so that we can still draw it after the preview
// stopped.
protected Rect framingRect;
protected Size previewSize;
// This constructor is used when the class is built from an XML resource.
public ViewfinderView(Context context, AttributeSet attrs) {
@@ -79,12 +85,14 @@ public final class ViewfinderView extends View {
resources.getColor(R.color.zxing_viewfinder_laser));
this.resultPointColor = attributes.getColor(R.styleable.zxing_finder_zxing_possible_result_points,
resources.getColor(R.color.zxing_possible_result_points));
this.laserVisibility = attributes.getBoolean(R.styleable.zxing_finder_zxing_viewfinder_laser_visibility,
true);
attributes.recycle();
scannerAlpha = 0;
possibleResultPoints = new ArrayList<>(5);
lastPossibleResultPoints = null;
possibleResultPoints = new ArrayList<>(MAX_RESULT_POINTS);
lastPossibleResultPoints = new ArrayList<>(MAX_RESULT_POINTS);
}
public void setCameraPreview(CameraPreview view) {
@@ -92,6 +100,7 @@ public final class ViewfinderView extends View {
view.addStateListener(new CameraPreview.StateListener() {
@Override
public void previewSized() {
refreshSizes();
invalidate();
}
@@ -109,22 +118,38 @@ public final class ViewfinderView extends View {
public void cameraError(Exception error) {
}
@Override
public void cameraClosed() {
}
});
}
protected void refreshSizes() {
if (cameraPreview == null) {
return;
}
Rect framingRect = cameraPreview.getFramingRect();
Size previewSize = cameraPreview.getPreviewSize();
if (framingRect != null && previewSize != null) {
this.framingRect = framingRect;
this.previewSize = previewSize;
}
}
@SuppressLint("DrawAllocation")
@Override
public void onDraw(Canvas canvas) {
if (cameraPreview == null || cameraPreview.getPreviewFramingRect() == null || cameraPreview.getFramingRect() == null) {
refreshSizes();
if (framingRect == null || previewSize == null) {
return;
}
Rect frame = cameraPreview.getFramingRect();
Rect previewFrame = cameraPreview.getPreviewFramingRect();
final Rect frame = framingRect;
final Size previewSize = this.previewSize;
int width = canvas.getWidth();
int height = canvas.getHeight();
final int width = canvas.getWidth();
final int height = canvas.getHeight();
// Draw the exterior (i.e. outside the framing rect) darkened
paint.setColor(resultBitmap != null ? resultColor : maskColor);
@@ -138,43 +163,52 @@ public final class ViewfinderView extends View {
paint.setAlpha(CURRENT_POINT_OPACITY);
canvas.drawBitmap(resultBitmap, null, frame, paint);
} else {
// If wanted, draw a red "laser scanner" line through the middle to show decoding is active
if (laserVisibility) {
paint.setColor(laserColor);
// Draw a red "laser scanner" line through the middle to show decoding is active
paint.setColor(laserColor);
paint.setAlpha(SCANNER_ALPHA[scannerAlpha]);
scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
int middle = frame.height() / 2 + frame.top;
canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1, middle + 2, paint);
paint.setAlpha(SCANNER_ALPHA[scannerAlpha]);
scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
float scaleX = frame.width() / (float) previewFrame.width();
float scaleY = frame.height() / (float) previewFrame.height();
List<ResultPoint> currentPossible = possibleResultPoints;
List<ResultPoint> currentLast = lastPossibleResultPoints;
int frameLeft = frame.left;
int frameTop = frame.top;
if (currentPossible.isEmpty()) {
lastPossibleResultPoints = null;
} else {
possibleResultPoints = new ArrayList<>(5);
lastPossibleResultPoints = currentPossible;
paint.setAlpha(CURRENT_POINT_OPACITY);
paint.setColor(resultPointColor);
for (ResultPoint point : currentPossible) {
canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX),
frameTop + (int) (point.getY() * scaleY),
POINT_SIZE, paint);
}
final int middle = frame.height() / 2 + frame.top;
canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1, middle + 2, paint);
}
if (currentLast != null) {
final float scaleX = this.getWidth() / (float) previewSize.width;
final float scaleY = this.getHeight() / (float) previewSize.height;
// draw the last possible result points
if (!lastPossibleResultPoints.isEmpty()) {
paint.setAlpha(CURRENT_POINT_OPACITY / 2);
paint.setColor(resultPointColor);
float radius = POINT_SIZE / 2.0f;
for (ResultPoint point : currentLast) {
canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX),
frameTop + (int) (point.getY() * scaleY),
radius, paint);
for (final ResultPoint point : lastPossibleResultPoints) {
canvas.drawCircle(
(int) (point.getX() * scaleX),
(int) (point.getY() * scaleY),
radius, paint
);
}
lastPossibleResultPoints.clear();
}
// draw current possible result points
if (!possibleResultPoints.isEmpty()) {
paint.setAlpha(CURRENT_POINT_OPACITY);
paint.setColor(resultPointColor);
for (final ResultPoint point : possibleResultPoints) {
canvas.drawCircle(
(int) (point.getX() * scaleX),
(int) (point.getY() * scaleY),
POINT_SIZE, paint
);
}
// swap and clear buffers
final List<ResultPoint> temp = possibleResultPoints;
possibleResultPoints = lastPossibleResultPoints;
lastPossibleResultPoints = temp;
possibleResultPoints.clear();
}
// Request another update at the animation interval, but only repaint the laser line,
@@ -212,13 +246,15 @@ public final class ViewfinderView extends View {
* @param point a point to draw, relative to the preview frame
*/
public void addPossibleResultPoint(ResultPoint point) {
List<ResultPoint> points = possibleResultPoints;
points.add(point);
int size = points.size();
if (size > MAX_RESULT_POINTS) {
// trim it
points.subList(0, size - MAX_RESULT_POINTS / 2).clear();
}
if (possibleResultPoints.size() < MAX_RESULT_POINTS)
possibleResultPoints.add(point);
}
public void setMaskColor(int maskColor) {
this.maskColor = maskColor;
}
public void setLaserVisibility(boolean visible) {
this.laserVisibility = visible;
}
}
@@ -64,12 +64,9 @@ public final class AutoFocusManager {
private final Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera theCamera) {
handler.post(new Runnable() {
@Override
public void run() {
focusing = false;
autoFocusAgainLater();
}
handler.post(() -> {
focusing = false;
autoFocusAgainLater();
});
}
};
@@ -83,7 +80,6 @@ public final class AutoFocusManager {
start();
}
private synchronized void autoFocusAgainLater() {
if (!stopped && !handler.hasMessages(MESSAGE_FOCUS)) {
handler.sendMessageDelayed(handler.obtainMessage(MESSAGE_FOCUS), AUTO_FOCUS_INTERVAL_MS);
@@ -135,5 +131,4 @@ public final class AutoFocusManager {
}
}
}
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.zxing.client.android.camera;
package com.journeyapps.barcodescanner.camera;
import android.annotation.TargetApi;
import android.graphics.Rect;
@@ -22,6 +22,8 @@ import android.hardware.Camera;
import android.os.Build;
import android.util.Log;
import com.journeyapps.barcodescanner.camera.CameraSettings;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@@ -50,24 +52,31 @@ public final class CameraConfigurationUtils {
}
public static void setFocus(Camera.Parameters parameters,
boolean autoFocus,
boolean disableContinuous,
CameraSettings.FocusMode focusModeSetting,
boolean safeMode) {
List<String> supportedFocusModes = parameters.getSupportedFocusModes();
String focusMode = null;
if (autoFocus) {
if (safeMode || disableContinuous) {
focusMode = findSettableValue("focus mode",
supportedFocusModes,
Camera.Parameters.FOCUS_MODE_AUTO);
} else {
focusMode = findSettableValue("focus mode",
supportedFocusModes,
Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,
Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO,
Camera.Parameters.FOCUS_MODE_AUTO);
}
}
if (safeMode || focusModeSetting == CameraSettings.FocusMode.AUTO) {
focusMode = findSettableValue("focus mode",
supportedFocusModes,
Camera.Parameters.FOCUS_MODE_AUTO);
} else if (focusModeSetting == CameraSettings.FocusMode.CONTINUOUS) {
focusMode = findSettableValue("focus mode",
supportedFocusModes,
Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,
Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO,
Camera.Parameters.FOCUS_MODE_AUTO);
} else if (focusModeSetting == CameraSettings.FocusMode.INFINITY) {
focusMode = findSettableValue("focus mode",
supportedFocusModes,
Camera.Parameters.FOCUS_MODE_INFINITY);
} else if (focusModeSetting == CameraSettings.FocusMode.MACRO) {
focusMode = findSettableValue("focus mode",
supportedFocusModes,
Camera.Parameters.FOCUS_MODE_MACRO);
}
// Maybe selected auto-focus but not available, so fall through here:
if (!safeMode && focusMode == null) {
focusMode = findSettableValue("focus mode",
@@ -354,5 +363,4 @@ public final class CameraConfigurationUtils {
return result.toString();
}
}
@@ -7,29 +7,53 @@ import android.view.SurfaceHolder;
import com.google.zxing.client.android.R;
import com.journeyapps.barcodescanner.Size;
import com.journeyapps.barcodescanner.SourceData;
import com.journeyapps.barcodescanner.Util;
/**
* Manage a camera instance using a background thread.
*
* All methods must be called from the main thread.
*/
public class CameraInstance {
private static final String TAG = CameraInstance.class.getSimpleName();
private CameraThread cameraThread;
private SurfaceHolder surfaceHolder;
private CameraSurface surface;
private CameraManager cameraManager;
private Handler readyHandler;
private DisplayConfiguration displayConfiguration;
private boolean open = false;
private boolean cameraClosed = true;
private Handler mainHandler;
private CameraSettings cameraSettings = new CameraSettings();
/**
* Construct a new CameraInstance.
*
* A new CameraManager is created.
*
* @param context the Android Context
*/
public CameraInstance(Context context) {
Util.validateMainThread();
this.cameraThread = CameraThread.getInstance();
this.cameraManager = new CameraManager(context);
this.cameraManager.setCameraSettings(cameraSettings);
this.mainHandler = new Handler();
}
/**
* Construct a new CameraInstance with a specific CameraManager.
*
* @param cameraManager the CameraManager to use
*/
public CameraInstance(CameraManager cameraManager) {
Util.validateMainThread();
this.cameraManager = cameraManager;
}
public void setDisplayConfiguration(DisplayConfiguration configuration) {
@@ -46,7 +70,11 @@ public class CameraInstance {
}
public void setSurfaceHolder(SurfaceHolder surfaceHolder) {
this.surfaceHolder = surfaceHolder;
setSurface(new CameraSurface(surfaceHolder));
}
public void setSurface(CameraSurface surface) {
this.surface = surface;
}
public CameraSettings getCameraSettings() {
@@ -87,6 +115,7 @@ public class CameraInstance {
Util.validateMainThread();
open = true;
cameraClosed = false;
cameraThread.incrementAndEnqueue(opener);
}
@@ -109,12 +138,20 @@ public class CameraInstance {
Util.validateMainThread();
if (open) {
cameraThread.enqueue(new Runnable() {
@Override
public void run() {
cameraManager.setTorch(on);
}
});
cameraThread.enqueue(() -> cameraManager.setTorch(on));
}
}
/**
* Changes the settings for Camera.
*
* @param callback {@link CameraParametersCallback}
*/
public void changeCameraParameters(final CameraParametersCallback callback) {
Util.validateMainThread();
if (open) {
cameraThread.enqueue(() -> cameraManager.changeCameraParameters(callback));
}
}
@@ -123,6 +160,8 @@ public class CameraInstance {
if (open) {
cameraThread.enqueue(closer);
} else {
cameraClosed = true;
}
open = false;
@@ -132,14 +171,18 @@ public class CameraInstance {
return open;
}
public void requestPreview(final PreviewCallback callback) {
validateOpen();
public boolean isCameraClosed() {
return cameraClosed;
}
cameraThread.enqueue(new Runnable() {
@Override
public void run() {
cameraManager.requestPreviewFrame(callback);
public void requestPreview(final PreviewCallback callback) {
mainHandler.post(() -> {
if (!open) {
Log.d(TAG, "Camera is closed, not requesting preview");
return;
}
cameraThread.enqueue(() -> cameraManager.requestPreviewFrame(callback));
});
}
@@ -149,7 +192,6 @@ public class CameraInstance {
}
}
private Runnable opener = new Runnable() {
@Override
public void run() {
@@ -184,7 +226,7 @@ public class CameraInstance {
public void run() {
try {
Log.d(TAG, "Starting preview");
cameraManager.setPreviewDisplay(surfaceHolder);
cameraManager.setPreviewDisplay(surface);
cameraManager.startPreview();
} catch (Exception e) {
notifyError(e);
@@ -204,6 +246,10 @@ public class CameraInstance {
Log.e(TAG, "Failed to close camera", e);
}
cameraClosed = true;
readyHandler.sendEmptyMessage(R.id.zxing_camera_closed);
cameraThread.decrementInstances();
}
};
@@ -213,4 +259,31 @@ public class CameraInstance {
readyHandler.obtainMessage(R.id.zxing_camera_error, error).sendToTarget();
}
}
/**
* Returns the CameraManager used to control the camera.
*
* The CameraManager is not thread-safe, and must only be used from the CameraThread.
*
* @return the CameraManager used
*/
protected CameraManager getCameraManager() {
return cameraManager;
}
/**
*
* @return the CameraThread used to manage the camera
*/
protected CameraThread getCameraThread() {
return cameraThread;
}
/**
*
* @return the surface om which the preview is displayed
*/
protected CameraSurface getSurface() {
return surface;
}
}
@@ -19,14 +19,11 @@ package com.journeyapps.barcodescanner.camera;
import android.content.Context;
import android.hardware.Camera;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import com.google.zxing.client.android.AmbientLightManager;
import com.google.zxing.client.android.camera.CameraConfigurationUtils;
import com.google.zxing.client.android.camera.open.OpenCameraInterface;
import com.journeyapps.barcodescanner.Size;
import com.journeyapps.barcodescanner.SourceData;
@@ -36,6 +33,10 @@ import java.util.ArrayList;
import java.util.List;
/**
* Wrapper to manage the Camera. This is not thread-safe, and the methods must always be called
* from the same thread.
*
*
* Call order:
*
* 1. setCameraSettings()
@@ -94,11 +95,30 @@ public final class CameraManager {
Size cameraResolution = resolution;
PreviewCallback callback = this.callback;
if (cameraResolution != null && callback != null) {
int format = camera.getParameters().getPreviewFormat();
SourceData source = new SourceData(data, cameraResolution.width, cameraResolution.height, format, getCameraRotation());
callback.onPreview(source);
try {
if (data == null) {
throw new NullPointerException("No preview data received");
}
int format = camera.getParameters().getPreviewFormat();
SourceData source = new SourceData(data, cameraResolution.width, cameraResolution.height, format, getCameraRotation());
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
source.setPreviewMirrored(true);
}
callback.onPreview(source);
} catch (RuntimeException e) {
// Could be:
// java.lang.RuntimeException: getParameters failed (empty parameters)
// IllegalArgumentException: Image data does not match the resolution
Log.e(TAG, "Camera preview failed", e);
callback.onPreviewError(e);
}
} else {
Log.d(TAG, "Got preview callback, but no handler or resolution available");
if (callback != null) {
// Should generally not happen
callback.onPreviewError(new Exception("No resolution available"));
}
}
}
}
@@ -136,6 +156,9 @@ public final class CameraManager {
* Must be called from camera thread.
*/
public void configure() {
if (camera == null) {
throw new RuntimeException("Camera not open");
}
setParameters();
}
@@ -143,7 +166,11 @@ public final class CameraManager {
* Must be called from camera thread.
*/
public void setPreviewDisplay(SurfaceHolder holder) throws IOException {
camera.setPreviewDisplay(holder);
setPreviewDisplay(new CameraSurface(holder));
}
public void setPreviewDisplay(CameraSurface surface) throws IOException {
surface.setPreview(camera);
}
/**
@@ -183,7 +210,6 @@ public final class CameraManager {
}
}
/**
* Closes the camera driver if still in use.
*
@@ -200,7 +226,7 @@ public final class CameraManager {
* @return true if the camera rotation is perpendicular to the current display rotation.
*/
public boolean isCameraRotated() {
if(rotationDegrees == -1) {
if (rotationDegrees == -1) {
throw new IllegalStateException("Rotation not calculated yet. Call configure() first.");
}
return rotationDegrees % 180 != 0;
@@ -215,7 +241,6 @@ public final class CameraManager {
return rotationDegrees;
}
private Camera.Parameters getDefaultCameraParameters() {
Camera.Parameters parameters = camera.getParameters();
if (defaultParameters == null) {
@@ -241,8 +266,7 @@ public final class CameraManager {
Log.w(TAG, "In camera config safe mode -- most settings will not be honored");
}
CameraConfigurationUtils.setFocus(parameters, settings.isAutoFocusEnabled(), !settings.isContinuousFocusEnabled(), safeMode);
CameraConfigurationUtils.setFocus(parameters, settings.getFocusMode(), safeMode);
if (!safeMode) {
CameraConfigurationUtils.setTorch(parameters, false);
@@ -274,6 +298,12 @@ public final class CameraManager {
parameters.setPreviewSize(requestedPreviewSize.width, requestedPreviewSize.height);
}
if (Build.DEVICE.equals("glass-1")) {
// We need to set the FPS on Google Glass devices, otherwise the preview is scrambled.
// FIXME - can/should we do this for other devices as well?
CameraConfigurationUtils.setBestPreviewFPS(parameters);
}
Log.i(TAG, "Final camera parameters: " + parameters.flatten());
camera.setParameters(parameters);
@@ -285,7 +315,7 @@ public final class CameraManager {
if (rawSupportedSizes == null) {
Camera.Size defaultSize = parameters.getPreviewSize();
if (defaultSize != null) {
// Work around potential platform bugs
Size previewSize = new Size(defaultSize.width, defaultSize.height);
previewSizes.add(new Size(defaultSize.width, defaultSize.height));
}
return previewSizes;
@@ -330,7 +360,6 @@ public final class CameraManager {
camera.setDisplayOrientation(rotation);
}
private void setParameters() {
try {
this.rotationDegrees = calculateDisplayRotation();
@@ -343,7 +372,7 @@ public final class CameraManager {
} catch (Exception e) {
// Failed, use safe mode
try {
setDesiredParameters(false);
setDesiredParameters(true);
} catch (Exception e2) {
// Well, darn. Give up
Log.w(TAG, "Camera rejected even safe-mode parameters! No configuration");
@@ -359,7 +388,10 @@ public final class CameraManager {
cameraPreviewCallback.setResolution(previewSize);
}
/**
* This returns false if the camera is not opened yet, failed to open, or has
* been closed.
*/
public boolean isOpen() {
return camera != null;
}
@@ -422,26 +454,52 @@ public final class CameraManager {
public void setTorch(boolean on) {
if (camera != null) {
boolean isOn = isTorchOn();
if (on != isOn) {
if (autoFocusManager != null) {
autoFocusManager.stop();
}
try {
boolean isOn = isTorchOn();
if (on != isOn) {
if (autoFocusManager != null) {
autoFocusManager.stop();
}
Camera.Parameters parameters = camera.getParameters();
CameraConfigurationUtils.setTorch(parameters, on);
if (settings.isExposureEnabled()) {
CameraConfigurationUtils.setBestExposure(parameters, on);
}
camera.setParameters(parameters);
Camera.Parameters parameters = camera.getParameters();
CameraConfigurationUtils.setTorch(parameters, on);
if (settings.isExposureEnabled()) {
CameraConfigurationUtils.setBestExposure(parameters, on);
}
camera.setParameters(parameters);
if (autoFocusManager != null) {
autoFocusManager.start();
if (autoFocusManager != null) {
autoFocusManager.start();
}
}
} catch(RuntimeException e) {
// Camera error. Could happen if the camera is being closed.
Log.e(TAG, "Failed to set torch", e);
}
}
}
/**
* Changes the settings for Camera.
*
* @param callback {@link CameraParametersCallback}
*/
public void changeCameraParameters(CameraParametersCallback callback) {
if (camera != null) {
try {
camera.setParameters(callback.changeCameraParameters(camera.getParameters()));
} catch(RuntimeException e) {
// Camera error. Could happen if the camera is being closed.
Log.e(TAG, "Failed to change camera parameters", e);
}
}
}
/**
*
* @return true if the torch is on
* @throws RuntimeException if there is a camera error
*/
public boolean isTorchOn() {
Camera.Parameters parameters = camera.getParameters();
if (parameters != null) {
@@ -453,4 +511,14 @@ public final class CameraManager {
return false;
}
}
/**
* Returns the Camera. This returns null if the camera is not opened yet, failed to open, or has
* been closed.
*
* @return the Camera
*/
public Camera getCamera() {
return camera;
}
}
@@ -0,0 +1,17 @@
package com.journeyapps.barcodescanner.camera;
import android.hardware.Camera;
/**
* Callback for {@link Camera.Parameters}.
*/
public interface CameraParametersCallback {
/**
* Changes the settings for Camera.
*
* @param parameters {@link Camera.Parameters}.
* @return {@link Camera.Parameters} with arguments.
*/
Camera.Parameters changeCameraParameters(Camera.Parameters parameters);
}
@@ -14,13 +14,19 @@ public class CameraSettings {
private boolean continuousFocusEnabled = false;
private boolean exposureEnabled = false;
private boolean autoTorchEnabled = false;
private FocusMode focusMode = FocusMode.AUTO;
public enum FocusMode {
AUTO,
CONTINUOUS,
INFINITY,
MACRO
}
public int getRequestedCameraId() {
return requestedCameraId;
}
/**
* Allows third party apps to specify the camera ID, rather than determine
* it automatically based on available cameras and their orientation.
@@ -98,6 +104,14 @@ public class CameraSettings {
public void setAutoFocusEnabled(boolean autoFocusEnabled) {
this.autoFocusEnabled = autoFocusEnabled;
if (autoFocusEnabled && continuousFocusEnabled) {
focusMode = FocusMode.CONTINUOUS;
} else if (autoFocusEnabled) {
focusMode = FocusMode.AUTO;
} else {
focusMode = null;
}
}
/**
@@ -111,6 +125,27 @@ public class CameraSettings {
public void setContinuousFocusEnabled(boolean continuousFocusEnabled) {
this.continuousFocusEnabled = continuousFocusEnabled;
if (continuousFocusEnabled) {
focusMode = FocusMode.CONTINUOUS;
} else if (autoFocusEnabled) {
focusMode = FocusMode.AUTO;
} else {
focusMode = null;
}
}
/**
* Default to FocusMode.AUTO.
*
* @return value of selected focus mode
*/
public FocusMode getFocusMode() {
return focusMode;
}
public void setFocusMode(FocusMode focusMode) {
this.focusMode = focusMode;
}
/**
@@ -0,0 +1,48 @@
package com.journeyapps.barcodescanner.camera;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.os.Build;
import android.view.SurfaceHolder;
import java.io.IOException;
/**
* A surface on which a camera preview is displayed.
*
* This wraps either a SurfaceHolder or a SurfaceTexture.
*/
public class CameraSurface {
private SurfaceHolder surfaceHolder;
private SurfaceTexture surfaceTexture;
public CameraSurface(SurfaceHolder surfaceHolder) {
if (surfaceHolder == null) {
throw new IllegalArgumentException("surfaceHolder may not be null");
}
this.surfaceHolder = surfaceHolder;
}
public CameraSurface(SurfaceTexture surfaceTexture) {
if (surfaceTexture == null) {
throw new IllegalArgumentException("surfaceTexture may not be null");
}
this.surfaceTexture = surfaceTexture;
}
public SurfaceHolder getSurfaceHolder() {
return surfaceHolder;
}
public SurfaceTexture getSurfaceTexture() {
return surfaceTexture;
}
public void setPreview(Camera camera) throws IOException {
if (surfaceHolder != null) {
camera.setPreviewDisplay(surfaceHolder);
} else {
camera.setPreviewTexture(surfaceTexture);
}
}
}
@@ -20,7 +20,6 @@ class CameraThread {
return instance;
}
private Handler handler;
private HandlerThread thread;
@@ -33,13 +32,35 @@ class CameraThread {
}
/**
* Call from main thread.
* Call from main thread or camera thread.
*
* Enqueues a task on the camera thread.
*
* @param runnable the task to enqueue
*/
protected void enqueue(Runnable runnable) {
synchronized (LOCK) {
checkRunning();
this.handler.post(runnable);
}
}
/**
* Call from main thread or camera thread.
*
* Enqueues a task on the camera thread.
*
* @param runnable the task to enqueue
* @param delayMillis the delay in milliseconds before executing the runnable
*/
protected void enqueueDelayed(Runnable runnable, long delayMillis) {
synchronized (LOCK) {
checkRunning();
this.handler.postDelayed(runnable, delayMillis);
}
}
private void checkRunning() {
synchronized (LOCK) {
if (this.handler == null) {
if (openCount <= 0) {
@@ -49,7 +70,6 @@ class CameraThread {
this.thread.start();
this.handler = new Handler(thread.getLooper());
}
this.handler.post(runnable);
}
}
@@ -79,7 +99,7 @@ class CameraThread {
/**
* Call from main thread.
*
* @param runner
* @param runner The {@link Runnable} to be enqueued
*/
protected void incrementAndEnqueue(Runnable runner) {
synchronized (LOCK) {
@@ -0,0 +1,78 @@
package com.journeyapps.barcodescanner.camera;
import android.graphics.Rect;
import android.util.Log;
import com.journeyapps.barcodescanner.Size;
/**
* Scales the dimensions so that it fits entirely inside the parent.One of width or height will
* fit exactly. Aspect ratio is preserved.
*/
public class CenterCropStrategy extends PreviewScalingStrategy {
private static final String TAG = CenterCropStrategy.class.getSimpleName();
/**
* Get a score for our size.
*
* Based on heuristics for penalizing scaling and cropping.
*
* 1.0 is perfect (exact match).
* 0.0 means we can't use it at all.
*
* @param size the camera preview size (that can be scaled)
* @param desired the viewfinder size
* @return the score
*/
@Override
protected float getScore(Size size, Size desired) {
if (size.width <= 0 || size.height <= 0) {
return 0f;
}
Size scaled = size.scaleCrop(desired);
// Scaling preserves aspect ratio
float scaleRatio = scaled.width * 1.0f / size.width;
// Treat downscaling as slightly better than upscaling
float scaleScore;
if (scaleRatio > 1.0f) {
// Upscaling
scaleScore = (float)Math.pow(1.0f / scaleRatio, 1.1);
} else {
// Downscaling
scaleScore = scaleRatio;
}
// Ratio of scaledDimension / dimension.
// Note that with scaleCrop, only one dimension is cropped.
float cropRatio = scaled.width * 1.0f / desired.width +
scaled.height * 1.0f / desired.height;
// Cropping is bad, square it
// 1.0 means no cropping. 50% cropping is 0.44f, 10% cropping is 0.82f
float cropScore = 1.0f / cropRatio / cropRatio;
return scaleScore * cropScore;
}
/**
* Scale the preview to cover the viewfinder, then center it.
*
* Aspect ratio is preserved.
*
* @param previewSize the size of the preview (camera), in current display orientation
* @param viewfinderSize the size of the viewfinder (display), in current display orientation
* @return a rect placing the preview
*/
public Rect scalePreview(Size previewSize, Size viewfinderSize) {
// We avoid scaling if feasible.
Size scaledPreview = previewSize.scaleCrop(viewfinderSize);
Log.i(TAG, "Preview: " + previewSize + "; Scaled: " + scaledPreview + "; Want: " + viewfinderSize);
int dx = (scaledPreview.width - viewfinderSize.width) / 2;
int dy = (scaledPreview.height - viewfinderSize.height) / 2;
return new Rect(-dx, -dy, scaledPreview.width - dx, scaledPreview.height - dy);
}
}
@@ -1,13 +1,9 @@
package com.journeyapps.barcodescanner.camera;
import android.graphics.Rect;
import android.util.Log;
import android.view.Surface;
import com.journeyapps.barcodescanner.Size;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
@@ -19,6 +15,7 @@ public class DisplayConfiguration {
private Size viewfinderSize;
private int rotation;
private boolean center = false;
private PreviewScalingStrategy previewScalingStrategy = new FitCenterStrategy();
public DisplayConfiguration(int rotation) {
this.rotation = rotation;
@@ -37,6 +34,14 @@ public class DisplayConfiguration {
return viewfinderSize;
}
public PreviewScalingStrategy getPreviewScalingStrategy() {
return previewScalingStrategy;
}
public void setPreviewScalingStrategy(PreviewScalingStrategy previewScalingStrategy) {
this.previewScalingStrategy = previewScalingStrategy;
}
/**
* @param rotate true to rotate the preview size
* @return desired preview size in natural camera orientation.
@@ -76,100 +81,7 @@ public class DisplayConfiguration {
final Size desired = getDesiredPreviewSize(isRotated);
if (desired == null) {
return sizes.get(0);
}
Collections.sort(sizes, new Comparator<Size>() {
@Override
public int compare(Size a, Size b) {
Size ascaled = scale(a, desired);
int aScale = ascaled.width - a.width;
Size bscaled = scale(b, desired);
int bScale = bscaled.width - b.width;
if (aScale == 0 && bScale == 0) {
// Both no scaling, pick the smaller one
return a.compareTo(b);
} else if (aScale == 0) {
// No scaling for a; pick a
return -1;
} else if (bScale == 0) {
// No scaling for b; pick b
return 1;
} else if (aScale < 0 && bScale < 0) {
// Both downscaled. Pick the smaller one (less downscaling).
return a.compareTo(b);
} else if (aScale > 0 && bScale > 0) {
// Both upscaled. Pick the larger one (less upscaling).
return -a.compareTo(b);
} else if (aScale < 0) {
// a downscaled, b upscaled. Pick a.
return -1;
} else {
// a upscaled, b downscaled. Pick b.
return 1;
}
}
});
Log.i(TAG, "Viewfinder size: " + desired);
Log.i(TAG, "Preview in order of preference: " + sizes);
return sizes.get(0);
}
/**
* Scale from so that to.fitsIn(size). Tries to scale by powers of two, or by 3/2. Aspect ratio
* is preserved.
*
* These scaling factors will theoretically result in fast scaling with minimal quality loss.
*
* TODO: confirm whether or not this is the case in practice.
*
* @param from the start size
* @param to the minimum desired size
* @return the scaled size
*/
public static Size scale(Size from, Size to) {
Size current = from;
if (!to.fitsIn(current)) {
// Scale up
while (true) {
Size scaled150 = current.scale(3, 2);
Size scaled200 = current.scale(2, 1);
if (to.fitsIn(scaled150)) {
// Scale by 3/2
return scaled150;
} else if (to.fitsIn(scaled200)) {
// Scale by 2/1
return scaled200;
} else {
// Scale by 2/1 and continue
current = scaled200;
}
}
} else {
// Scale down
while (true) {
Size scaled66 = current.scale(2, 3);
Size scaled50 = current.scale(1, 2);
if (!to.fitsIn(scaled50)) {
if (to.fitsIn(scaled66)) {
// Scale by 2/3
return scaled66;
} else {
// No more downscaling
return current;
}
} else {
// Scale by 1/2
current = scaled50;
}
}
}
return previewScalingStrategy.getBestPreviewSize(sizes, desired);
}
/**
@@ -177,17 +89,10 @@ public class DisplayConfiguration {
*
* Aspect ratio is preserved.
*
* @param previewSize the size of the preview
* @param previewSize the size of the preview (camera), in current display orientation
* @return a rect placing the preview
*/
public Rect scalePreview(Size previewSize) {
// We avoid scaling if feasible.
Size scaledPreview = scale(previewSize, viewfinderSize);
Log.i(TAG, "Preview: " + previewSize + "; Scaled: " + scaledPreview + "; Want: " + viewfinderSize);
int dx = (scaledPreview.width - viewfinderSize.width) / 2;
int dy = (scaledPreview.height - viewfinderSize.height) / 2;
return new Rect(-dx, -dy, scaledPreview.width - dx, scaledPreview.height - dy);
return previewScalingStrategy.scalePreview(previewSize, viewfinderSize);
}
}
@@ -0,0 +1,78 @@
package com.journeyapps.barcodescanner.camera;
import android.graphics.Rect;
import android.util.Log;
import com.journeyapps.barcodescanner.Size;
/**
* Scales the size so that both dimensions will be greater than or equal to the corresponding
* dimension of the parent. One of width or height will fit exactly. Aspect ratio is preserved.
*/
public class FitCenterStrategy extends PreviewScalingStrategy {
private static final String TAG = FitCenterStrategy.class.getSimpleName();
/**
* Get a score for our size.
*
* Based on heuristics for penalizing scaling and cropping.
*
* 1.0 is perfect (exact match).
* 0.0 means we can't use it at all.
*
* @param size the camera preview size (that can be scaled)
* @param desired the viewfinder size
* @return the score
*/
@Override
protected float getScore(Size size, Size desired) {
if (size.width <= 0 || size.height <= 0) {
return 0f;
}
Size scaled = size.scaleFit(desired);
// Scaling preserves aspect ratio
float scaleRatio = scaled.width * 1.0f / size.width;
// Treat downscaling as slightly better than upscaling
float scaleScore;
if (scaleRatio > 1.0f) {
// Upscaling
scaleScore = (float)Math.pow(1.0f / scaleRatio, 1.1);
} else {
// Downscaling
scaleScore = scaleRatio;
}
// Ratio of scaledDimension / dimension.
// Note that with scaleCrop, only one dimension is cropped.
float cropRatio = (desired.width * 1.0f / scaled.width) *
(desired.height * 1.0f / scaled.height);
// Cropping is very bad, since it's used-visible for centerFit
// 1.0 means no cropping.
float cropScore = 1.0f / cropRatio / cropRatio / cropRatio;
return scaleScore * cropScore;
}
/**
* Scale the preview to cover the viewfinder, then center it.
*
* Aspect ratio is preserved.
*
* @param previewSize the size of the preview (camera), in current display orientation
* @param viewfinderSize the size of the viewfinder (display), in current display orientation
* @return a rect placing the preview
*/
public Rect scalePreview(Size previewSize, Size viewfinderSize) {
// We avoid scaling if feasible.
Size scaledPreview = previewSize.scaleFit(viewfinderSize);
Log.i(TAG, "Preview: " + previewSize + "; Scaled: " + scaledPreview + "; Want: " + viewfinderSize);
int dx = (scaledPreview.width - viewfinderSize.width) / 2;
int dy = (scaledPreview.height - viewfinderSize.height) / 2;
return new Rect(-dx, -dy, scaledPreview.width - dx, scaledPreview.height - dy);
}
}
@@ -0,0 +1,62 @@
package com.journeyapps.barcodescanner.camera;
import android.graphics.Rect;
import com.journeyapps.barcodescanner.Size;
/**
* Scales the size so that it fits exactly. Aspect ratio is NOT preserved.
*/
public class FitXYStrategy extends PreviewScalingStrategy {
private static final String TAG = FitXYStrategy.class.getSimpleName();
private static float absRatio(float ratio) {
if (ratio < 1.0f) {
return 1.0f / ratio;
} else {
return ratio;
}
}
/**
* Get a score for our size.
*
* Based on heuristics for penalizing scaling and cropping.
*
* 1.0 is perfect (exact match).
* 0.0 means we can't use it at all.
*
* @param size the camera preview size (that can be scaled)
* @param desired the viewfinder size
* @return the score
*/
@Override
protected float getScore(Size size, Size desired) {
if (size.width <= 0 || size.height <= 0) {
return 0f;
}
float scaleX = absRatio(size.width * 1.0f / desired.width);
float scaleY = absRatio(size.height * 1.0f / desired.height);
float scaleScore = 1.0f / scaleX / scaleY;
float distortion = absRatio((1.0f * size.width / size.height) / (1.0f * desired.width / desired.height));
// Distortion is bad!
float distortionScore = 1.0f / distortion / distortion / distortion;
return scaleScore * distortionScore;
}
/**
* Scale the preview to match the viewfinder exactly.
*
* @param previewSize the size of the preview (camera), in current display orientation
* @param viewfinderSize the size of the viewfinder (display), in current display orientation
* @return a rect placing the preview
*/
public Rect scalePreview(Size previewSize, Size viewfinderSize) {
return new Rect(0, 0, viewfinderSize.width, viewfinderSize.height);
}
}

Some files were not shown because too many files have changed in this diff Show More