feat: add barcode scanner plugin (#536)
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"barcode-scanner": major
|
||||||
|
"barcode-scanner-js": major
|
||||||
|
---
|
||||||
|
|
||||||
|
Initial release.
|
@ -1,16 +1,6 @@
|
|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="Theme.api" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
<style name="Theme.api" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||||
<!-- Primary brand color. -->
|
|
||||||
<item name="colorPrimary">@color/purple_200</item>
|
|
||||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
|
||||||
<item name="colorOnPrimary">@color/black</item>
|
|
||||||
<!-- Secondary brand color. -->
|
|
||||||
<item name="colorSecondary">@color/teal_200</item>
|
|
||||||
<item name="colorSecondaryVariant">@color/teal_200</item>
|
|
||||||
<item name="colorOnSecondary">@color/black</item>
|
|
||||||
<!-- Status bar color. -->
|
|
||||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
|
||||||
<!-- Customize your theme here. -->
|
<!-- Customize your theme here. -->
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,16 +1,6 @@
|
|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="Theme.api" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
<style name="Theme.api" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||||
<!-- Primary brand color. -->
|
|
||||||
<item name="colorPrimary">@color/purple_500</item>
|
|
||||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
|
||||||
<item name="colorOnPrimary">@color/white</item>
|
|
||||||
<!-- Secondary brand color. -->
|
|
||||||
<item name="colorSecondary">@color/teal_200</item>
|
|
||||||
<item name="colorSecondaryVariant">@color/teal_700</item>
|
|
||||||
<item name="colorOnSecondary">@color/black</item>
|
|
||||||
<!-- Status bar color. -->
|
|
||||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
|
||||||
<!-- Customize your theme here. -->
|
<!-- Customize your theme here. -->
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
xcuserdata/
|
||||||
|
build/
|
||||||
|
Externals/
|
After Width: | Height: | Size: 844 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 7.7 KiB |
After Width: | Height: | Size: 152 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 7.7 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 9.9 KiB |
After Width: | Height: | Size: 11 KiB |
@ -0,0 +1,116 @@
|
|||||||
|
{
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"size": "20x20",
|
||||||
|
"idiom": "iphone",
|
||||||
|
"filename": "AppIcon-20x20@2x.png",
|
||||||
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": "20x20",
|
||||||
|
"idiom": "iphone",
|
||||||
|
"filename": "AppIcon-20x20@3x.png",
|
||||||
|
"scale": "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": "29x29",
|
||||||
|
"idiom": "iphone",
|
||||||
|
"filename": "AppIcon-29x29@2x-1.png",
|
||||||
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": "29x29",
|
||||||
|
"idiom": "iphone",
|
||||||
|
"filename": "AppIcon-29x29@3x.png",
|
||||||
|
"scale": "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": "40x40",
|
||||||
|
"idiom": "iphone",
|
||||||
|
"filename": "AppIcon-40x40@2x.png",
|
||||||
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": "40x40",
|
||||||
|
"idiom": "iphone",
|
||||||
|
"filename": "AppIcon-40x40@3x.png",
|
||||||
|
"scale": "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": "60x60",
|
||||||
|
"idiom": "iphone",
|
||||||
|
"filename": "AppIcon-60x60@2x.png",
|
||||||
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": "60x60",
|
||||||
|
"idiom": "iphone",
|
||||||
|
"filename": "AppIcon-60x60@3x.png",
|
||||||
|
"scale": "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": "20x20",
|
||||||
|
"idiom": "ipad",
|
||||||
|
"filename": "AppIcon-20x20@1x.png",
|
||||||
|
"scale": "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": "20x20",
|
||||||
|
"idiom": "ipad",
|
||||||
|
"filename": "AppIcon-20x20@2x-1.png",
|
||||||
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": "29x29",
|
||||||
|
"idiom": "ipad",
|
||||||
|
"filename": "AppIcon-29x29@1x.png",
|
||||||
|
"scale": "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": "29x29",
|
||||||
|
"idiom": "ipad",
|
||||||
|
"filename": "AppIcon-29x29@2x.png",
|
||||||
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": "40x40",
|
||||||
|
"idiom": "ipad",
|
||||||
|
"filename": "AppIcon-40x40@1x.png",
|
||||||
|
"scale": "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": "40x40",
|
||||||
|
"idiom": "ipad",
|
||||||
|
"filename": "AppIcon-40x40@2x-1.png",
|
||||||
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": "76x76",
|
||||||
|
"idiom": "ipad",
|
||||||
|
"filename": "AppIcon-76x76@1x.png",
|
||||||
|
"scale": "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": "76x76",
|
||||||
|
"idiom": "ipad",
|
||||||
|
"filename": "AppIcon-76x76@2x.png",
|
||||||
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": "83.5x83.5",
|
||||||
|
"idiom": "ipad",
|
||||||
|
"filename": "AppIcon-83.5x83.5@2x.png",
|
||||||
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": "1024x1024",
|
||||||
|
"idiom": "ios-marketing",
|
||||||
|
"filename": "AppIcon-512@2x.png",
|
||||||
|
"scale": "1x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info": {
|
||||||
|
"version": 1,
|
||||||
|
"author": "xcode"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info": {
|
||||||
|
"version": 1,
|
||||||
|
"author": "xcode"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>method</key>
|
||||||
|
<string>development</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
@ -0,0 +1,21 @@
|
|||||||
|
# Uncomment the next line to define a global platform for your project
|
||||||
|
|
||||||
|
target 'api_iOS' do
|
||||||
|
platform :ios, '13.0'
|
||||||
|
# Pods for api_iOS
|
||||||
|
end
|
||||||
|
|
||||||
|
target 'api_macOS' do
|
||||||
|
platform :osx, '11.0'
|
||||||
|
# Pods for api_macOS
|
||||||
|
end
|
||||||
|
|
||||||
|
# Delete the deployment target for iOS and macOS, causing it to be inherited from the Podfile
|
||||||
|
post_install do |installer|
|
||||||
|
installer.pods_project.targets.each do |target|
|
||||||
|
target.build_configurations.each do |config|
|
||||||
|
config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET'
|
||||||
|
config.build_settings.delete 'MACOSX_DEPLOYMENT_TARGET'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace ffi {
|
||||||
|
extern "C" {
|
||||||
|
void start_app();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
|||||||
|
#include "bindings/bindings.h"
|
||||||
|
|
||||||
|
int main(int argc, char * argv[]) {
|
||||||
|
ffi::start_app();
|
||||||
|
return 0;
|
||||||
|
}
|
@ -0,0 +1,462 @@
|
|||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 51;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
2ECFC1BC47D948875C8CEC41 /* libapi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FC53D4128D7F74E4E6338455 /* libapi.a */; };
|
||||||
|
3043432501C9BC2DB6B4CB95 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71EB788DE4662CFC0D97F567 /* CoreGraphics.framework */; };
|
||||||
|
328B4ADB3700C1873BEB7B10 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 90D3B673AFAB8D8AB561F616 /* main.mm */; };
|
||||||
|
6F379F15DA085785BA2624D4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6B7E79E23E646BA7968B457C /* Assets.xcassets */; };
|
||||||
|
9AADB041D25772D04E543F15 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 62601E25FA39E62BE119B74D /* Metal.framework */; };
|
||||||
|
9DDA3BE70DD0E4013973FE38 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B6082E363D51372A7658C351 /* UIKit.framework */; };
|
||||||
|
AFA0CA286325FD7A34968CA2 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 384966E551417F94A02D2706 /* Security.framework */; };
|
||||||
|
B60763BD194DFACA215EC7DA /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC377692DC31A070A0188C9D /* QuartzCore.framework */; };
|
||||||
|
C6D80743F168BDF017B7769E /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 59CFE20DCF760BE67D9CE3D6 /* WebKit.framework */; };
|
||||||
|
DFFF888045C8D9D9FB69E8FD /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 338E66700FD330B99D434DD7 /* MetalKit.framework */; };
|
||||||
|
F86717F05E27C72C9FA1FB27 /* assets in Resources */ = {isa = PBXBuildFile; fileRef = 74A8FDFB350B966F5AAD4A24 /* assets */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
0E96CE07CD20273DD46BF325 /* main.rs */ = {isa = PBXFileReference; path = main.rs; sourceTree = "<group>"; };
|
||||||
|
1C1AB1B414CA2795AFBEDDB9 /* tray.rs */ = {isa = PBXFileReference; path = tray.rs; sourceTree = "<group>"; };
|
||||||
|
2F63E2AA460089BB58D40C79 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
338E66700FD330B99D434DD7 /* MetalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalKit.framework; path = System/Library/Frameworks/MetalKit.framework; sourceTree = SDKROOT; };
|
||||||
|
384966E551417F94A02D2706 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
|
||||||
|
59CFE20DCF760BE67D9CE3D6 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };
|
||||||
|
5AC703CEBA41A121596066F3 /* api_iOS.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = api_iOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
62601E25FA39E62BE119B74D /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; };
|
||||||
|
6B7E79E23E646BA7968B457C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
|
71EB788DE4662CFC0D97F567 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
|
||||||
|
74A8FDFB350B966F5AAD4A24 /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; path = assets; sourceTree = SOURCE_ROOT; };
|
||||||
|
785D025E9542F7E098BF22B5 /* lib.rs */ = {isa = PBXFileReference; path = lib.rs; sourceTree = "<group>"; };
|
||||||
|
879941AE3DAA14534BBC6391 /* api_iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = api_iOS.entitlements; sourceTree = "<group>"; };
|
||||||
|
90D3B673AFAB8D8AB561F616 /* main.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = "<group>"; };
|
||||||
|
B6082E363D51372A7658C351 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
|
||||||
|
DC377692DC31A070A0188C9D /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
|
||||||
|
EC8C7948C50C3C9B5D96CB61 /* bindings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bindings.h; sourceTree = "<group>"; };
|
||||||
|
F835F52713CE8F029D5D252C /* cmd.rs */ = {isa = PBXFileReference; path = cmd.rs; sourceTree = "<group>"; };
|
||||||
|
FC53D4128D7F74E4E6338455 /* libapi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libapi.a; sourceTree = "<group>"; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
11E18DCDB3ADFE87C18915EF /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
2ECFC1BC47D948875C8CEC41 /* libapi.a in Frameworks */,
|
||||||
|
3043432501C9BC2DB6B4CB95 /* CoreGraphics.framework in Frameworks */,
|
||||||
|
9AADB041D25772D04E543F15 /* Metal.framework in Frameworks */,
|
||||||
|
DFFF888045C8D9D9FB69E8FD /* MetalKit.framework in Frameworks */,
|
||||||
|
B60763BD194DFACA215EC7DA /* QuartzCore.framework in Frameworks */,
|
||||||
|
AFA0CA286325FD7A34968CA2 /* Security.framework in Frameworks */,
|
||||||
|
9DDA3BE70DD0E4013973FE38 /* UIKit.framework in Frameworks */,
|
||||||
|
C6D80743F168BDF017B7769E /* WebKit.framework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
0677CEAF1F282F38CBA0F140 = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
74A8FDFB350B966F5AAD4A24 /* assets */,
|
||||||
|
6B7E79E23E646BA7968B457C /* Assets.xcassets */,
|
||||||
|
F2116A6428EED18BE2A07E2B /* api_iOS */,
|
||||||
|
86D903732E10FAC4D300E8DF /* Externals */,
|
||||||
|
7A9A7AC155D9E22E54D6D847 /* Sources */,
|
||||||
|
CF9AA87D2F6E9C389B7AB70B /* src */,
|
||||||
|
10C9FC3FA3E12D6A4A67999D /* Frameworks */,
|
||||||
|
4AC51E67B71E27F15B02C5CD /* Products */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
07051859D6E2D8109C8FB128 /* bindings */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EC8C7948C50C3C9B5D96CB61 /* bindings.h */,
|
||||||
|
);
|
||||||
|
path = bindings;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
10C9FC3FA3E12D6A4A67999D /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
71EB788DE4662CFC0D97F567 /* CoreGraphics.framework */,
|
||||||
|
FC53D4128D7F74E4E6338455 /* libapi.a */,
|
||||||
|
62601E25FA39E62BE119B74D /* Metal.framework */,
|
||||||
|
338E66700FD330B99D434DD7 /* MetalKit.framework */,
|
||||||
|
DC377692DC31A070A0188C9D /* QuartzCore.framework */,
|
||||||
|
384966E551417F94A02D2706 /* Security.framework */,
|
||||||
|
B6082E363D51372A7658C351 /* UIKit.framework */,
|
||||||
|
59CFE20DCF760BE67D9CE3D6 /* WebKit.framework */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
4AC51E67B71E27F15B02C5CD /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
5AC703CEBA41A121596066F3 /* api_iOS.app */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
7A9A7AC155D9E22E54D6D847 /* Sources */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
A3574F52DBC5463B9C3D043D /* api */,
|
||||||
|
);
|
||||||
|
path = Sources;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
86D903732E10FAC4D300E8DF /* Externals */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
);
|
||||||
|
path = Externals;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
A3574F52DBC5463B9C3D043D /* api */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
90D3B673AFAB8D8AB561F616 /* main.mm */,
|
||||||
|
07051859D6E2D8109C8FB128 /* bindings */,
|
||||||
|
);
|
||||||
|
path = api;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
CF9AA87D2F6E9C389B7AB70B /* src */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
F835F52713CE8F029D5D252C /* cmd.rs */,
|
||||||
|
785D025E9542F7E098BF22B5 /* lib.rs */,
|
||||||
|
0E96CE07CD20273DD46BF325 /* main.rs */,
|
||||||
|
1C1AB1B414CA2795AFBEDDB9 /* tray.rs */,
|
||||||
|
);
|
||||||
|
name = src;
|
||||||
|
path = ../../src;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
F2116A6428EED18BE2A07E2B /* api_iOS */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
879941AE3DAA14534BBC6391 /* api_iOS.entitlements */,
|
||||||
|
2F63E2AA460089BB58D40C79 /* Info.plist */,
|
||||||
|
);
|
||||||
|
path = api_iOS;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
54DC6E273C78071F3BA12EF3 /* api_iOS */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 01CBC40275452376830D79B1 /* Build configuration list for PBXNativeTarget "api_iOS" */;
|
||||||
|
buildPhases = (
|
||||||
|
FF948951157DE71465B5BD5F /* Build Rust Code */,
|
||||||
|
71E73CC9AB5F1323EC1F6365 /* Sources */,
|
||||||
|
CA2BEC44B6EDA1F21B6155CD /* Resources */,
|
||||||
|
11E18DCDB3ADFE87C18915EF /* Frameworks */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = api_iOS;
|
||||||
|
productName = api_iOS;
|
||||||
|
productReference = 5AC703CEBA41A121596066F3 /* api_iOS.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
9BC88C3717DA5D4B78A51C15 /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
LastUpgradeCheck = 1200;
|
||||||
|
TargetAttributes = {
|
||||||
|
54DC6E273C78071F3BA12EF3 = {
|
||||||
|
DevelopmentTeam = Q93MBH6S2F;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = 8FA67D0F928A09CD639137D1 /* Build configuration list for PBXProject "api" */;
|
||||||
|
compatibilityVersion = "Xcode 11.0";
|
||||||
|
developmentRegion = en;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
Base,
|
||||||
|
en,
|
||||||
|
);
|
||||||
|
mainGroup = 0677CEAF1F282F38CBA0F140;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
54DC6E273C78071F3BA12EF3 /* api_iOS */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
CA2BEC44B6EDA1F21B6155CD /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
6F379F15DA085785BA2624D4 /* Assets.xcassets in Resources */,
|
||||||
|
F86717F05E27C72C9FA1FB27 /* assets in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
FF948951157DE71465B5BD5F /* Build Rust Code */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
alwaysOutOfDate = 1;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Build Rust Code";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(SRCROOT)/target/aarch64-apple-ios/${CONFIGURATION}/deps/libapi.a",
|
||||||
|
"$(SRCROOT)/target/x86_64-apple-ios/${CONFIGURATION}/deps/libapi.a",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "node /Users/lucas/projects/tauri/plugins-workspace/examples/api/./node_modules/.bin/../../../../node_modules/.pnpm/@tauri-apps+cli@2.0.0-alpha.14/node_modules/@tauri-apps/cli/tauri.js ios xcode-script -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --framework-search-paths \"${FRAMEWORK_SEARCH_PATHS:?}\" --header-search-paths \"${HEADER_SEARCH_PATHS:?}\" --gcc-preprocessor-definitions \"${GCC_PREPROCESSOR_DEFINITIONS:-}\" --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?}";
|
||||||
|
};
|
||||||
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
71E73CC9AB5F1323EC1F6365 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
328B4ADB3700C1873BEB7B10 /* main.mm in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
A83F70B4C02DD0222038C7F1 /* release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
};
|
||||||
|
name = release;
|
||||||
|
};
|
||||||
|
B6AD77E490F315562F75D3D7 /* debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"DEBUG=1",
|
||||||
|
);
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
};
|
||||||
|
name = debug;
|
||||||
|
};
|
||||||
|
BF284FE6E7AE0C8DDCCE398B /* debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
|
ARCHS = (
|
||||||
|
arm64,
|
||||||
|
"arm64-sim",
|
||||||
|
);
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = api_iOS/api_iOS.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
|
DEVELOPMENT_TEAM = Q93MBH6S2F;
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"\".\"",
|
||||||
|
);
|
||||||
|
INFOPLIST_FILE = api_iOS/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
"LIBRARY_SEARCH_PATHS[arch=arm64-sim]" = "$(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)";
|
||||||
|
"LIBRARY_SEARCH_PATHS[arch=arm64]" = "$(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)";
|
||||||
|
"LIBRARY_SEARCH_PATHS[arch=x86_64]" = "$(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.tauri.api;
|
||||||
|
PRODUCT_NAME = "Tauri API";
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
VALID_ARCHS = "arm64 arm64-sim";
|
||||||
|
};
|
||||||
|
name = debug;
|
||||||
|
};
|
||||||
|
DB0E254D0FD84970B57F6410 /* release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
|
ARCHS = (
|
||||||
|
arm64,
|
||||||
|
"arm64-sim",
|
||||||
|
);
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = api_iOS/api_iOS.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
|
DEVELOPMENT_TEAM = Q93MBH6S2F;
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"\".\"",
|
||||||
|
);
|
||||||
|
INFOPLIST_FILE = api_iOS/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
"LIBRARY_SEARCH_PATHS[arch=arm64-sim]" = "$(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)";
|
||||||
|
"LIBRARY_SEARCH_PATHS[arch=arm64]" = "$(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)";
|
||||||
|
"LIBRARY_SEARCH_PATHS[arch=x86_64]" = "$(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.tauri.api;
|
||||||
|
PRODUCT_NAME = "Tauri API";
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
VALID_ARCHS = "arm64 arm64-sim";
|
||||||
|
};
|
||||||
|
name = release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
01CBC40275452376830D79B1 /* Build configuration list for PBXNativeTarget "api_iOS" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
BF284FE6E7AE0C8DDCCE398B /* debug */,
|
||||||
|
DB0E254D0FD84970B57F6410 /* release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = debug;
|
||||||
|
};
|
||||||
|
8FA67D0F928A09CD639137D1 /* Build configuration list for PBXProject "api" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
B6AD77E490F315562F75D3D7 /* debug */,
|
||||||
|
A83F70B4C02DD0222038C7F1 /* release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = debug;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
};
|
||||||
|
rootObject = 9BC88C3717DA5D4B78A51C15 /* Project object */;
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>BuildSystemType</key>
|
||||||
|
<string>Original</string>
|
||||||
|
<key>DisableBuildSystemDeprecationDiagnostic</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
@ -0,0 +1,131 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1200"
|
||||||
|
version = "1.7">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES"
|
||||||
|
runPostActionsOnFailure = "NO">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "54DC6E273C78071F3BA12EF3"
|
||||||
|
BuildableName = "api_iOS.app"
|
||||||
|
BlueprintName = "api_iOS"
|
||||||
|
ReferencedContainer = "container:api.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "NO"
|
||||||
|
onlyGenerateCoverageForSpecifiedTargets = "NO">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "54DC6E273C78071F3BA12EF3"
|
||||||
|
BuildableName = "api_iOS.app"
|
||||||
|
BlueprintName = "api_iOS"
|
||||||
|
ReferencedContainer = "container:api.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
<CommandLineArguments>
|
||||||
|
</CommandLineArguments>
|
||||||
|
<EnvironmentVariables>
|
||||||
|
<EnvironmentVariable
|
||||||
|
key = "RUST_BACKTRACE"
|
||||||
|
value = "full"
|
||||||
|
isEnabled = "YES">
|
||||||
|
</EnvironmentVariable>
|
||||||
|
<EnvironmentVariable
|
||||||
|
key = "RUST_LOG"
|
||||||
|
value = "info"
|
||||||
|
isEnabled = "YES">
|
||||||
|
</EnvironmentVariable>
|
||||||
|
</EnvironmentVariables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "54DC6E273C78071F3BA12EF3"
|
||||||
|
BuildableName = "api_iOS.app"
|
||||||
|
BlueprintName = "api_iOS"
|
||||||
|
ReferencedContainer = "container:api.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
<CommandLineArguments>
|
||||||
|
</CommandLineArguments>
|
||||||
|
<EnvironmentVariables>
|
||||||
|
<EnvironmentVariable
|
||||||
|
key = "RUST_BACKTRACE"
|
||||||
|
value = "full"
|
||||||
|
isEnabled = "YES">
|
||||||
|
</EnvironmentVariable>
|
||||||
|
<EnvironmentVariable
|
||||||
|
key = "RUST_LOG"
|
||||||
|
value = "info"
|
||||||
|
isEnabled = "YES">
|
||||||
|
</EnvironmentVariable>
|
||||||
|
</EnvironmentVariables>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "NO"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "54DC6E273C78071F3BA12EF3"
|
||||||
|
BuildableName = "api_iOS.app"
|
||||||
|
BlueprintName = "api_iOS"
|
||||||
|
ReferencedContainer = "container:api.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
<CommandLineArguments>
|
||||||
|
</CommandLineArguments>
|
||||||
|
<EnvironmentVariables>
|
||||||
|
<EnvironmentVariable
|
||||||
|
key = "RUST_BACKTRACE"
|
||||||
|
value = "full"
|
||||||
|
isEnabled = "YES">
|
||||||
|
</EnvironmentVariable>
|
||||||
|
<EnvironmentVariable
|
||||||
|
key = "RUST_LOG"
|
||||||
|
value = "info"
|
||||||
|
isEnabled = "YES">
|
||||||
|
</EnvironmentVariable>
|
||||||
|
</EnvironmentVariables>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>2.0.0</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>2.0.0</string>
|
||||||
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
<true/>
|
||||||
|
<key>UILaunchStoryboardName</key>
|
||||||
|
<string>LaunchScreen</string>
|
||||||
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
|
<array>
|
||||||
|
<string>arm64</string>
|
||||||
|
<string>metal</string>
|
||||||
|
</array>
|
||||||
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>To be able to scan barcodes</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict/>
|
||||||
|
</plist>
|
@ -0,0 +1,92 @@
|
|||||||
|
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
name: api
|
||||||
|
options:
|
||||||
|
bundleIdPrefix: com.tauri
|
||||||
|
deploymentTarget:
|
||||||
|
iOS: 13.0
|
||||||
|
fileGroups: [../../src]
|
||||||
|
configs:
|
||||||
|
debug: debug
|
||||||
|
release: release
|
||||||
|
settingGroups:
|
||||||
|
app:
|
||||||
|
base:
|
||||||
|
PRODUCT_NAME: Tauri API
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER: com.tauri.api
|
||||||
|
DEVELOPMENT_TEAM: Q93MBH6S2F
|
||||||
|
targetTemplates:
|
||||||
|
app:
|
||||||
|
type: application
|
||||||
|
sources:
|
||||||
|
- path: Sources
|
||||||
|
scheme:
|
||||||
|
environmentVariables:
|
||||||
|
RUST_BACKTRACE: full
|
||||||
|
RUST_LOG: info
|
||||||
|
settings:
|
||||||
|
groups: [app]
|
||||||
|
targets:
|
||||||
|
api_iOS:
|
||||||
|
type: application
|
||||||
|
platform: iOS
|
||||||
|
sources:
|
||||||
|
- path: Sources
|
||||||
|
- path: Assets.xcassets
|
||||||
|
- path: Externals
|
||||||
|
- path: api_iOS
|
||||||
|
- path: assets
|
||||||
|
buildPhase: resources
|
||||||
|
type: folder
|
||||||
|
info:
|
||||||
|
path: api_iOS/Info.plist
|
||||||
|
properties:
|
||||||
|
LSRequiresIPhoneOS: true
|
||||||
|
UILaunchStoryboardName: LaunchScreen
|
||||||
|
UIRequiredDeviceCapabilities: [arm64, metal]
|
||||||
|
UISupportedInterfaceOrientations:
|
||||||
|
- UIInterfaceOrientationPortrait
|
||||||
|
- UIInterfaceOrientationLandscapeLeft
|
||||||
|
- UIInterfaceOrientationLandscapeRight
|
||||||
|
UISupportedInterfaceOrientations~ipad:
|
||||||
|
- UIInterfaceOrientationPortrait
|
||||||
|
- UIInterfaceOrientationPortraitUpsideDown
|
||||||
|
- UIInterfaceOrientationLandscapeLeft
|
||||||
|
- UIInterfaceOrientationLandscapeRight
|
||||||
|
CFBundleShortVersionString: 2.0.0
|
||||||
|
CFBundleVersion: 2.0.0
|
||||||
|
entitlements:
|
||||||
|
path: api_iOS/api_iOS.entitlements
|
||||||
|
scheme:
|
||||||
|
environmentVariables:
|
||||||
|
RUST_BACKTRACE: full
|
||||||
|
RUST_LOG: info
|
||||||
|
settings:
|
||||||
|
base:
|
||||||
|
ENABLE_BITCODE: false
|
||||||
|
ARCHS: [arm64, arm64-sim]
|
||||||
|
VALID_ARCHS: arm64 arm64-sim
|
||||||
|
LIBRARY_SEARCH_PATHS[arch=x86_64]: $(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)
|
||||||
|
LIBRARY_SEARCH_PATHS[arch=arm64]: $(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)
|
||||||
|
LIBRARY_SEARCH_PATHS[arch=arm64-sim]: $(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: true
|
||||||
|
groups: [app]
|
||||||
|
dependencies:
|
||||||
|
- framework: libapi.a
|
||||||
|
embed: false
|
||||||
|
- sdk: CoreGraphics.framework
|
||||||
|
- sdk: Metal.framework
|
||||||
|
- sdk: MetalKit.framework
|
||||||
|
- sdk: QuartzCore.framework
|
||||||
|
- sdk: Security.framework
|
||||||
|
- sdk: UIKit.framework
|
||||||
|
- sdk: WebKit.framework
|
||||||
|
preBuildScripts:
|
||||||
|
- script: node /Users/lucas/projects/tauri/plugins-workspace/examples/api/./node_modules/.bin/../../../../node_modules/.pnpm/@tauri-apps+cli@2.0.0-alpha.14/node_modules/@tauri-apps/cli/tauri.js ios xcode-script -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --framework-search-paths "${FRAMEWORK_SEARCH_PATHS:?}" --header-search-paths "${HEADER_SEARCH_PATHS:?}" --gcc-preprocessor-definitions "${GCC_PREPROCESSOR_DEFINITIONS:-}" --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?}
|
||||||
|
name: Build Rust Code
|
||||||
|
basedOnDependencyAnalysis: false
|
||||||
|
outputFiles:
|
||||||
|
- $(SRCROOT)/target/aarch64-apple-ios/${CONFIGURATION}/deps/libapi.a
|
||||||
|
- $(SRCROOT)/target/x86_64-apple-ios/${CONFIGURATION}/deps/libapi.a
|
@ -0,0 +1,155 @@
|
|||||||
|
<script>
|
||||||
|
import { scan, checkPermissions, requestPermissions, Format, cancel } from "@tauri-apps/plugin-barcode-scanner";
|
||||||
|
|
||||||
|
export let onMessage;
|
||||||
|
|
||||||
|
let scanning = false;
|
||||||
|
let windowed = true;
|
||||||
|
let formats = [Format.QRCode];
|
||||||
|
const supportedFormats = [Format.QRCode, Format.EAN13];
|
||||||
|
|
||||||
|
async function startScan() {
|
||||||
|
let permission = await checkPermissions();
|
||||||
|
if (permission === 'prompt') {
|
||||||
|
permission = await requestPermissions();
|
||||||
|
}
|
||||||
|
if (permission === 'granted') {
|
||||||
|
scanning = true;
|
||||||
|
scan({ windowed, formats })
|
||||||
|
.then((res) => {
|
||||||
|
scanning = false;
|
||||||
|
onMessage(res);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
scanning = false;
|
||||||
|
onMessage(error);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
onMessage('Permission denied')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function cancelScan() {
|
||||||
|
await cancel();
|
||||||
|
scanning = false;
|
||||||
|
onMessage("cancelled");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="full-height">
|
||||||
|
<div class:invisible={scanning}>
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" id="scanner-windowed" bind:checked={windowed} />
|
||||||
|
<label for="scanner-windowed">Windowed</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<select class="input" id="format" multiple bind:value={formats}>
|
||||||
|
{#each supportedFormats as f}
|
||||||
|
<option value={f}>{f}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button class="btn" type="button" on:click={startScan}>Scan</button>
|
||||||
|
</div>
|
||||||
|
<div class="scanning full-height" class:invisible={!scanning}>
|
||||||
|
<div class="scanner-background">
|
||||||
|
<!-- this background simulates the camera view -->
|
||||||
|
</div>
|
||||||
|
<div class="container full-height">
|
||||||
|
<div class="barcode-scanner--area--container">
|
||||||
|
<div class="relative">
|
||||||
|
<p>Aim your camera at a QR code</p>
|
||||||
|
<button class="btn" type="button" on:click={cancelScan}>Cancel</button>
|
||||||
|
</div>
|
||||||
|
<div class="square surround-cover">
|
||||||
|
<div class="barcode-scanner--area--outer surround-cover">
|
||||||
|
<div class="barcode-scanner--area--inner" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.invisible {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-height {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: #fff;
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.relative {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.square {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: 0.3s;
|
||||||
|
}
|
||||||
|
.square:after {
|
||||||
|
content: "";
|
||||||
|
top: 0;
|
||||||
|
display: block;
|
||||||
|
padding-bottom: 100%;
|
||||||
|
}
|
||||||
|
.square > div {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.surround-cover {
|
||||||
|
box-shadow: 0 0 0 99999px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.barcode-scanner--area--container {
|
||||||
|
width: 80%;
|
||||||
|
max-width: min(500px, 80vh);
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
.barcode-scanner--area--outer {
|
||||||
|
display: flex;
|
||||||
|
border-radius: 1em;
|
||||||
|
}
|
||||||
|
.barcode-scanner--area--inner {
|
||||||
|
width: 100%;
|
||||||
|
margin: 1rem;
|
||||||
|
border: 2px solid #fff;
|
||||||
|
box-shadow: 0px 0px 2px 1px rgb(0 0 0 / 0.5),
|
||||||
|
inset 0px 0px 2px 1px rgb(0 0 0 / 0.5);
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scanner-background {
|
||||||
|
background: linear-gradient(45deg, #673ab7, transparent);
|
||||||
|
background-position: 45% 50%;
|
||||||
|
background-size: cover;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1 @@
|
|||||||
|
/.tauri
|
@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
name = "tauri-plugin-barcode-scanner"
|
||||||
|
version = "1.0.0"
|
||||||
|
description = "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS"
|
||||||
|
edition = { workspace = true }
|
||||||
|
authors = { workspace = true }
|
||||||
|
license = { workspace = true }
|
||||||
|
links = "tauri-plugin-barcode-scanner"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tauri-build = { workspace = true }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = { workspace = true }
|
||||||
|
serde_json = { workspace = true }
|
||||||
|
tauri = { workspace = true }
|
||||||
|
log = { workspace = true }
|
||||||
|
thiserror = { workspace = true }
|
@ -0,0 +1,20 @@
|
|||||||
|
SPDXVersion: SPDX-2.1
|
||||||
|
DataLicense: CC0-1.0
|
||||||
|
PackageName: tauri
|
||||||
|
DataFormat: SPDXRef-1
|
||||||
|
PackageSupplier: Organization: The Tauri Programme in the Commons Conservancy
|
||||||
|
PackageHomePage: https://tauri.app
|
||||||
|
PackageLicenseDeclared: Apache-2.0
|
||||||
|
PackageLicenseDeclared: MIT
|
||||||
|
PackageCopyrightText: 2019-2022, The Tauri Programme in the Commons Conservancy
|
||||||
|
PackageSummary: <text>Tauri is a rust project that enables developers to make secure
|
||||||
|
and small desktop applications using a web frontend.
|
||||||
|
</text>
|
||||||
|
PackageComment: <text>The package includes the following libraries; see
|
||||||
|
Relationship information.
|
||||||
|
</text>
|
||||||
|
Created: 2019-05-20T09:00:00Z
|
||||||
|
PackageDownloadLocation: git://github.com/tauri-apps/tauri
|
||||||
|
PackageDownloadLocation: git+https://github.com/tauri-apps/tauri.git
|
||||||
|
PackageDownloadLocation: git+ssh://github.com/tauri-apps/tauri.git
|
||||||
|
Creator: Person: Daniel Thompson-Yvetot
|
@ -0,0 +1,177 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 - Present Tauri Apps Contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
@ -0,0 +1,98 @@
|
|||||||
|

|
||||||
|
|
||||||
|
Allows your mobile application to use the camera to scan QR codes, EAN-13 and other kinds of barcodes.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
_This plugin requires a Rust version of at least **1.64**_
|
||||||
|
|
||||||
|
There are three general methods of installation that we can recommend.
|
||||||
|
|
||||||
|
1. Use crates.io and npm (easiest, and requires you to trust that our publishing pipeline worked)
|
||||||
|
2. Pull sources directly from Github using git tags / revision hashes (most secure)
|
||||||
|
3. Git submodule install this repo in your tauri project and then use file protocol to ingest the source (most secure, but inconvenient to use)
|
||||||
|
|
||||||
|
Install the Core plugin by adding the following to your `Cargo.toml` file:
|
||||||
|
|
||||||
|
`src-tauri/Cargo.toml`
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
tauri-plugin-barcode-scanner = "2.0.0-alpha"
|
||||||
|
# alternatively with Git:
|
||||||
|
tauri-plugin-barcode-scanner = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
|
||||||
|
```
|
||||||
|
|
||||||
|
You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
|
||||||
|
|
||||||
|
> Note: Since most JavaScript package managers are unable to install packages from git monorepos we provide read-only mirrors of each plugin. This makes installation option 2 more ergonomic to use.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm add @tauri-apps/plugin-barcode-scanner
|
||||||
|
# or
|
||||||
|
npm add @tauri-apps/plugin-barcode-scanner
|
||||||
|
# or
|
||||||
|
yarn add @tauri-apps/plugin-barcode-scanner
|
||||||
|
|
||||||
|
# alternatively with Git:
|
||||||
|
pnpm add https://github.com/tauri-apps/tauri-plugin-barcode-scanner#v2
|
||||||
|
# or
|
||||||
|
npm add https://github.com/tauri-apps/tauri-plugin-barcode-scanner#v2
|
||||||
|
# or
|
||||||
|
yarn add https://github.com/tauri-apps/tauri-plugin-barcode-scanner#v2
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
First you need to register the core plugin with Tauri:
|
||||||
|
|
||||||
|
`src-tauri/src/main.rs`
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
tauri::Builder::default()
|
||||||
|
.plugin(tauri_plugin_barcode_scanner::init())
|
||||||
|
.run(tauri::generate_context!())
|
||||||
|
.expect("error while running tauri application");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Afterwards all the plugin's APIs are available through the JavaScript guest bindings:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { scan } from "@tauri-apps/plugin-barcode-scanner";
|
||||||
|
|
||||||
|
// `windowed: true` actually sets the webview to transparent
|
||||||
|
// instead of opening a separate view for the camera
|
||||||
|
// make sure your user interface is ready to show what is underneath with a transparent element
|
||||||
|
scan({ windowed: true, formats: [""] })
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
PRs accepted. Please make sure to read the Contributing Guide before making a pull request.
|
||||||
|
|
||||||
|
## Contributed By
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td align="center" valign="middle">
|
||||||
|
<a href="https://crabnebula.dev" target="_blank">
|
||||||
|
<img src="contributors/crabnebula.svg" alt="CrabNebula" width="283">
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" valign="middle">
|
||||||
|
<a href="https://impierce.com" target="_blank">
|
||||||
|
<img src="contributors/impierce.svg" alt="Impierce" width="283" height="90">
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Code: (c) 2015 - Present - The Tauri Programme within The Commons Conservancy.
|
||||||
|
|
||||||
|
MIT or MIT/Apache 2.0 where applicable.
|
@ -0,0 +1,2 @@
|
|||||||
|
/build
|
||||||
|
/.tauri
|
@ -0,0 +1,56 @@
|
|||||||
|
plugins {
|
||||||
|
id("com.android.library")
|
||||||
|
id("org.jetbrains.kotlin.android")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "app.tauri.barcodescanner"
|
||||||
|
compileSdk = 32
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk = 24
|
||||||
|
targetSdk = 32
|
||||||
|
|
||||||
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
consumerProguardFiles("consumer-rules.pro")
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
isMinifyEnabled = false
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
"proguard-rules.pro"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
implementation("androidx.core:core-ktx:1.9.0")
|
||||||
|
implementation("androidx.appcompat:appcompat:1.6.0")
|
||||||
|
implementation("com.google.android.material:material:1.7.0")
|
||||||
|
implementation("androidx.camera:camera-core:1.1.0")
|
||||||
|
implementation("androidx.camera:camera-view:1.1.0")
|
||||||
|
implementation("androidx.camera:camera-lifecycle:1.1.0")
|
||||||
|
implementation("androidx.camera:camera-camera2:1.1.0")
|
||||||
|
implementation("androidx.camera:camera-lifecycle:1.1.0")
|
||||||
|
implementation("androidx.camera:camera-view:1.1.0")
|
||||||
|
implementation("com.google.android.gms:play-services-mlkit-barcode-scanning:18.1.0")
|
||||||
|
testImplementation("junit:junit:4.13.2")
|
||||||
|
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||||
|
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||||
|
implementation("com.journeyapps:zxing-android-embedded:4.3.0") {
|
||||||
|
isTransitive = false
|
||||||
|
}
|
||||||
|
implementation("com.google.zxing:core:3.3.0")
|
||||||
|
implementation(project(":tauri-android"))
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# 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 *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
@ -0,0 +1,2 @@
|
|||||||
|
include ':tauri-android'
|
||||||
|
project(':tauri-android').projectDir = new File('./.tauri/tauri-api')
|
@ -0,0 +1,28 @@
|
|||||||
|
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package app.tauri.barcodescanner
|
||||||
|
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
import org.junit.Assert.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
fun useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
assertEquals("app.tauri.barcodescanner", appContext.packageName)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:hardwareAccelerated="true">
|
||||||
|
|
||||||
|
<!-- Paste the following line into the AndroidManifest.xml of your project -->
|
||||||
|
<!-- See also: https://capacitorjs.com/docs/plugins/android#manifest -->
|
||||||
|
<!-- <uses-feature android:name="android.hardware.camera" android:required="false" /> -->
|
||||||
|
|
||||||
|
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
|
</manifest>
|
@ -0,0 +1,438 @@
|
|||||||
|
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package app.tauri.barcodescanner
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Context.MODE_PRIVATE
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.VibrationEffect
|
||||||
|
import android.os.Vibrator
|
||||||
|
import android.provider.Settings
|
||||||
|
import android.util.Size
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.webkit.WebView
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import androidx.activity.result.ActivityResult
|
||||||
|
import androidx.camera.core.Camera
|
||||||
|
import androidx.camera.core.CameraSelector
|
||||||
|
import androidx.camera.core.ImageAnalysis
|
||||||
|
import androidx.camera.core.ImageProxy
|
||||||
|
import androidx.camera.core.Preview
|
||||||
|
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||||
|
import androidx.camera.view.PreviewView
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import app.tauri.Logger
|
||||||
|
import app.tauri.PermissionState
|
||||||
|
import app.tauri.annotation.ActivityCallback
|
||||||
|
import app.tauri.annotation.Command
|
||||||
|
import app.tauri.annotation.Permission
|
||||||
|
import app.tauri.annotation.PermissionCallback
|
||||||
|
import app.tauri.annotation.TauriPlugin
|
||||||
|
import app.tauri.plugin.Invoke
|
||||||
|
import app.tauri.plugin.JSArray
|
||||||
|
import app.tauri.plugin.JSObject
|
||||||
|
import app.tauri.plugin.Plugin
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
|
import com.google.mlkit.vision.barcode.BarcodeScannerOptions
|
||||||
|
import com.google.mlkit.vision.barcode.BarcodeScanning
|
||||||
|
import com.google.mlkit.vision.barcode.common.Barcode
|
||||||
|
import com.google.mlkit.vision.common.InputImage
|
||||||
|
import org.json.JSONException
|
||||||
|
import java.util.Collections
|
||||||
|
import java.util.concurrent.ExecutionException
|
||||||
|
|
||||||
|
private const val PERMISSION_ALIAS_CAMERA = "camera"
|
||||||
|
private const val PERMISSION_NAME = Manifest.permission.CAMERA
|
||||||
|
private const val PREFS_PERMISSION_FIRST_TIME_ASKING = "PREFS_PERMISSION_FIRST_TIME_ASKING"
|
||||||
|
|
||||||
|
@TauriPlugin(
|
||||||
|
permissions = [
|
||||||
|
Permission(strings = [Manifest.permission.CAMERA], alias = "camera")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
class BarcodeScannerPlugin(private val activity: Activity) : Plugin(activity),
|
||||||
|
ImageAnalysis.Analyzer {
|
||||||
|
private lateinit var webView: WebView
|
||||||
|
private var previewView: PreviewView? = null
|
||||||
|
private var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>? = null
|
||||||
|
private var cameraProvider: ProcessCameraProvider? = null
|
||||||
|
private var graphicOverlay: GraphicOverlay? = null
|
||||||
|
private var camera: Camera? = null
|
||||||
|
private var vibrator: Vibrator? = null
|
||||||
|
|
||||||
|
private var scannerOptions: BarcodeScannerOptions? = null
|
||||||
|
private var scanner: com.google.mlkit.vision.barcode.BarcodeScanner? = null
|
||||||
|
|
||||||
|
private var requestPermissionResponse: JSObject? = null
|
||||||
|
private var windowed = false
|
||||||
|
|
||||||
|
// declare a map constant for allowed barcode formats
|
||||||
|
private val supportedFormats = supportedFormats()
|
||||||
|
|
||||||
|
private var savedInvoke: Invoke? = null
|
||||||
|
private var webViewBackground: Drawable? = null
|
||||||
|
|
||||||
|
override fun load(webView: WebView) {
|
||||||
|
super.load(webView)
|
||||||
|
this.webView = webView
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun supportedFormats(): Map<String, Int> {
|
||||||
|
val map: MutableMap<String, Int> = HashMap()
|
||||||
|
map["UPC_A"] = Barcode.FORMAT_UPC_A
|
||||||
|
map["UPC_E"] = Barcode.FORMAT_UPC_E
|
||||||
|
map["EAN_8"] = Barcode.FORMAT_EAN_8
|
||||||
|
map["EAN_13"] = Barcode.FORMAT_EAN_13
|
||||||
|
map["CODE_39"] = Barcode.FORMAT_CODE_39
|
||||||
|
map["CODE_93"] = Barcode.FORMAT_CODE_93
|
||||||
|
map["CODE_128"] = Barcode.FORMAT_CODE_128
|
||||||
|
map["CODABAR"] = Barcode.FORMAT_CODABAR
|
||||||
|
map["ITF"] = Barcode.FORMAT_ITF
|
||||||
|
map["AZTEC"] = Barcode.FORMAT_AZTEC
|
||||||
|
map["DATA_MATRIX"] = Barcode.FORMAT_DATA_MATRIX
|
||||||
|
map["PDF_417"] = Barcode.FORMAT_PDF417
|
||||||
|
map["QR_CODE"] = Barcode.FORMAT_QR_CODE
|
||||||
|
return Collections.unmodifiableMap(map)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hasCamera(): Boolean {
|
||||||
|
return activity.packageManager
|
||||||
|
.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupCamera(cameraDirection: String, windowed: Boolean) {
|
||||||
|
activity
|
||||||
|
.runOnUiThread {
|
||||||
|
val previewView = PreviewView(activity)
|
||||||
|
previewView.layoutParams = FrameLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
|
)
|
||||||
|
this.previewView = previewView
|
||||||
|
|
||||||
|
val graphicOverlay = GraphicOverlay(activity)
|
||||||
|
graphicOverlay.layoutParams = FrameLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
|
)
|
||||||
|
this.graphicOverlay = graphicOverlay
|
||||||
|
|
||||||
|
val parent = webView.parent as ViewGroup
|
||||||
|
parent.addView(previewView)
|
||||||
|
parent.addView(graphicOverlay)
|
||||||
|
|
||||||
|
this.windowed = windowed
|
||||||
|
if (windowed) {
|
||||||
|
webView.bringToFront()
|
||||||
|
webViewBackground = webView.background
|
||||||
|
webView.setBackgroundColor(Color.TRANSPARENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
val cameraProviderFuture = ProcessCameraProvider.getInstance(activity)
|
||||||
|
cameraProviderFuture.addListener(
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
val cameraProvider = cameraProviderFuture.get()
|
||||||
|
bindPreview(
|
||||||
|
cameraProvider,
|
||||||
|
if (cameraDirection == "front") CameraSelector.LENS_FACING_FRONT else CameraSelector.LENS_FACING_BACK
|
||||||
|
)
|
||||||
|
this.cameraProvider = cameraProvider
|
||||||
|
} catch (e: InterruptedException) {
|
||||||
|
// ignored
|
||||||
|
} catch (_: ExecutionException) {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ContextCompat.getMainExecutor(activity)
|
||||||
|
)
|
||||||
|
this.cameraProviderFuture = cameraProviderFuture
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindPreview(cameraProvider: ProcessCameraProvider, cameraDirection: Int) {
|
||||||
|
activity
|
||||||
|
.runOnUiThread {
|
||||||
|
val preview = Preview.Builder().build()
|
||||||
|
val cameraSelector =
|
||||||
|
CameraSelector.Builder().requireLensFacing(cameraDirection).build()
|
||||||
|
preview.setSurfaceProvider(previewView?.surfaceProvider)
|
||||||
|
val imageAnalysis = ImageAnalysis.Builder()
|
||||||
|
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
||||||
|
.setTargetResolution(Size(1280, 720))
|
||||||
|
.build()
|
||||||
|
imageAnalysis.setAnalyzer(
|
||||||
|
ContextCompat.getMainExecutor(activity),
|
||||||
|
this
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
camera = cameraProvider.bindToLifecycle(
|
||||||
|
activity as LifecycleOwner,
|
||||||
|
cameraSelector,
|
||||||
|
preview,
|
||||||
|
imageAnalysis
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun dismantleCamera() {
|
||||||
|
activity
|
||||||
|
.runOnUiThread {
|
||||||
|
if (cameraProvider != null) {
|
||||||
|
cameraProvider?.unbindAll()
|
||||||
|
val parent = webView.parent as ViewGroup
|
||||||
|
parent.removeView(previewView)
|
||||||
|
parent.removeView(graphicOverlay)
|
||||||
|
camera = null
|
||||||
|
previewView = null
|
||||||
|
graphicOverlay = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getFormats(invoke: Invoke): List<Int> {
|
||||||
|
val jsFormats = invoke.getArray("formats", JSArray())
|
||||||
|
val formats = ArrayList<Int>()
|
||||||
|
for (i in 0 until jsFormats.length()) {
|
||||||
|
try {
|
||||||
|
val targetedFormat: String = jsFormats.getString(i)
|
||||||
|
val targetedBarcodeFormat =
|
||||||
|
supportedFormats[targetedFormat]
|
||||||
|
if (targetedBarcodeFormat != null) {
|
||||||
|
formats.add(targetedBarcodeFormat)
|
||||||
|
}
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return formats
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun prepare(direction: String, windowed: Boolean) {
|
||||||
|
dismantleCamera()
|
||||||
|
setupCamera(direction, windowed)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun destroy() {
|
||||||
|
dismantleCamera()
|
||||||
|
savedInvoke = null
|
||||||
|
if (windowed) {
|
||||||
|
if (webViewBackground != null) {
|
||||||
|
webView.background = webViewBackground
|
||||||
|
webViewBackground = null
|
||||||
|
} else {
|
||||||
|
webView.setBackgroundColor(Color.WHITE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
private fun configureCamera(formats: List<Int>) {
|
||||||
|
activity
|
||||||
|
.runOnUiThread {
|
||||||
|
val vibrator =
|
||||||
|
activity.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
||||||
|
this.vibrator = vibrator
|
||||||
|
if (previewView == null) {
|
||||||
|
throw Exception("Something went wrong configuring the BarcodeScanner")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formats.isNotEmpty()) {
|
||||||
|
val mappedFormats = mapFormats(formats)
|
||||||
|
val options =
|
||||||
|
BarcodeScannerOptions.Builder()
|
||||||
|
.setBarcodeFormats(Barcode.FORMAT_QR_CODE, *mappedFormats).build()
|
||||||
|
scannerOptions = options
|
||||||
|
scanner = BarcodeScanning.getClient(options)
|
||||||
|
} else {
|
||||||
|
val options = BarcodeScannerOptions.Builder()
|
||||||
|
.setBarcodeFormats(Barcode.FORMAT_ALL_FORMATS).build()
|
||||||
|
scannerOptions = options
|
||||||
|
scanner = BarcodeScanning.getClient(options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mapFormats(integers: List<Int>): IntArray {
|
||||||
|
val ret = IntArray(integers.size)
|
||||||
|
for (i in ret.indices) {
|
||||||
|
if (integers[i] != Barcode.FORMAT_QR_CODE) ret[i] = integers[i]
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun analyze(image: ImageProxy) {
|
||||||
|
@SuppressLint("UnsafeOptInUsageError") val mediaImage = image.image
|
||||||
|
if (mediaImage != null) {
|
||||||
|
val inputImage =
|
||||||
|
InputImage.fromMediaImage(mediaImage, image.imageInfo.rotationDegrees)
|
||||||
|
scanner
|
||||||
|
?.process(inputImage)
|
||||||
|
?.addOnSuccessListener { barcodes ->
|
||||||
|
if (barcodes.isNotEmpty()) {
|
||||||
|
val barcode = barcodes[0]
|
||||||
|
val bounds = barcode.boundingBox
|
||||||
|
val rawValue = barcode.rawValue ?: ""
|
||||||
|
val rawFormat = barcode.format
|
||||||
|
var format: String? = null
|
||||||
|
|
||||||
|
for (entry in supportedFormats.entries) {
|
||||||
|
if (entry.value == rawFormat) {
|
||||||
|
format = entry.key
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val s = bounds?.flattenToString()
|
||||||
|
val jsObject = JSObject()
|
||||||
|
jsObject.put("content", rawValue)
|
||||||
|
jsObject.put("format", format)
|
||||||
|
jsObject.put("bounds", s)
|
||||||
|
|
||||||
|
savedInvoke?.resolve(jsObject)
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?.addOnFailureListener { e ->
|
||||||
|
Logger.error(e.message ?: e.toString())
|
||||||
|
}
|
||||||
|
?.addOnCompleteListener {
|
||||||
|
image.close()
|
||||||
|
mediaImage.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command
|
||||||
|
fun vibrate(invoke: Invoke) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
vibrator!!.vibrate(
|
||||||
|
VibrationEffect.createOneShot(
|
||||||
|
50,
|
||||||
|
VibrationEffect.DEFAULT_AMPLITUDE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
invoke.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command
|
||||||
|
fun cancel(invoke: Invoke) {
|
||||||
|
destroy()
|
||||||
|
savedInvoke?.reject("cancelled")
|
||||||
|
invoke.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command
|
||||||
|
fun scan(invoke: Invoke) {
|
||||||
|
savedInvoke = invoke
|
||||||
|
if (hasCamera()) {
|
||||||
|
if (getPermissionState("camera") != PermissionState.GRANTED) {
|
||||||
|
throw Exception("No permission to use camera. Did you request it yet?")
|
||||||
|
} else {
|
||||||
|
webViewBackground = null
|
||||||
|
prepare(invoke.getString("cameraDirection", "back"), invoke.getBoolean("windowed", false))
|
||||||
|
configureCamera(getFormats(invoke))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun markFirstPermissionRequest() {
|
||||||
|
val sharedPreference: SharedPreferences =
|
||||||
|
activity.getSharedPreferences(PREFS_PERMISSION_FIRST_TIME_ASKING, MODE_PRIVATE)
|
||||||
|
sharedPreference.edit().putBoolean(PERMISSION_NAME, false).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun firstPermissionRequest(): Boolean {
|
||||||
|
return activity.getSharedPreferences(PREFS_PERMISSION_FIRST_TIME_ASKING, MODE_PRIVATE)
|
||||||
|
.getBoolean(PERMISSION_NAME, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ObsoleteSdkInt")
|
||||||
|
@PermissionCallback
|
||||||
|
fun cameraPermissionCallback(invoke: Invoke) {
|
||||||
|
if (requestPermissionResponse == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val requestPermissionResponse = requestPermissionResponse!!
|
||||||
|
|
||||||
|
val granted = getPermissionState(PERMISSION_ALIAS_CAMERA) === PermissionState.GRANTED
|
||||||
|
|
||||||
|
if (granted) {
|
||||||
|
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.GRANTED)
|
||||||
|
} else {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
if (!activity.shouldShowRequestPermissionRationale(PERMISSION_NAME)) {
|
||||||
|
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.DENIED)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.GRANTED)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
invoke.resolve(requestPermissionResponse)
|
||||||
|
this.requestPermissionResponse = null
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ObsoleteSdkInt")
|
||||||
|
@Command
|
||||||
|
override fun requestPermissions(invoke: Invoke) {
|
||||||
|
val requestPermissionResponse = JSObject()
|
||||||
|
this.requestPermissionResponse = requestPermissionResponse
|
||||||
|
if (getPermissionState(PERMISSION_ALIAS_CAMERA) === PermissionState.GRANTED) {
|
||||||
|
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.GRANTED)
|
||||||
|
} else {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
if (firstPermissionRequest() || activity.shouldShowRequestPermissionRationale(
|
||||||
|
PERMISSION_NAME
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
markFirstPermissionRequest()
|
||||||
|
requestPermissionForAlias(
|
||||||
|
PERMISSION_ALIAS_CAMERA,
|
||||||
|
invoke,
|
||||||
|
"cameraPermissionCallback"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.DENIED)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.GRANTED)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
invoke.resolve(requestPermissionResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command
|
||||||
|
fun openAppSettings(invoke: Invoke) {
|
||||||
|
val intent = Intent(
|
||||||
|
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
|
||||||
|
Uri.fromParts("package", activity.packageName, null)
|
||||||
|
)
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
startActivityForResult(invoke, intent, "openSettingsResult")
|
||||||
|
}
|
||||||
|
|
||||||
|
@ActivityCallback
|
||||||
|
private fun openSettingsResult(invoke: Invoke, result: ActivityResult) {
|
||||||
|
invoke.resolve()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,192 @@
|
|||||||
|
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package app.tauri.barcodescanner
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Matrix
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.View
|
||||||
|
import com.google.android.gms.common.internal.Preconditions
|
||||||
|
|
||||||
|
class GraphicOverlay: View {
|
||||||
|
private val lock = Any()
|
||||||
|
private val graphics: MutableList<Graphic> = ArrayList()
|
||||||
|
|
||||||
|
private val transformationMatrix = Matrix()
|
||||||
|
|
||||||
|
private var imageWidth = 0
|
||||||
|
private var imageHeight = 0
|
||||||
|
|
||||||
|
private var scaleFactor = 1.0f
|
||||||
|
|
||||||
|
private var postScaleWidthOffset = 0f
|
||||||
|
|
||||||
|
private var postScaleHeightOffset = 0f
|
||||||
|
private var isImageFlipped = false
|
||||||
|
private var needUpdateTransformation = true
|
||||||
|
|
||||||
|
abstract class Graphic(private val overlay: GraphicOverlay) {
|
||||||
|
abstract fun draw(canvas: Canvas?)
|
||||||
|
protected fun drawRect(
|
||||||
|
canvas: Canvas,
|
||||||
|
left: Float,
|
||||||
|
top: Float,
|
||||||
|
right: Float,
|
||||||
|
bottom: Float,
|
||||||
|
paint: Paint?
|
||||||
|
) {
|
||||||
|
canvas.drawRect(left, top, right, bottom, paint!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun drawText(canvas: Canvas, text: String?, x: Float, y: Float, paint: Paint?) {
|
||||||
|
canvas.drawText(text!!, x, y, paint!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Adjusts the supplied value from the image scale to the view scale. */
|
||||||
|
fun scale(imagePixel: Float): Float {
|
||||||
|
return imagePixel * overlay.scaleFactor
|
||||||
|
}
|
||||||
|
|
||||||
|
val applicationContext
|
||||||
|
get() = overlay.context.applicationContext
|
||||||
|
|
||||||
|
fun isImageFlipped(): Boolean {
|
||||||
|
return overlay.isImageFlipped
|
||||||
|
}
|
||||||
|
|
||||||
|
fun translateX(x: Float): Float {
|
||||||
|
return if (overlay.isImageFlipped) {
|
||||||
|
overlay.width - (scale(x) - overlay.postScaleWidthOffset)
|
||||||
|
} else {
|
||||||
|
scale(x) - overlay.postScaleWidthOffset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun translateY(y: Float): Float {
|
||||||
|
return scale(y) - overlay.postScaleHeightOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTransformationMatrix(): Matrix {
|
||||||
|
return overlay.transformationMatrix
|
||||||
|
}
|
||||||
|
|
||||||
|
fun postInvalidate() {
|
||||||
|
overlay.postInvalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updatePaintColorByZValue(
|
||||||
|
paint: Paint,
|
||||||
|
canvas: Canvas,
|
||||||
|
visualizeZ: Boolean,
|
||||||
|
rescaleZForVisualization: Boolean,
|
||||||
|
zInImagePixel: Float,
|
||||||
|
zMin: Float,
|
||||||
|
zMax: Float
|
||||||
|
) {
|
||||||
|
if (!visualizeZ) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val zLowerBoundInScreenPixel: Float
|
||||||
|
val zUpperBoundInScreenPixel: Float
|
||||||
|
if (rescaleZForVisualization) {
|
||||||
|
zLowerBoundInScreenPixel = (-0.001f).coerceAtMost(scale(zMin))
|
||||||
|
zUpperBoundInScreenPixel = 0.001f.coerceAtLeast(scale(zMax))
|
||||||
|
} else {
|
||||||
|
val defaultRangeFactor = 1f
|
||||||
|
zLowerBoundInScreenPixel = -defaultRangeFactor * canvas.width
|
||||||
|
zUpperBoundInScreenPixel = defaultRangeFactor * canvas.width
|
||||||
|
}
|
||||||
|
val zInScreenPixel = scale(zInImagePixel)
|
||||||
|
if (zInScreenPixel < 0) {
|
||||||
|
val v = (zInScreenPixel / zLowerBoundInScreenPixel * 255).toInt()
|
||||||
|
paint.setARGB(0, 0, 255, 0)
|
||||||
|
} else {
|
||||||
|
val v = (zInScreenPixel / zUpperBoundInScreenPixel * 255).toInt()
|
||||||
|
paint.setARGB(0, 0, 255, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(context: Context): super(context)
|
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet): super(context, attrs) {
|
||||||
|
addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
|
||||||
|
needUpdateTransformation = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
synchronized(lock) { graphics.clear() }
|
||||||
|
postInvalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun add(graphic: Graphic) {
|
||||||
|
synchronized(lock) { graphics.add(graphic) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun remove(graphic: Graphic) {
|
||||||
|
synchronized(lock) { graphics.remove(graphic) }
|
||||||
|
postInvalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setImageSourceInfo(imageWidth: Int, imageHeight: Int, isFlipped: Boolean) {
|
||||||
|
Preconditions.checkState(imageWidth > 0, "image width must be positive")
|
||||||
|
Preconditions.checkState(imageHeight > 0, "image height must be positive")
|
||||||
|
synchronized(lock) {
|
||||||
|
this.imageWidth = imageWidth
|
||||||
|
this.imageHeight = imageHeight
|
||||||
|
isImageFlipped = isFlipped
|
||||||
|
needUpdateTransformation = true
|
||||||
|
}
|
||||||
|
postInvalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getImageWidth(): Int {
|
||||||
|
return imageWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getImageHeight(): Int {
|
||||||
|
return imageHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateTransformationIfNeeded() {
|
||||||
|
if (!needUpdateTransformation || imageWidth <= 0 || imageHeight <= 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val viewAspectRatio = width.toFloat() / height
|
||||||
|
val imageAspectRatio = imageWidth.toFloat() / imageHeight
|
||||||
|
postScaleWidthOffset = 0f
|
||||||
|
postScaleHeightOffset = 0f
|
||||||
|
if (viewAspectRatio > imageAspectRatio) {
|
||||||
|
// The image needs to be vertically cropped to be displayed in this view.
|
||||||
|
scaleFactor = width.toFloat() / imageWidth
|
||||||
|
postScaleHeightOffset = (width.toFloat() / imageAspectRatio - height) / 2
|
||||||
|
} else {
|
||||||
|
// The image needs to be horizontally cropped to be displayed in this view.
|
||||||
|
scaleFactor = height.toFloat() / imageHeight
|
||||||
|
postScaleWidthOffset = (height.toFloat() * imageAspectRatio - width) / 2
|
||||||
|
}
|
||||||
|
transformationMatrix.reset()
|
||||||
|
transformationMatrix.setScale(scaleFactor, scaleFactor)
|
||||||
|
transformationMatrix.postTranslate(-postScaleWidthOffset, -postScaleHeightOffset)
|
||||||
|
if (isImageFlipped) {
|
||||||
|
transformationMatrix.postScale(-1f, 1f, width / 2f, height / 2f)
|
||||||
|
}
|
||||||
|
needUpdateTransformation = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas?) {
|
||||||
|
super.onDraw(canvas)
|
||||||
|
synchronized(lock) {
|
||||||
|
updateTransformationIfNeeded()
|
||||||
|
for (graphic in graphics) {
|
||||||
|
graphic.draw(canvas)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package app.tauri.barcodescanner
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
import org.junit.Assert.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example local unit test, which will execute on the development machine (host).
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
class ExampleUnitTest {
|
||||||
|
@Test
|
||||||
|
fun addition_isCorrect() {
|
||||||
|
assertEquals(4, 2 + 2)
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 48 KiB |
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if let Err(error) = tauri_build::mobile::PluginBuilder::new()
|
||||||
|
.android_path("android")
|
||||||
|
.ios_path("ios")
|
||||||
|
.run()
|
||||||
|
{
|
||||||
|
println!("{error:#}");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 14 KiB |
@ -0,0 +1,75 @@
|
|||||||
|
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import { invoke } from "@tauri-apps/api/tauri";
|
||||||
|
|
||||||
|
export type PermissionState = "granted" | "denied" | "prompt";
|
||||||
|
|
||||||
|
export enum Format {
|
||||||
|
QRCode = "QR_CODE",
|
||||||
|
UPC_A = "UPC_A",
|
||||||
|
UPC_E = "UPC_E",
|
||||||
|
EAN8 = "EAN_8",
|
||||||
|
EAN13 = "EAN_13",
|
||||||
|
Code39 = "CODE_39",
|
||||||
|
Code93 = "CODE_93",
|
||||||
|
Code128 = "CODE_128",
|
||||||
|
Codabar = "CODABAR",
|
||||||
|
ITF = "ITF",
|
||||||
|
Aztec = "AZTEC",
|
||||||
|
DataMatrix = "DATA_MATRIX",
|
||||||
|
PDF417 = "PDF_417",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ScanOptions {
|
||||||
|
cameraDirection?: "back" | "front";
|
||||||
|
formats?: Format[];
|
||||||
|
windowed?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Scanned {
|
||||||
|
content: string;
|
||||||
|
format: Format;
|
||||||
|
bounds: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start scanning.
|
||||||
|
* @param options
|
||||||
|
*/
|
||||||
|
export async function scan(options?: ScanOptions): Promise<Scanned> {
|
||||||
|
return await invoke("plugin:barcodeScanner|scan", { ...options });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel the current scan process.
|
||||||
|
*/
|
||||||
|
export async function cancel(): Promise<void> {
|
||||||
|
return await invoke("plugin:barcodeScanner|cancel");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get permission state.
|
||||||
|
*/
|
||||||
|
export async function checkPermissions(): Promise<PermissionState> {
|
||||||
|
return await invoke<{ camera: PermissionState }>(
|
||||||
|
"plugin:barcodeScanner|checkPermissions",
|
||||||
|
).then((r) => r.camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request permissions to use the camera.
|
||||||
|
*/
|
||||||
|
export async function requestPermissions(): Promise<PermissionState> {
|
||||||
|
return await invoke<{ camera: PermissionState }>(
|
||||||
|
"plugin:barcodeScanner|requestPermissions",
|
||||||
|
).then((r) => r.camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open application settings. Useful if permission was denied and the user must manually enable it.
|
||||||
|
*/
|
||||||
|
export async function openAppSettings(): Promise<void> {
|
||||||
|
return await invoke("plugin:barcodeScanner|openAppSettings");
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
/*.xcodeproj
|
||||||
|
xcuserdata/
|
||||||
|
DerivedData/
|
||||||
|
.swiftpm/config/registries.json
|
||||||
|
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||||
|
.netrc
|
||||||
|
Package.resolved
|
@ -0,0 +1,35 @@
|
|||||||
|
// swift-tools-version:5.3
|
||||||
|
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "tauri-plugin-barcode-scanner",
|
||||||
|
platforms: [
|
||||||
|
.iOS(.v13)
|
||||||
|
],
|
||||||
|
products: [
|
||||||
|
// Products define the executables and libraries a package produces, and make them visible to other packages.
|
||||||
|
.library(
|
||||||
|
name: "tauri-plugin-barcode-scanner",
|
||||||
|
type: .static,
|
||||||
|
targets: ["tauri-plugin-barcode-scanner"])
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
.package(name: "Tauri", path: "../.tauri/tauri-api")
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||||
|
// Targets can depend on other targets in this package, and on products in packages this package depends on.
|
||||||
|
.target(
|
||||||
|
name: "tauri-plugin-barcode-scanner",
|
||||||
|
dependencies: [
|
||||||
|
.byName(name: "Tauri")
|
||||||
|
],
|
||||||
|
path: "Sources")
|
||||||
|
]
|
||||||
|
)
|
@ -0,0 +1,3 @@
|
|||||||
|
# Tauri Plugin Barcode Scanner
|
||||||
|
|
||||||
|
A description of this package.
|
@ -0,0 +1,300 @@
|
|||||||
|
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import AVFoundation
|
||||||
|
import Tauri
|
||||||
|
import UIKit
|
||||||
|
import WebKit
|
||||||
|
|
||||||
|
enum SupportedFormat: String, CaseIterable {
|
||||||
|
// UPC_A not supported
|
||||||
|
case UPC_E
|
||||||
|
case EAN_8
|
||||||
|
case EAN_13
|
||||||
|
case CODE_39
|
||||||
|
case CODE_93
|
||||||
|
case CODE_128
|
||||||
|
// CODABAR not supported
|
||||||
|
case ITF
|
||||||
|
case AZTEC
|
||||||
|
case DATA_MATRIX
|
||||||
|
case PDF_417
|
||||||
|
case QR_CODE
|
||||||
|
|
||||||
|
var value: AVMetadataObject.ObjectType {
|
||||||
|
switch self {
|
||||||
|
case .UPC_E: return AVMetadataObject.ObjectType.upce
|
||||||
|
case .EAN_8: return AVMetadataObject.ObjectType.ean8
|
||||||
|
case .EAN_13: return AVMetadataObject.ObjectType.ean13
|
||||||
|
case .CODE_39: return AVMetadataObject.ObjectType.code39
|
||||||
|
case .CODE_93: return AVMetadataObject.ObjectType.code93
|
||||||
|
case .CODE_128: return AVMetadataObject.ObjectType.code128
|
||||||
|
case .ITF: return AVMetadataObject.ObjectType.interleaved2of5
|
||||||
|
case .AZTEC: return AVMetadataObject.ObjectType.aztec
|
||||||
|
case .DATA_MATRIX: return AVMetadataObject.ObjectType.dataMatrix
|
||||||
|
case .PDF_417: return AVMetadataObject.ObjectType.pdf417
|
||||||
|
case .QR_CODE: return AVMetadataObject.ObjectType.qr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CaptureError: Error {
|
||||||
|
case backCameraUnavailable
|
||||||
|
case frontCameraUnavailable
|
||||||
|
case couldNotCaptureInput(error: NSError)
|
||||||
|
}
|
||||||
|
|
||||||
|
class BarcodeScannerPlugin: Plugin, AVCaptureMetadataOutputObjectsDelegate {
|
||||||
|
var webView: WKWebView!
|
||||||
|
var cameraView: CameraView!
|
||||||
|
var captureSession: AVCaptureSession?
|
||||||
|
var captureVideoPreviewLayer: AVCaptureVideoPreviewLayer?
|
||||||
|
var metaOutput: AVCaptureMetadataOutput?
|
||||||
|
|
||||||
|
var currentCamera = 0
|
||||||
|
var frontCamera: AVCaptureDevice?
|
||||||
|
var backCamera: AVCaptureDevice?
|
||||||
|
|
||||||
|
var isScanning = false
|
||||||
|
|
||||||
|
var windowed = false
|
||||||
|
var previousBackgroundColor: UIColor? = UIColor.white
|
||||||
|
|
||||||
|
var invoke: Invoke? = nil
|
||||||
|
|
||||||
|
var scanFormats = [AVMetadataObject.ObjectType]()
|
||||||
|
|
||||||
|
public override func load(webview: WKWebView) {
|
||||||
|
self.webView = webview
|
||||||
|
loadCamera()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadCamera() {
|
||||||
|
cameraView = CameraView(
|
||||||
|
frame: CGRect(
|
||||||
|
x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
|
||||||
|
cameraView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||||
|
}
|
||||||
|
|
||||||
|
public func metadataOutput(
|
||||||
|
_ captureOutput: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject],
|
||||||
|
from connection: AVCaptureConnection
|
||||||
|
) {
|
||||||
|
if metadataObjects.count == 0 || !self.isScanning {
|
||||||
|
// while nothing is detected, or if scanning is false, do nothing.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let found = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
|
||||||
|
if scanFormats.contains(found.type) {
|
||||||
|
var jsObject: JsonObject = [:]
|
||||||
|
|
||||||
|
jsObject["format"] = formatStringFromMetadata(found.type)
|
||||||
|
if found.stringValue != nil {
|
||||||
|
jsObject["content"] = found.stringValue
|
||||||
|
}
|
||||||
|
|
||||||
|
invoke?.resolve(jsObject)
|
||||||
|
destroy()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupCamera(direction: String, windowed: Bool) {
|
||||||
|
do {
|
||||||
|
var cameraDirection = direction
|
||||||
|
cameraView.backgroundColor = UIColor.clear
|
||||||
|
if windowed {
|
||||||
|
webView.superview?.insertSubview(cameraView, belowSubview: webView)
|
||||||
|
} else {
|
||||||
|
webView.superview?.insertSubview(cameraView, aboveSubview: webView)
|
||||||
|
}
|
||||||
|
|
||||||
|
let availableVideoDevices = discoverCaptureDevices()
|
||||||
|
for device in availableVideoDevices {
|
||||||
|
if device.position == AVCaptureDevice.Position.back {
|
||||||
|
backCamera = device
|
||||||
|
} else if device.position == AVCaptureDevice.Position.front {
|
||||||
|
frontCamera = device
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// older iPods have no back camera
|
||||||
|
if cameraDirection == "back" {
|
||||||
|
if backCamera == nil {
|
||||||
|
cameraDirection = "front"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if frontCamera == nil {
|
||||||
|
cameraDirection = "back"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let input: AVCaptureDeviceInput
|
||||||
|
input = try createCaptureDeviceInput(
|
||||||
|
cameraDirection: cameraDirection, backCamera: backCamera, frontCamera: frontCamera)
|
||||||
|
captureSession = AVCaptureSession()
|
||||||
|
captureSession!.addInput(input)
|
||||||
|
metaOutput = AVCaptureMetadataOutput()
|
||||||
|
captureSession!.addOutput(metaOutput!)
|
||||||
|
metaOutput!.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
|
||||||
|
captureVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
|
||||||
|
cameraView.addPreviewLayer(captureVideoPreviewLayer)
|
||||||
|
|
||||||
|
self.windowed = windowed
|
||||||
|
if windowed {
|
||||||
|
self.previousBackgroundColor = self.webView.backgroundColor
|
||||||
|
self.webView.isOpaque = false
|
||||||
|
self.webView.backgroundColor = UIColor.clear
|
||||||
|
self.webView.scrollView.backgroundColor = UIColor.clear
|
||||||
|
}
|
||||||
|
} catch CaptureError.backCameraUnavailable {
|
||||||
|
//
|
||||||
|
} catch CaptureError.frontCameraUnavailable {
|
||||||
|
//
|
||||||
|
} catch CaptureError.couldNotCaptureInput {
|
||||||
|
//
|
||||||
|
} catch {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func dismantleCamera() {
|
||||||
|
if self.captureSession != nil {
|
||||||
|
self.captureSession!.stopRunning()
|
||||||
|
self.cameraView.removePreviewLayer()
|
||||||
|
self.captureVideoPreviewLayer = nil
|
||||||
|
self.metaOutput = nil
|
||||||
|
self.captureSession = nil
|
||||||
|
self.frontCamera = nil
|
||||||
|
self.backCamera = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
self.isScanning = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private func destroy() {
|
||||||
|
dismantleCamera()
|
||||||
|
invoke = nil
|
||||||
|
if windowed {
|
||||||
|
let backgroundColor = previousBackgroundColor ?? UIColor.white
|
||||||
|
webView.isOpaque = true
|
||||||
|
webView.backgroundColor = backgroundColor
|
||||||
|
webView.scrollView.backgroundColor = backgroundColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getPermissionState() -> String {
|
||||||
|
var permissionState: String
|
||||||
|
|
||||||
|
switch AVCaptureDevice.authorizationStatus(for: .video) {
|
||||||
|
case .authorized:
|
||||||
|
permissionState = "granted"
|
||||||
|
case .denied:
|
||||||
|
permissionState = "denied"
|
||||||
|
default:
|
||||||
|
permissionState = "prompt"
|
||||||
|
}
|
||||||
|
|
||||||
|
return permissionState
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc override func checkPermissions(_ invoke: Invoke) {
|
||||||
|
let permissionState = getPermissionState()
|
||||||
|
invoke.resolve(["camera": permissionState])
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc override func requestPermissions(_ invoke: Invoke) {
|
||||||
|
let state = getPermissionState()
|
||||||
|
if state == "prompt" {
|
||||||
|
AVCaptureDevice.requestAccess(for: .video) { (authorized) in
|
||||||
|
invoke.resolve(["camera": authorized ? "granted" : "denied"])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
invoke.resolve(["camera": state])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func openAppSettings(_ invoke: Invoke) {
|
||||||
|
guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
if UIApplication.shared.canOpenURL(settingsUrl) {
|
||||||
|
UIApplication.shared.open(
|
||||||
|
settingsUrl,
|
||||||
|
completionHandler: { (success) in
|
||||||
|
invoke.resolve()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func runScanner(_ invoke: Invoke) {
|
||||||
|
scanFormats = [AVMetadataObject.ObjectType]()
|
||||||
|
|
||||||
|
if (invoke.data["formats"]) != nil {
|
||||||
|
let _scanFormats = invoke.getArray("formats", String.self)
|
||||||
|
|
||||||
|
if _scanFormats != nil && _scanFormats?.count ?? 0 > 0 {
|
||||||
|
_scanFormats?.forEach { targetedFormat in
|
||||||
|
if let value = SupportedFormat(rawValue: targetedFormat)?.value {
|
||||||
|
scanFormats.append(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if scanFormats.count == 0 {
|
||||||
|
for supportedFormat in SupportedFormat.allCases {
|
||||||
|
scanFormats.append(supportedFormat.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.metaOutput!.metadataObjectTypes = self.scanFormats
|
||||||
|
self.captureSession!.startRunning()
|
||||||
|
|
||||||
|
self.isScanning = true
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func scan(_ invoke: Invoke) {
|
||||||
|
self.invoke = invoke
|
||||||
|
|
||||||
|
var iOS14min: Bool = false
|
||||||
|
if #available(iOS 14.0, *) { iOS14min = true }
|
||||||
|
if !iOS14min && self.getPermissionState() != "granted" {
|
||||||
|
var authorized = false
|
||||||
|
AVCaptureDevice.requestAccess(for: .video) { (isAuthorized) in
|
||||||
|
authorized = isAuthorized
|
||||||
|
}
|
||||||
|
if !authorized {
|
||||||
|
invoke.reject("denied by the user")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async { [self] in
|
||||||
|
self.loadCamera()
|
||||||
|
self.dismantleCamera()
|
||||||
|
self.setupCamera(
|
||||||
|
direction: invoke.getString("cameraDirection") ?? "back",
|
||||||
|
windowed: invoke.getBool("windowed") ?? false
|
||||||
|
)
|
||||||
|
self.runScanner(invoke)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func cancel(_ invoke: Invoke) {
|
||||||
|
self.invoke?.reject("cancelled")
|
||||||
|
|
||||||
|
destroy()
|
||||||
|
invoke.resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@_cdecl("init_plugin_barcode_scanner")
|
||||||
|
func initPlugin() -> Plugin {
|
||||||
|
return BarcodeScannerPlugin()
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import AVFoundation
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class CameraView: UIView {
|
||||||
|
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
|
||||||
|
|
||||||
|
func interfaceOrientationToVideoOrientation(_ orientation: UIInterfaceOrientation)
|
||||||
|
-> AVCaptureVideoOrientation
|
||||||
|
{
|
||||||
|
switch orientation {
|
||||||
|
case UIInterfaceOrientation.portrait:
|
||||||
|
return AVCaptureVideoOrientation.portrait
|
||||||
|
case UIInterfaceOrientation.portraitUpsideDown:
|
||||||
|
return AVCaptureVideoOrientation.portraitUpsideDown
|
||||||
|
case UIInterfaceOrientation.landscapeLeft:
|
||||||
|
return AVCaptureVideoOrientation.landscapeLeft
|
||||||
|
case UIInterfaceOrientation.landscapeRight:
|
||||||
|
return AVCaptureVideoOrientation.landscapeRight
|
||||||
|
default:
|
||||||
|
return AVCaptureVideoOrientation.portraitUpsideDown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
if let sublayers = self.layer.sublayers {
|
||||||
|
for layer in sublayers {
|
||||||
|
layer.frame = self.bounds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let interfaceOrientation = UIApplication.shared.windows.first(where: { $0.isKeyWindow })?
|
||||||
|
.windowScene?.interfaceOrientation
|
||||||
|
{
|
||||||
|
self.videoPreviewLayer?.connection?.videoOrientation = interfaceOrientationToVideoOrientation(
|
||||||
|
interfaceOrientation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addPreviewLayer(_ previewLayer: AVCaptureVideoPreviewLayer?) {
|
||||||
|
previewLayer!.videoGravity = AVLayerVideoGravity.resizeAspectFill
|
||||||
|
previewLayer!.frame = self.bounds
|
||||||
|
self.layer.addSublayer(previewLayer!)
|
||||||
|
self.videoPreviewLayer = previewLayer
|
||||||
|
}
|
||||||
|
|
||||||
|
func removePreviewLayer() {
|
||||||
|
if self.videoPreviewLayer != nil {
|
||||||
|
self.videoPreviewLayer!.removeFromSuperlayer()
|
||||||
|
self.videoPreviewLayer = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import AVFoundation
|
||||||
|
|
||||||
|
func createCaptureDeviceInput(
|
||||||
|
cameraDirection: String, backCamera: AVCaptureDevice?, frontCamera: AVCaptureDevice?
|
||||||
|
) throws
|
||||||
|
-> AVCaptureDeviceInput
|
||||||
|
{
|
||||||
|
var captureDevice: AVCaptureDevice
|
||||||
|
if cameraDirection == "back" {
|
||||||
|
if backCamera != nil {
|
||||||
|
captureDevice = backCamera!
|
||||||
|
} else {
|
||||||
|
throw CaptureError.backCameraUnavailable
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if frontCamera != nil {
|
||||||
|
captureDevice = frontCamera!
|
||||||
|
} else {
|
||||||
|
throw CaptureError.frontCameraUnavailable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let captureDeviceInput: AVCaptureDeviceInput
|
||||||
|
do {
|
||||||
|
captureDeviceInput = try AVCaptureDeviceInput(device: captureDevice)
|
||||||
|
} catch let error as NSError {
|
||||||
|
throw CaptureError.couldNotCaptureInput(error: error)
|
||||||
|
}
|
||||||
|
return captureDeviceInput
|
||||||
|
}
|
||||||
|
|
||||||
|
func discoverCaptureDevices() -> [AVCaptureDevice] {
|
||||||
|
if #available(iOS 13.0, *) {
|
||||||
|
return AVCaptureDevice.DiscoverySession(
|
||||||
|
deviceTypes: [
|
||||||
|
.builtInTripleCamera, .builtInDualCamera, .builtInTelephotoCamera,
|
||||||
|
.builtInTrueDepthCamera,
|
||||||
|
.builtInUltraWideCamera, .builtInDualWideCamera, .builtInWideAngleCamera,
|
||||||
|
], mediaType: .video, position: .unspecified
|
||||||
|
).devices
|
||||||
|
} else {
|
||||||
|
return AVCaptureDevice.DiscoverySession(
|
||||||
|
deviceTypes: [
|
||||||
|
.builtInDualCamera, .builtInWideAngleCamera, .builtInTelephotoCamera,
|
||||||
|
.builtInTrueDepthCamera,
|
||||||
|
], mediaType: .video, position: .unspecified
|
||||||
|
).devices
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatStringFromMetadata(_ type: AVMetadataObject.ObjectType) -> String {
|
||||||
|
switch type {
|
||||||
|
case AVMetadataObject.ObjectType.upce:
|
||||||
|
return "UPC_E"
|
||||||
|
case AVMetadataObject.ObjectType.ean8:
|
||||||
|
return "EAN_8"
|
||||||
|
case AVMetadataObject.ObjectType.ean13:
|
||||||
|
return "EAN_13"
|
||||||
|
case AVMetadataObject.ObjectType.code39:
|
||||||
|
return "CODE_39"
|
||||||
|
case AVMetadataObject.ObjectType.code93:
|
||||||
|
return "CODE_93"
|
||||||
|
case AVMetadataObject.ObjectType.code128:
|
||||||
|
return "CODE_128"
|
||||||
|
case AVMetadataObject.ObjectType.interleaved2of5:
|
||||||
|
return "ITF"
|
||||||
|
case AVMetadataObject.ObjectType.aztec:
|
||||||
|
return "AZTEC"
|
||||||
|
case AVMetadataObject.ObjectType.dataMatrix:
|
||||||
|
return "DATA_MATRIX"
|
||||||
|
case AVMetadataObject.ObjectType.pdf417:
|
||||||
|
return "PDF_417"
|
||||||
|
case AVMetadataObject.ObjectType.qr:
|
||||||
|
return "QR_CODE"
|
||||||
|
default:
|
||||||
|
return type.rawValue
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import ExamplePlugin
|
||||||
|
|
||||||
|
final class ExamplePluginTests: XCTestCase {
|
||||||
|
func testExample() throws {
|
||||||
|
let plugin = ExamplePlugin()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "@tauri-apps/plugin-barcode-scanner",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS",
|
||||||
|
"license": "MIT or APACHE-2.0",
|
||||||
|
"authors": [
|
||||||
|
"Tauri Programme within The Commons Conservancy"
|
||||||
|
],
|
||||||
|
"type": "module",
|
||||||
|
"browser": "dist-js/index.min.js",
|
||||||
|
"module": "dist-js/index.mjs",
|
||||||
|
"types": "dist-js/index.d.ts",
|
||||||
|
"exports": {
|
||||||
|
"import": "./dist-js/index.mjs",
|
||||||
|
"types": "./dist-js/index.d.ts",
|
||||||
|
"browser": "./dist-js/index.min.js"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "rollup -c"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist-js",
|
||||||
|
"!dist-js/**/*.map",
|
||||||
|
"README.md",
|
||||||
|
"LICENSE"
|
||||||
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"tslib": "^2.5.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@tauri-apps/api": "2.0.0-alpha.8"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
import { readFileSync } from "fs";
|
||||||
|
|
||||||
|
import { createConfig } from "../../shared/rollup.config.mjs";
|
||||||
|
|
||||||
|
export default createConfig({
|
||||||
|
input: "guest-js/index.ts",
|
||||||
|
pkg: JSON.parse(
|
||||||
|
readFileSync(new URL("./package.json", import.meta.url), "utf8"),
|
||||||
|
),
|
||||||
|
external: [/^@tauri-apps\/api/],
|
||||||
|
});
|
@ -0,0 +1 @@
|
|||||||
|
if("__TAURI__"in window){var __TAURI_BARCODESCANNER__=function(e){"use strict";var n=Object.defineProperty,t=(e,n,t)=>{if(!n.has(e))throw TypeError("Cannot "+t)},r=(e,n,r)=>(t(e,n,"read from private field"),r?r.call(e):n.get(e)),a=(e,n,r,a)=>(t(e,n,"write to private field"),a?a.call(e,r):n.set(e,r),r);function i(e,n=!1){let t=window.crypto.getRandomValues(new Uint32Array(1))[0],r=`_${t}`;return Object.defineProperty(window,r,{value:t=>(n&&Reflect.deleteProperty(window,r),e?.(t)),writable:!1,configurable:!0}),t}((e,t)=>{for(var r in t)n(e,r,{get:t[r],enumerable:!0})})({},{Channel:()=>c,PluginListener:()=>_,addPluginListener:()=>l,convertFileSrc:()=>u,invoke:()=>d,transformCallback:()=>i});var o,c=class{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,((e,n,t)=>{if(n.has(e))throw TypeError("Cannot add the same private member more than once");n instanceof WeakSet?n.add(e):n.set(e,t)})(this,o,(()=>{})),this.id=i((e=>{r(this,o).call(this,e)}))}set onmessage(e){a(this,o,e)}get onmessage(){return r(this,o)}toJSON(){return`__CHANNEL__:${this.id}`}};o=new WeakMap;var s,_=class{constructor(e,n,t){this.plugin=e,this.event=n,this.channelId=t}async unregister(){return d(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}};async function l(e,n,t){let r=new c;return r.onmessage=t,d(`plugin:${e}|register_listener`,{event:n,handler:r}).then((()=>new _(e,n,r.id)))}async function d(e,n={},t){return new Promise(((r,a)=>{let o=i((e=>{r(e),Reflect.deleteProperty(window,`_${c}`)}),!0),c=i((e=>{a(e),Reflect.deleteProperty(window,`_${o}`)}),!0);window.__TAURI_IPC__({cmd:e,callback:o,error:c,payload:n,options:t})}))}function u(e,n="asset"){return window.__TAURI__.convertFileSrc(e,n)}return e.Format=void 0,(s=e.Format||(e.Format={})).QRCode="QR_CODE",s.UPC_A="UPC_A",s.UPC_E="UPC_E",s.EAN8="EAN_8",s.EAN13="EAN_13",s.Code39="CODE_39",s.Code93="CODE_93",s.Code128="CODE_128",s.Codabar="CODABAR",s.ITF="ITF",s.Aztec="AZTEC",s.DataMatrix="DATA_MATRIX",s.PDF417="PDF_417",e.cancel=async function(){return await d("plugin:barcodeScanner|cancel")},e.checkPermissions=async function(){return await d("plugin:barcodeScanner|checkPermissions").then((e=>e.camera))},e.openAppSettings=async function(){return await d("plugin:barcodeScanner|openAppSettings")},e.requestPermissions=async function(){return await d("plugin:barcodeScanner|requestPermissions").then((e=>e.camera))},e.scan=async function(e){return await d("plugin:barcodeScanner|scan",{...e})},e}({});Object.defineProperty(window.__TAURI__,"barcodeScanner",{value:__TAURI_BARCODESCANNER__})}
|
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
use serde::{ser::Serializer, Serialize};
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error(transparent)]
|
||||||
|
Io(#[from] std::io::Error),
|
||||||
|
#[cfg(mobile)]
|
||||||
|
#[error(transparent)]
|
||||||
|
PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Error {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(self.to_string().as_ref())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#![cfg(mobile)]
|
||||||
|
|
||||||
|
use tauri::{
|
||||||
|
plugin::{Builder, PluginHandle, TauriPlugin},
|
||||||
|
Manager, Runtime,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use models::*;
|
||||||
|
|
||||||
|
mod error;
|
||||||
|
mod models;
|
||||||
|
|
||||||
|
pub use error::{Error, Result};
|
||||||
|
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
const PLUGIN_IDENTIFIER: &str = "app.tauri.barcodescanner";
|
||||||
|
|
||||||
|
#[cfg(target_os = "ios")]
|
||||||
|
tauri::ios_plugin_binding!(init_plugin_barcode_scanner);
|
||||||
|
|
||||||
|
/// Access to the scanner APIs.
|
||||||
|
pub struct BarcodeScanner<R: Runtime>(PluginHandle<R>);
|
||||||
|
|
||||||
|
impl<R: Runtime> BarcodeScanner<R> {}
|
||||||
|
|
||||||
|
/// Extensions to [`tauri::App`], [`tauri::AppHandle`] and [`tauri::Window`] to access the barcode scanner APIs.
|
||||||
|
pub trait BarcodeScannerExt<R: Runtime> {
|
||||||
|
fn barcode_scanner(&self) -> &BarcodeScanner<R>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Runtime, T: Manager<R>> crate::BarcodeScannerExt<R> for T {
|
||||||
|
fn barcode_scanner(&self) -> &BarcodeScanner<R> {
|
||||||
|
self.state::<BarcodeScanner<R>>().inner()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initializes the plugin.
|
||||||
|
pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||||
|
Builder::new("barcodeScanner")
|
||||||
|
.setup(|app, api| {
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
let handle = api.register_android_plugin(PLUGIN_IDENTIFIER, "BarcodeScannerPlugin")?;
|
||||||
|
#[cfg(target_os = "ios")]
|
||||||
|
let handle = api.register_ios_plugin(init_plugin_barcode_scanner)?;
|
||||||
|
app.manage(BarcodeScanner(handle));
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-License-Identifier: MIT
|
@ -0,0 +1,3 @@
|
|||||||
|
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-License-Identifier: MIT
|
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"include": ["guest-js/*.ts"]
|
||||||
|
}
|