Toggle navigation
Toggle navigation
This project
Loading...
Sign in
flutter_package
/
mobile_scanner
Go to a project
Toggle navigation
Projects
Groups
Snippets
Help
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
fumin65
2024-03-21 21:14:33 +0900
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
b151eba24b26eff1b926933d503e58ef6c3e1c7e
b151eba2
1 parent
269bc951
add pause function
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
174 additions
and
32 deletions
android/src/main/kotlin/dev/steenbakker/mobile_scanner/MobileScanner.kt
android/src/main/kotlin/dev/steenbakker/mobile_scanner/MobileScannerExceptions.kt
android/src/main/kotlin/dev/steenbakker/mobile_scanner/MobileScannerHandler.kt
example/lib/barcode_scanner_controller.dart
example/lib/scanner_button_widgets.dart
ios/Classes/MobileScanner.swift
ios/Classes/MobileScannerError.swift
ios/Classes/MobileScannerPlugin.swift
lib/src/method_channel/mobile_scanner_method_channel.dart
lib/src/mobile_scanner_controller.dart
lib/src/mobile_scanner_platform_interface.dart
android/src/main/kotlin/dev/steenbakker/mobile_scanner/MobileScanner.kt
View file @
b151eba
...
...
@@ -19,7 +19,6 @@ import androidx.camera.core.ExperimentalGetImage
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import androidx.camera.core.Preview
import androidx.camera.core.resolutionselector.AspectRatioStrategy
import androidx.camera.core.resolutionselector.ResolutionSelector
import androidx.camera.core.resolutionselector.ResolutionStrategy
import androidx.camera.lifecycle.ProcessCameraProvider
...
...
@@ -259,7 +258,7 @@ class MobileScanner(
}
cameraProvider?.unbindAll()
textureEntry = textureRegistry.createSurfaceTexture()
textureEntry = texture
Entry ?: texture
Registry.createSurfaceTexture()
// Preview
val surfaceProvider = Preview.SurfaceProvider { request ->
...
...
@@ -380,14 +379,33 @@ class MobileScanner(
}, executor)
}
/**
* Pause barcode scanning.
*/
fun pause() {
if (isPaused()) {
throw AlreadyPaused()
} else if (isStopped()) {
throw AlreadyStopped()
}
releaseCamera()
}
/**
* Stop barcode scanning.
*/
fun stop() {
if (isStopped()) {
if (
!isPaused() &&
isStopped()) {
throw AlreadyStopped()
}
releaseCamera()
releaseTexture()
}
private fun releaseCamera() {
if (displayListener != null) {
val displayManager = activity.applicationContext.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
...
...
@@ -398,15 +416,19 @@ class MobileScanner(
val owner = activity as LifecycleOwner
camera?.cameraInfo?.torchState?.removeObservers(owner)
cameraProvider?.unbindAll()
textureEntry?.release()
camera = null
preview = null
textureEntry = null
cameraProvider = null
}
private fun releaseTexture() {
textureEntry?.release()
textureEntry = null
}
private fun isStopped() = camera == null && preview == null
private fun isPaused() = isStopped() && textureEntry != null
/**
* Toggles the flash light on or off.
...
...
android/src/main/kotlin/dev/steenbakker/mobile_scanner/MobileScannerExceptions.kt
View file @
b151eba
...
...
@@ -3,6 +3,7 @@ package dev.steenbakker.mobile_scanner
class NoCamera : Exception()
class AlreadyStarted : Exception()
class AlreadyStopped : Exception()
class AlreadyPaused : Exception()
class CameraError : Exception()
class ZoomWhenStopped : Exception()
class ZoomNotInRange : Exception()
\ No newline at end of file
...
...
android/src/main/kotlin/dev/steenbakker/mobile_scanner/MobileScannerHandler.kt
View file @
b151eba
...
...
@@ -122,6 +122,7 @@ class MobileScannerHandler(
})
"start" -> start(call, result)
"torch" -> toggleTorch(call, result)
"pause" -> pause(result)
"stop" -> stop(result)
"analyzeImage" -> analyzeImage(call, result)
"setScale" -> setScale(call, result)
...
...
@@ -227,6 +228,18 @@ class MobileScannerHandler(
)
}
private fun pause(result: MethodChannel.Result) {
try {
mobileScanner!!.pause()
result.success(null)
} catch (e: Exception) {
when (e) {
is AlreadyPaused, is AlreadyStopped -> result.success(null)
else -> throw e
}
}
}
private fun stop(result: MethodChannel.Result) {
try {
mobileScanner!!.stop()
...
...
example/lib/barcode_scanner_controller.dart
View file @
b151eba
...
...
@@ -106,6 +106,7 @@ class _BarcodeScannerWithControllerState
children:
[
ToggleFlashlightButton
(
controller:
controller
),
StartStopMobileScannerButton
(
controller:
controller
),
PauseMobileScannerButton
(
controller:
controller
),
Expanded
(
child:
Center
(
child:
_buildBarcode
(
_barcode
))),
SwitchCameraButton
(
controller:
controller
),
AnalyzeImageFromGalleryButton
(
controller:
controller
),
...
...
example/lib/scanner_button_widgets.dart
View file @
b151eba
...
...
@@ -166,3 +166,30 @@ class ToggleFlashlightButton extends StatelessWidget {
);
}
}
class
PauseMobileScannerButton
extends
StatelessWidget
{
const
PauseMobileScannerButton
({
required
this
.
controller
,
super
.
key
});
final
MobileScannerController
controller
;
@override
Widget
build
(
BuildContext
context
)
{
return
ValueListenableBuilder
(
valueListenable:
controller
,
builder:
(
context
,
state
,
child
)
{
if
(!
state
.
isInitialized
||
!
state
.
isRunning
)
{
return
const
SizedBox
.
shrink
();
}
return
IconButton
(
color:
Colors
.
white
,
iconSize:
32.0
,
icon:
const
Icon
(
Icons
.
pause
),
onPressed:
()
async
{
await
controller
.
pause
();
},
);
},
);
}
}
...
...
ios/Classes/MobileScanner.swift
View file @
b151eba
...
...
@@ -60,6 +60,14 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
private
var
imagesCurrentlyBeingProcessed
=
false
public
var
timeoutSeconds
:
Double
=
0
private
var
stopped
:
Bool
{
return
device
==
nil
||
captureSession
==
nil
}
private
var
paused
:
Bool
{
return
stopped
&&
textureId
!=
nil
}
init
(
registry
:
FlutterTextureRegistry
?,
mobileScannerCallback
:
@escaping
MobileScannerCallback
,
torchModeChangeCallback
:
@escaping
TorchModeChangeCallback
,
zoomScaleChangeCallback
:
@escaping
ZoomScaleChangeCallback
)
{
self
.
registry
=
registry
...
...
@@ -126,6 +134,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
/// Gets called when a new image is added to the buffer
public
func
captureOutput
(
_
output
:
AVCaptureOutput
,
didOutput
sampleBuffer
:
CMSampleBuffer
,
from
connection
:
AVCaptureConnection
)
{
guard
let
imageBuffer
=
CMSampleBufferGetImageBuffer
(
sampleBuffer
)
else
{
print
(
"Failed to get image buffer from sample buffer."
)
return
...
...
@@ -180,7 +189,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
barcodesString
=
nil
scanner
=
barcodeScannerOptions
!=
nil
?
BarcodeScanner
.
barcodeScanner
(
options
:
barcodeScannerOptions
!
)
:
BarcodeScanner
.
barcodeScanner
()
captureSession
=
AVCaptureSession
()
textureId
=
registry
?
.
register
(
self
)
textureId
=
textureId
??
registry
?
.
register
(
self
)
// Open the camera device
device
=
getDefaultCameraDevice
(
position
:
cameraPosition
)
...
...
@@ -300,28 +309,50 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
completion
(
MobileScannerStartParameters
())
}
}
/// Pause scanning for barcodes
func
pause
()
throws
{
if
(
paused
)
{
throw
MobileScannerError
.
alreadyPaused
}
else
if
(
stopped
)
{
throw
MobileScannerError
.
alreadyStopped
}
releaseCamera
()
}
/// Stop scanning for barcodes
func
stop
()
throws
{
if
(
device
==
nil
||
captureSession
==
nil
)
{
if
(
!
paused
&&
stopped
)
{
throw
MobileScannerError
.
alreadyStopped
}
releaseCamera
()
releaseTexture
()
}
private
func
releaseCamera
()
{
guard
let
captureSession
=
captureSession
else
{
return
}
captureSession
!.
stopRunning
()
for
input
in
captureSession
!.
inputs
{
captureSession
!.
removeInput
(
input
)
captureSession
.
stopRunning
()
for
input
in
captureSession
.
inputs
{
captureSession
.
removeInput
(
input
)
}
for
output
in
captureSession
!.
outputs
{
captureSession
!.
removeOutput
(
output
)
for
output
in
captureSession
.
outputs
{
captureSession
.
removeOutput
(
output
)
}
latestBuffer
=
nil
device
.
removeObserver
(
self
,
forKeyPath
:
#
keyPath
(
AVCaptureDevice
.
torchMode
))
device
.
removeObserver
(
self
,
forKeyPath
:
#
keyPath
(
AVCaptureDevice
.
videoZoomFactor
))
self
.
captureSession
=
nil
device
=
nil
}
private
func
releaseTexture
()
{
registry
?
.
unregisterTexture
(
textureId
)
textureId
=
nil
captureSession
=
nil
device
=
nil
}
/// Set the torch mode.
...
...
ios/Classes/MobileScannerError.swift
View file @
b151eba
...
...
@@ -10,6 +10,7 @@ enum MobileScannerError: Error {
case
noCamera
case
alreadyStarted
case
alreadyStopped
case
alreadyPaused
case
cameraError
(
_
error
:
Error
)
case
zoomWhenStopped
case
zoomError
(
_
error
:
Error
)
...
...
ios/Classes/MobileScannerPlugin.swift
View file @
b151eba
...
...
@@ -78,6 +78,8 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {
AVCaptureDevice
.
requestAccess
(
for
:
.
video
,
completionHandler
:
{
result
(
$0
)
})
case
"start"
:
start
(
call
,
result
)
case
"pause"
:
pause
(
result
)
case
"stop"
:
stop
(
result
)
case
"torch"
:
...
...
@@ -146,6 +148,14 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {
details
:
nil
))
}
}
/// Stops the mobileScanner without closing the texture.
private
func
pause
(
_
result
:
@escaping
FlutterResult
)
{
do
{
try
mobileScanner
.
pause
()
}
catch
{}
result
(
nil
)
}
/// Stops the mobileScanner and closes the texture.
private
func
stop
(
_
result
:
@escaping
FlutterResult
)
{
...
...
lib/src/method_channel/mobile_scanner_method_channel.dart
View file @
b151eba
...
...
@@ -38,6 +38,7 @@ class MethodChannelMobileScanner extends MobileScannerPlatform {
}
int
?
_textureId
;
bool
_pausing
=
false
;
/// Parse a [BarcodeCapture] from the given [event].
BarcodeCapture
?
_parseBarcode
(
Map
<
Object
?,
Object
?>?
event
)
{
...
...
@@ -206,7 +207,7 @@ class MethodChannelMobileScanner extends MobileScannerPlatform {
@override
Future
<
MobileScannerViewAttributes
>
start
(
StartOptions
startOptions
)
async
{
if
(
_textureId
!=
null
)
{
if
(
!
_pausing
&&
_textureId
!=
null
)
{
throw
const
MobileScannerException
(
errorCode:
MobileScannerErrorCode
.
controllerAlreadyInitialized
,
errorDetails:
MobileScannerErrorDetails
(
...
...
@@ -274,6 +275,8 @@ class MethodChannelMobileScanner extends MobileScannerPlatform {
size
=
Size
(
width
,
height
);
}
_pausing
=
false
;
return
MobileScannerViewAttributes
(
hasTorch:
hasTorch
,
numberOfCameras:
numberOfCameras
,
...
...
@@ -288,10 +291,23 @@ class MethodChannelMobileScanner extends MobileScannerPlatform {
}
_textureId
=
null
;
_pausing
=
false
;
await
methodChannel
.
invokeMethod
<
void
>(
'stop'
);
}
@override
Future
<
void
>
pause
()
async
{
if
(
_pausing
)
{
return
;
}
_pausing
=
true
;
await
methodChannel
.
invokeMethod
<
void
>(
'pause'
);
}
@override
Future
<
void
>
updateScanWindow
(
Rect
?
window
)
async
{
if
(
_textureId
==
null
)
{
...
...
lib/src/mobile_scanner_controller.dart
View file @
b151eba
...
...
@@ -166,6 +166,25 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> {
}
}
void
_stop
()
{
// Do nothing if not initialized or already stopped.
// On the web, the permission popup triggers a lifecycle change from resumed to inactive,
// due to the permission popup gaining focus.
// This would 'stop' the camera while it is not ready yet.
if
(!
value
.
isInitialized
||
!
value
.
isRunning
||
_isDisposed
)
{
return
;
}
_disposeListeners
();
// After the camera stopped, set the torch state to off,
// as the torch state callback is never called when the camera is stopped.
value
=
value
.
copyWith
(
isRunning:
false
,
torchState:
TorchState
.
off
,
);
}
/// Analyze an image file.
///
/// The [path] points to a file on the device.
...
...
@@ -301,26 +320,21 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> {
///
/// Does nothing if the camera is already stopped.
Future
<
void
>
stop
()
async
{
// Do nothing if not initialized or already stopped.
// On the web, the permission popup triggers a lifecycle change from resumed to inactive,
// due to the permission popup gaining focus.
// This would 'stop' the camera while it is not ready yet.
if
(!
value
.
isInitialized
||
!
value
.
isRunning
||
_isDisposed
)
{
return
;
}
_disposeListeners
();
// After the camera stopped, set the torch state to off,
// as the torch state callback is never called when the camera is stopped.
value
=
value
.
copyWith
(
isRunning:
false
,
torchState:
TorchState
.
off
,
);
_stop
();
await
MobileScannerPlatform
.
instance
.
stop
();
}
/// Pause the camera.
///
/// This method stops to update camera frame and scan barcodes.
/// After calling this method, the camera can be restarted using [start].
///
/// Does nothing if the camera is already paused or stopped.
Future
<
void
>
pause
()
async
{
_stop
();
await
MobileScannerPlatform
.
instance
.
pause
();
}
/// Switch between the front and back camera.
///
/// Does nothing if the device has less than 2 cameras.
...
...
lib/src/mobile_scanner_platform_interface.dart
View file @
b151eba
...
...
@@ -95,6 +95,12 @@ abstract class MobileScannerPlatform extends PlatformInterface {
throw
UnimplementedError
(
'stop() has not been implemented.'
);
}
/// Pause the camera.
Future
<
void
>
pause
()
{
throw
UnimplementedError
(
'pause() has not been implemented.'
);
}
/// Update the scan window to the given [window] rectangle.
///
/// Any barcodes that do not intersect with the given [window] will be ignored.
...
...
Please
register
or
login
to post a comment