Compare commits
179 Commits
feat/hooks
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e04f6156d | ||
|
|
c307142c9d | ||
|
|
3467454fcf | ||
|
|
42388be530 | ||
|
|
4b65edfe84 | ||
|
|
d973dbaab6 | ||
|
|
b01531189a | ||
|
|
819ba0d526 | ||
|
|
43f2a0f774 | ||
|
|
90ec544cd2 | ||
|
|
5c1187ed42 | ||
|
|
1e7152e179 | ||
|
|
d5e74c05ed | ||
|
|
bde54e0559 | ||
|
|
1d876ef5eb | ||
|
|
a6a7a9be1b | ||
|
|
27dbe43ad3 | ||
|
|
641ae7525c | ||
|
|
b990a2465c | ||
|
|
f4f6352392 | ||
|
|
7109ac7ab1 | ||
|
|
47637774a1 | ||
|
|
628c4162e1 | ||
|
|
d7092048ff | ||
|
|
5a3af65773 | ||
|
|
2782a24d25 | ||
|
|
bec91a4b5a | ||
|
|
20c62fb972 | ||
|
|
5a27a361fd | ||
|
|
0df72c3114 | ||
|
|
c3159aa374 | ||
|
|
360a5b0ce2 | ||
|
|
951fcbd397 | ||
|
|
0d90e84848 | ||
|
|
e78242b038 | ||
|
|
2d7985a46f | ||
|
|
f3d6d7db19 | ||
|
|
93e6581cde | ||
|
|
d62da5f092 | ||
|
|
a1ba2252b2 | ||
|
|
7c796bf0fd | ||
|
|
bd4a7014a9 | ||
|
|
e1462ea169 | ||
|
|
9675813b7f | ||
|
|
7706ccbea7 | ||
|
|
b738f41a7e | ||
|
|
0c78f0341d | ||
|
|
6f433267d6 | ||
|
|
e11918ccf7 | ||
|
|
bb774ead9b | ||
|
|
945e97a042 | ||
|
|
042880a584 | ||
|
|
3ba989edba | ||
|
|
263b34e520 | ||
|
|
dcbbbf453a | ||
|
|
134173f938 | ||
|
|
c434051f0f | ||
|
|
ac76480d7d | ||
|
|
26393332dd | ||
|
|
485d098091 | ||
|
|
f5e68266dd | ||
|
|
cec79269ee | ||
|
|
8c9d1e8f6d | ||
|
|
a416a75aa8 | ||
|
|
633322e1e2 | ||
|
|
20712367d8 | ||
|
|
250d98c306 | ||
|
|
fa61fce613 | ||
|
|
ee88b38e22 | ||
|
|
9463bbc77a | ||
|
|
ffed4359ab | ||
|
|
9478d158d0 | ||
|
|
de1d7762eb | ||
|
|
b130991527 | ||
|
|
0f4107e729 | ||
|
|
815cb08d0b | ||
|
|
95201574c0 | ||
|
|
e55893bf4b | ||
|
|
04e6f0674c | ||
|
|
0641fc1755 | ||
|
|
dc4f65702f | ||
|
|
b6f9bb3b97 | ||
|
|
970248c1a6 | ||
|
|
d49643813b | ||
|
|
802b56d88c | ||
|
|
2f57436c08 | ||
|
|
a5b55fa608 | ||
|
|
611fd9e5d1 | ||
|
|
ce791350a4 | ||
|
|
e02890c41a | ||
|
|
5b32936f00 | ||
|
|
ec84aa9d6b | ||
|
|
77c0d0f751 | ||
|
|
48a3a4dfd8 | ||
|
|
868d0fb828 | ||
|
|
7b3c542b14 | ||
|
|
3e94912fec | ||
|
|
cc9a271781 | ||
|
|
45308c1bae | ||
|
|
56cfa4739a | ||
|
|
cb79f53adc | ||
|
|
296045e299 | ||
|
|
baf6f9fd52 | ||
|
|
85b951ced1 | ||
|
|
bfe04aaec8 | ||
|
|
9c18c25f19 | ||
|
|
3ee43d4acd | ||
|
|
d59f5a099d | ||
|
|
68953bcb14 | ||
|
|
090f7390db | ||
|
|
910c69a30a | ||
|
|
e7ed948841 | ||
|
|
0111237eeb | ||
|
|
d1e3d66a69 | ||
|
|
9e9e12b05a | ||
|
|
86e8053160 | ||
|
|
3a9103694d | ||
|
|
f4965cfc3d | ||
|
|
e9af04afbc | ||
|
|
353eecec59 | ||
|
|
1f29ffb023 | ||
|
|
8c2100dd63 | ||
|
|
4c54bd5f2f | ||
|
|
f11136a119 | ||
|
|
8e4d36aa14 | ||
|
|
d477f4c53c | ||
|
|
0afc608eb0 | ||
|
|
18d9347dfa | ||
|
|
0911852b53 | ||
|
|
1f895481c9 | ||
|
|
4c757447cb | ||
|
|
886a3bb758 | ||
|
|
9f2796decf | ||
|
|
fb2105c7cc | ||
|
|
43a7323f83 | ||
|
|
407295bc47 | ||
|
|
944c304b88 | ||
|
|
745e8de8c3 | ||
|
|
38c5197023 | ||
|
|
4a1cfa5c2a | ||
|
|
e76faaec41 | ||
|
|
7c7124df1c | ||
|
|
4522841f8a | ||
|
|
51a492782e | ||
|
|
72f2dc9f6d | ||
|
|
583507f223 | ||
|
|
3f801ee11c | ||
|
|
ea0e9e68e7 | ||
|
|
d60d201eb2 | ||
|
|
398ac120fc | ||
|
|
3f66aa5aeb | ||
|
|
802ce479ff | ||
|
|
6cc8fefc6f | ||
|
|
adae4a294d | ||
|
|
c7e92b29c8 | ||
|
|
ba0e427304 | ||
|
|
c683076a48 | ||
|
|
7c2572dc7a | ||
|
|
9b4af8e649 | ||
|
|
7806f840cf | ||
|
|
8761fe0a2b | ||
|
|
ea05b1449d | ||
|
|
cd4c8f24e3 | ||
|
|
dea33716aa | ||
|
|
b57538827c | ||
|
|
db7b9e4a98 | ||
|
|
d84127f310 | ||
|
|
acfa7bd18b | ||
|
|
5de4bc54df | ||
|
|
1fa0441144 | ||
|
|
2788b9d0d9 | ||
|
|
fc387c271a | ||
|
|
e2748428b7 | ||
|
|
84cf1df7aa | ||
|
|
81fcbcc885 | ||
|
|
ae687d047c | ||
|
|
2cde2e76a6 | ||
|
|
2bb2540766 | ||
|
|
e7aa6a3a1a |
@ -1,43 +1,39 @@
|
||||
version: 2
|
||||
executorType: docker
|
||||
jobs:
|
||||
build-app:
|
||||
resource_class: medium
|
||||
environment:
|
||||
- GRADLE_OPTS: '-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs="-Xmx3072m -XX:+HeapDumpOnOutOfMemoryError"'
|
||||
- REACT_NATIVE_MAX_WORKERS: 2
|
||||
- ANDROID_BUILD_TOOLS_VERSION: '28.0.3'
|
||||
working_directory: ~/app
|
||||
docker:
|
||||
- image: reactnativecommunity/react-native-android
|
||||
version: 2.1
|
||||
|
||||
orbs:
|
||||
react-native: react-native-community/react-native@4.4.0
|
||||
|
||||
commands:
|
||||
checkout-attach-workspace:
|
||||
description: "Checkout and attach workspace"
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-npm-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||
- v1-npm
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: yarn install --ignore-engines
|
||||
- save_cache:
|
||||
key: v1-npm
|
||||
paths:
|
||||
- node_modules/
|
||||
- save_cache:
|
||||
key: v1-npm-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||
paths:
|
||||
- node_modules/
|
||||
- attach_workspace:
|
||||
at: .
|
||||
|
||||
jobs:
|
||||
install:
|
||||
executor: react-native/linux_js
|
||||
steps:
|
||||
- checkout-attach-workspace
|
||||
- react-native/yarn_install
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- node_modules
|
||||
|
||||
lint:
|
||||
executor: react-native/linux_android
|
||||
steps:
|
||||
- checkout-attach-workspace
|
||||
- run:
|
||||
name: Lint
|
||||
command: yarn lint
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-gradle-{{ checksum "android/gradle/wrapper/gradle-wrapper.properties" }}-{{ checksum "examples/basic/android/gradle/wrapper/gradle-wrapper.properties" }}
|
||||
- v1-gradle-wrapper
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-gradle-cache-{{ checksum "android/build.gradle" }}-{{ checksum "examples/basic/android/build.gradle" }}
|
||||
- v1-gradle-cache
|
||||
|
||||
build-app:
|
||||
executor: react-native/linux_android
|
||||
steps:
|
||||
- checkout-attach-workspace
|
||||
- run:
|
||||
name: Run Checks
|
||||
command: |
|
||||
@ -48,32 +44,13 @@ jobs:
|
||||
- run:
|
||||
name: Run Yarn to Generate react.gradle
|
||||
command: cd examples/basic/android && yarn
|
||||
- run:
|
||||
name: Build Sample App
|
||||
command: |
|
||||
cd examples/basic/android && chmod +x ./gradlew && ./gradlew build
|
||||
- store_artifacts:
|
||||
path: examples/basic/android/app/build/reports
|
||||
destination: app
|
||||
- save_cache:
|
||||
key: v1-gradle-wrapper-{{ checksum "examples/basic/android/gradle/wrapper/gradle-wrapper.properties" }}
|
||||
paths:
|
||||
- ~/.gradle/wrapper
|
||||
- save_cache:
|
||||
key: v1-gradle-cache-{{ checksum "examples/basic/android/build.gradle" }}
|
||||
paths:
|
||||
- ~/.gradle/caches
|
||||
- deploy:
|
||||
command: |
|
||||
if [ "${CIRCLE_BRANCH}" == "master" ]; then
|
||||
yarn ci:publish
|
||||
fi
|
||||
- react-native/android_build:
|
||||
project_path: ./examples/basic/android
|
||||
|
||||
deploy-docs:
|
||||
working_directory: ~/app
|
||||
docker:
|
||||
- image: circleci/node:8.11.1
|
||||
executor: react-native/linux_js
|
||||
steps:
|
||||
- checkout
|
||||
- checkout-attach-workspace
|
||||
- run:
|
||||
name: Deploying to GitHub Pages
|
||||
command: |
|
||||
@ -81,12 +58,46 @@ jobs:
|
||||
git config --global user.name "${GH_NAME}"
|
||||
echo "machine github.com login $GH_NAME password $GH_TOKEN_DOCS" > ~/.netrc
|
||||
cd website && yarn install && GIT_USER=${GH_NAME} yarn run publish-gh-pages
|
||||
|
||||
publish-version:
|
||||
executor: react-native/linux_js
|
||||
steps:
|
||||
- checkout-attach-workspace
|
||||
- run:
|
||||
name: Run semantic-release
|
||||
command: yarn ci:publish
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build-and-deploy-docs:
|
||||
|
||||
develop:
|
||||
jobs:
|
||||
- build-app
|
||||
- deploy-docs:
|
||||
- install:
|
||||
filters:
|
||||
branches:
|
||||
ignore: master
|
||||
|
||||
- lint:
|
||||
requires:
|
||||
- install
|
||||
|
||||
- build-app:
|
||||
requires:
|
||||
- install
|
||||
|
||||
release:
|
||||
jobs:
|
||||
- install:
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
|
||||
- deploy-docs:
|
||||
requires:
|
||||
- install
|
||||
|
||||
- publish-version:
|
||||
requires:
|
||||
- install
|
||||
|
||||
|
||||
|
||||
1
.eslintIgnore
Normal file
1
.eslintIgnore
Normal file
@ -0,0 +1 @@
|
||||
tests
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -49,3 +49,6 @@ package-json.lock
|
||||
examples/mlkit/android/app/google-services.json
|
||||
examples/mlkit/ios/Pods
|
||||
examples/mlkit/ios/mlkit/GoogleService-Info.plist
|
||||
|
||||
!debug.keystore
|
||||
/ios/Pods/
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
# React Native Camera [](#backers) [](#sponsors) [](http://badge.fury.io/js/react-native-camera) [](https://www.npmjs.com/package/react-native-camera)
|
||||
|
||||
[Looking for Maintainers](https://github.com/react-native-community/react-native-camera/issues/3000)
|
||||
|
||||
We are looking for maintainers for this package, or to deprecated this in favor of expo-camera, it nobody want to maintain this
|
||||
|
||||
## Docs
|
||||
Follow our docs here [https://react-native-community.github.io/react-native-camera/](https://react-native-community.github.io/react-native-camera/)
|
||||
Follow our docs here [https://react-native-camera.github.io/react-native-camera/](https://react-native-camera.github.io/react-native-camera/)
|
||||
|
||||
## Sponsors
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ buildscript {
|
||||
|
||||
dependencies {
|
||||
//noinspection GradleDependency
|
||||
classpath("com.android.tools.build:gradle:3.5.2")
|
||||
classpath("com.android.tools.build:gradle:3.6.3")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -41,18 +41,6 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java.srcDirs = ['src/main/java']
|
||||
}
|
||||
general {
|
||||
java.srcDirs = ['src/general/java']
|
||||
}
|
||||
mlkit {
|
||||
java.srcDirs = ['src/mlkit/java']
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
warning 'InvalidPackage'
|
||||
@ -78,7 +66,8 @@ repositories {
|
||||
dependencies {
|
||||
def googlePlayServicesVisionVersion = safeExtGet('googlePlayServicesVisionVersion', safeExtGet('googlePlayServicesVersion', '17.0.2'))
|
||||
|
||||
implementation 'com.facebook.react:react-native:+'
|
||||
//noinspection GradleDynamicVersion
|
||||
implementation 'com.facebook.react:react-native:+' // From node_modules
|
||||
implementation "com.google.zxing:core:3.3.3"
|
||||
implementation "com.drewnoakes:metadata-extractor:2.11.0"
|
||||
generalImplementation "com.google.android.gms:play-services-vision:$googlePlayServicesVisionVersion"
|
||||
|
||||
BIN
android/gradle/wrapper/gradle-wrapper.jar
vendored
BIN
android/gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
@ -1,6 +1,5 @@
|
||||
#Wed Jan 23 23:35:17 CST 2019
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
|
||||
|
||||
120
android/gradlew
vendored
120
android/gradlew
vendored
@ -1,4 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
@ -6,42 +22,6 @@
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
@ -60,6 +40,46 @@ cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@ -85,7 +105,7 @@ location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
@ -105,8 +125,8 @@ if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
@ -150,11 +170,19 @@ if $cygwin ; then
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
30
android/gradlew.bat
vendored
30
android/gradlew.bat
vendored
@ -1,3 +1,19 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@ -8,14 +24,14 @@
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
@ -46,10 +62,9 @@ echo location of your Java installation.
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
@ -60,11 +75,6 @@ set _SKIP=2
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
@ -29,19 +29,18 @@ public class BarcodeDetectorAsyncTask extends android.os.AsyncTask<Void, Void, S
|
||||
private int mPaddingTop;
|
||||
|
||||
public BarcodeDetectorAsyncTask(
|
||||
BarcodeDetectorAsyncTaskDelegate delegate,
|
||||
RNBarcodeDetector barcodeDetector,
|
||||
byte[] imageData,
|
||||
int width,
|
||||
int height,
|
||||
int rotation,
|
||||
float density,
|
||||
int facing,
|
||||
int viewWidth,
|
||||
int viewHeight,
|
||||
int viewPaddingLeft,
|
||||
int viewPaddingTop
|
||||
) {
|
||||
BarcodeDetectorAsyncTaskDelegate delegate,
|
||||
RNBarcodeDetector barcodeDetector,
|
||||
byte[] imageData,
|
||||
int width,
|
||||
int height,
|
||||
int rotation,
|
||||
float density,
|
||||
int facing,
|
||||
int viewWidth,
|
||||
int viewHeight,
|
||||
int viewPaddingLeft,
|
||||
int viewPaddingTop) {
|
||||
mImageData = imageData;
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
@ -73,7 +72,7 @@ public class BarcodeDetectorAsyncTask extends android.os.AsyncTask<Void, Void, S
|
||||
mDelegate.onBarcodeDetectionError(mBarcodeDetector);
|
||||
} else {
|
||||
if (barcodes.size() > 0) {
|
||||
mDelegate.onBarcodesDetected(serializeEventData(barcodes));
|
||||
mDelegate.onBarcodesDetected(serializeEventData(barcodes), mWidth, mHeight, mImageData);
|
||||
}
|
||||
mDelegate.onBarcodeDetectingTaskCompleted();
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@ import android.graphics.SurfaceTexture;
|
||||
import android.hardware.Camera;
|
||||
import android.media.CamcorderProfile;
|
||||
import android.media.MediaRecorder;
|
||||
import android.media.MediaActionSound;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import androidx.collection.SparseArrayCompat;
|
||||
@ -82,6 +83,9 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
|
||||
Camera mCamera;
|
||||
|
||||
// do not instantiate this every time since it allocates unnecessary resources
|
||||
MediaActionSound sound = new MediaActionSound();
|
||||
|
||||
private Camera.Parameters mCameraParameters;
|
||||
|
||||
private final Camera.CameraInfo mCameraInfo = new Camera.CameraInfo();
|
||||
@ -123,7 +127,10 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
|
||||
private boolean mIsScanning;
|
||||
|
||||
private Boolean mPlaySoundOnCapture = false;
|
||||
|
||||
private boolean mustUpdateSurface;
|
||||
private boolean surfaceWasDestroyed;
|
||||
|
||||
private SurfaceTexture mPreviewTexture;
|
||||
|
||||
@ -133,12 +140,56 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
preview.setCallback(new PreviewImpl.Callback() {
|
||||
@Override
|
||||
public void onSurfaceChanged() {
|
||||
updateSurface();
|
||||
|
||||
// if we got our surface destroyed
|
||||
// we must re-start the camera and surface
|
||||
// otherwise, just update our surface
|
||||
|
||||
|
||||
synchronized(Camera1.this){
|
||||
if(!surfaceWasDestroyed){
|
||||
updateSurface();
|
||||
}
|
||||
else{
|
||||
mBgHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
start();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceDestroyed() {
|
||||
stop();
|
||||
|
||||
// need to this early so we don't get buffer errors due to sufrace going away.
|
||||
// Then call stop in bg thread since it might be quite slow and will freeze
|
||||
// the UI or cause an ANR while it is happening.
|
||||
synchronized(Camera1.this){
|
||||
if(mCamera != null){
|
||||
|
||||
// let the instance know our surface was destroyed
|
||||
// and we might need to re-create it and restart the camera
|
||||
surfaceWasDestroyed = true;
|
||||
|
||||
try{
|
||||
mCamera.setPreviewCallback(null);
|
||||
// note: this might give a debug message that can be ignored.
|
||||
mCamera.setPreviewDisplay(null);
|
||||
}
|
||||
catch(Exception e){
|
||||
Log.e("CAMERA_1::", "onSurfaceDestroyed preview cleanup failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
mBgHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -228,6 +279,8 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
mMediaRecorder = null;
|
||||
|
||||
if (mIsRecording.get()) {
|
||||
mCallback.onRecordingEnd();
|
||||
|
||||
int deviceOrientation = displayOrientationToOrientationEnum(mDeviceOrientation);
|
||||
mCallback.onVideoRecorded(mVideoPath, mOrientation != Constants.ORIENTATION_AUTO ? mOrientation : deviceOrientation, deviceOrientation);
|
||||
}
|
||||
@ -235,8 +288,13 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
|
||||
if (mCamera != null) {
|
||||
mIsPreviewActive = false;
|
||||
mCamera.stopPreview();
|
||||
mCamera.setPreviewCallback(null);
|
||||
try{
|
||||
mCamera.stopPreview();
|
||||
mCamera.setPreviewCallback(null);
|
||||
}
|
||||
catch(Exception e){
|
||||
Log.e("CAMERA_1::", "stop preview cleanup failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
releaseCamera();
|
||||
@ -247,6 +305,8 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
@SuppressLint("NewApi")
|
||||
void setUpPreview() {
|
||||
try {
|
||||
surfaceWasDestroyed = false;
|
||||
|
||||
if(mCamera != null){
|
||||
if (mPreviewTexture != null) {
|
||||
mCamera.setPreviewTexture(mPreviewTexture);
|
||||
@ -403,31 +463,55 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
return mPictureSizes.sizes(ratio);
|
||||
}
|
||||
|
||||
// Returns the best available size match for a given
|
||||
// width and height
|
||||
// returns the biggest available size
|
||||
private Size getBestSizeMatch(int desiredWidth, int desiredHeight, SortedSet<Size> sizes) {
|
||||
if(sizes == null || sizes.isEmpty()){
|
||||
return null;
|
||||
}
|
||||
|
||||
Size result = sizes.last();
|
||||
|
||||
// iterate from smallest to largest, and stay with the closest-biggest match
|
||||
if(desiredWidth != 0 && desiredHeight != 0){
|
||||
for (Size size : sizes) {
|
||||
if (desiredWidth <= size.getWidth() && desiredHeight <= size.getHeight()) {
|
||||
result = size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
void setPictureSize(Size size) {
|
||||
if (size == null) {
|
||||
if (mAspectRatio == null) {
|
||||
return;
|
||||
}
|
||||
SortedSet<Size> sizes = mPictureSizes.sizes(mAspectRatio);
|
||||
if(sizes != null && !sizes.isEmpty())
|
||||
{
|
||||
mPictureSize = sizes.last();
|
||||
}
|
||||
} else {
|
||||
mPictureSize = size;
|
||||
}
|
||||
synchronized(this){
|
||||
if (mCameraParameters != null && mCamera != null) {
|
||||
mCameraParameters.setPictureSize(mPictureSize.getWidth(), mPictureSize.getHeight());
|
||||
try{
|
||||
mCamera.setParameters(mCameraParameters);
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "setParameters failed", e);
|
||||
}
|
||||
|
||||
}
|
||||
// if no changes, don't do anything
|
||||
if(size == null && mPictureSize == null){
|
||||
return;
|
||||
}
|
||||
else if(size != null && size.equals(mPictureSize)){
|
||||
return;
|
||||
}
|
||||
|
||||
mPictureSize = size;
|
||||
|
||||
// if camera is opened, request parameters update
|
||||
if (isCameraOpened()) {
|
||||
mBgHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized(Camera1.this){
|
||||
if(mCamera != null){
|
||||
adjustCameraParameters();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -446,6 +530,7 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
final Set<Size> sizes = mPreviewSizes.sizes(ratio);
|
||||
if (sizes == null) {
|
||||
// do nothing, ratio remains unchanged. Consistent with Camera2 and initial mount behaviour
|
||||
Log.w("CAMERA_1::", "setAspectRatio received an unsupported value and will be ignored.");
|
||||
} else {
|
||||
mAspectRatio = ratio;
|
||||
mBgHandler.post(new Runnable() {
|
||||
@ -695,16 +780,27 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
// this shouldn't be needed and messes up autoFocusPointOfInterest
|
||||
// camera.cancelAutoFocus();
|
||||
|
||||
if (options.hasKey("pauseAfterCapture") && !options.getBoolean("pauseAfterCapture")) {
|
||||
camera.startPreview();
|
||||
mIsPreviewActive = true;
|
||||
if (mIsScanning) {
|
||||
camera.setPreviewCallback(Camera1.this);
|
||||
if(mPlaySoundOnCapture){
|
||||
sound.play(MediaActionSound.SHUTTER_CLICK);
|
||||
}
|
||||
|
||||
// our camera might have been released
|
||||
// when this callback fires, so make sure we have
|
||||
// exclusive access when restoring its preview
|
||||
synchronized(Camera1.this){
|
||||
if(mCamera != null){
|
||||
if (options.hasKey("pauseAfterCapture") && !options.getBoolean("pauseAfterCapture")) {
|
||||
mCamera.startPreview();
|
||||
mIsPreviewActive = true;
|
||||
if (mIsScanning) {
|
||||
mCamera.setPreviewCallback(Camera1.this);
|
||||
}
|
||||
} else {
|
||||
mCamera.stopPreview();
|
||||
mIsPreviewActive = false;
|
||||
mCamera.setPreviewCallback(null);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
camera.stopPreview();
|
||||
mIsPreviewActive = false;
|
||||
camera.setPreviewCallback(null);
|
||||
}
|
||||
|
||||
isPictureCaptureInProgress.set(false);
|
||||
@ -729,7 +825,7 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean record(String path, int maxDuration, int maxFileSize, boolean recordAudio, CamcorderProfile profile, int orientation) {
|
||||
boolean record(String path, int maxDuration, int maxFileSize, boolean recordAudio, CamcorderProfile profile, int orientation, int fps) {
|
||||
|
||||
// make sure compareAndSet is last because we are setting it
|
||||
if (!isPictureCaptureInProgress.get() && mIsRecording.compareAndSet(false, true)) {
|
||||
@ -737,7 +833,7 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
mOrientation = orientation;
|
||||
}
|
||||
try {
|
||||
setUpMediaRecorder(path, maxDuration, maxFileSize, recordAudio, profile);
|
||||
setUpMediaRecorder(path, maxDuration, maxFileSize, recordAudio, profile, fps);
|
||||
mMediaRecorder.prepare();
|
||||
mMediaRecorder.start();
|
||||
|
||||
@ -753,6 +849,10 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
Log.e("CAMERA_1::", "Record setParameters failed", e);
|
||||
}
|
||||
|
||||
int deviceOrientation = displayOrientationToOrientationEnum(mDeviceOrientation);
|
||||
mCallback.onRecordingStart(path, mOrientation != Constants.ORIENTATION_AUTO ? mOrientation : deviceOrientation, deviceOrientation);
|
||||
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
mIsRecording.set(false);
|
||||
@ -776,6 +876,16 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void pauseRecording() {
|
||||
pauseMediaRecorder();
|
||||
}
|
||||
|
||||
@Override
|
||||
void resumeRecording() {
|
||||
resumeMediaRecorder();
|
||||
}
|
||||
|
||||
@Override
|
||||
int getCameraOrientation() {
|
||||
return mCameraInfo.orientation;
|
||||
@ -868,21 +978,34 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
*/
|
||||
private void chooseCamera() {
|
||||
if(_mCameraId == null){
|
||||
int count = Camera.getNumberOfCameras();
|
||||
if(count == 0){
|
||||
throw new RuntimeException("No camera available.");
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
Camera.getCameraInfo(i, mCameraInfo);
|
||||
if (mCameraInfo.facing == mFacing) {
|
||||
mCameraId = i;
|
||||
try{
|
||||
int count = Camera.getNumberOfCameras();
|
||||
if(count == 0){
|
||||
//throw new RuntimeException("No camera available.");
|
||||
mCameraId = INVALID_CAMERA_ID;
|
||||
Log.w("CAMERA_1::", "getNumberOfCameras returned 0. No camera available.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
Camera.getCameraInfo(i, mCameraInfo);
|
||||
if (mCameraInfo.facing == mFacing) {
|
||||
mCameraId = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// no camera found, set the one we have
|
||||
mCameraId = 0;
|
||||
Camera.getCameraInfo(mCameraId, mCameraInfo);
|
||||
}
|
||||
// getCameraInfo may fail if hardware is unavailable
|
||||
// and crash the whole app. Return INVALID_CAMERA_ID
|
||||
// which will in turn fire a mount error event
|
||||
catch(Exception e){
|
||||
Log.e("CAMERA_1::", "chooseCamera failed.", e);
|
||||
mCameraId = INVALID_CAMERA_ID;
|
||||
}
|
||||
// no camera found, set the one we have
|
||||
mCameraId = 0;
|
||||
Camera.getCameraInfo(mCameraId, mCameraInfo);
|
||||
}
|
||||
else{
|
||||
try{
|
||||
@ -899,23 +1022,43 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
if (mCamera != null) {
|
||||
releaseCamera();
|
||||
}
|
||||
|
||||
// in case we got an invalid camera ID
|
||||
// due to no cameras or invalid ID provided,
|
||||
// return false so we can raise a mount error
|
||||
if(mCameraId == INVALID_CAMERA_ID){
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
mCamera = Camera.open(mCameraId);
|
||||
mCameraParameters = mCamera.getParameters();
|
||||
|
||||
// Supported preview sizes
|
||||
mPreviewSizes.clear();
|
||||
for (Camera.Size size : mCameraParameters.getSupportedPreviewSizes()) {
|
||||
mPreviewSizes.add(new Size(size.width, size.height));
|
||||
}
|
||||
|
||||
// Supported picture sizes;
|
||||
mPictureSizes.clear();
|
||||
for (Camera.Size size : mCameraParameters.getSupportedPictureSizes()) {
|
||||
mPictureSizes.add(new Size(size.width, size.height));
|
||||
}
|
||||
|
||||
// to be consistent with Camera2, and to prevent crashes on some devices
|
||||
// do not allow preview sizes that are not also in the picture sizes set
|
||||
for (AspectRatio aspectRatio : mPreviewSizes.ratios()) {
|
||||
if (mPictureSizes.sizes(aspectRatio) == null) {
|
||||
mPreviewSizes.remove(aspectRatio);
|
||||
}
|
||||
}
|
||||
|
||||
// AspectRatio
|
||||
if (mAspectRatio == null) {
|
||||
mAspectRatio = Constants.DEFAULT_ASPECT_RATIO;
|
||||
}
|
||||
|
||||
adjustCameraParameters();
|
||||
mCamera.setDisplayOrientation(calcDisplayOrientation(mDisplayOrientation));
|
||||
mCallback.onCameraOpened();
|
||||
@ -939,20 +1082,41 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
void adjustCameraParameters() {
|
||||
SortedSet<Size> sizes = mPreviewSizes.sizes(mAspectRatio);
|
||||
if (sizes == null) { // Not supported
|
||||
Log.w("CAMERA_1::", "adjustCameraParameters received an unsupported aspect ratio value and will be ignored.");
|
||||
mAspectRatio = chooseAspectRatio();
|
||||
sizes = mPreviewSizes.sizes(mAspectRatio);
|
||||
}
|
||||
Size size = chooseOptimalSize(sizes);
|
||||
|
||||
// Always re-apply camera parameters
|
||||
mPictureSize = mPictureSizes.sizes(mAspectRatio).last();
|
||||
// make sure both preview and picture size are always
|
||||
// valid for the currently chosen camera and aspect ratio
|
||||
Size size = chooseOptimalSize(sizes);
|
||||
Size pictureSize = null;
|
||||
|
||||
// do not alter mPictureSize
|
||||
// since it may be valid for other camera/aspect ratio updates
|
||||
// just make sure we get the right and most suitable value
|
||||
if(mPictureSize != null){
|
||||
pictureSize = getBestSizeMatch(
|
||||
mPictureSize.getWidth(),
|
||||
mPictureSize.getHeight(),
|
||||
mPictureSizes.sizes(mAspectRatio)
|
||||
);
|
||||
}
|
||||
else{
|
||||
pictureSize = getBestSizeMatch(
|
||||
0,
|
||||
0,
|
||||
mPictureSizes.sizes(mAspectRatio)
|
||||
);
|
||||
}
|
||||
|
||||
boolean needsToStopPreview = mIsPreviewActive;
|
||||
if (needsToStopPreview) {
|
||||
mCamera.stopPreview();
|
||||
mIsPreviewActive = false;
|
||||
}
|
||||
mCameraParameters.setPreviewSize(size.getWidth(), size.getHeight());
|
||||
mCameraParameters.setPictureSize(mPictureSize.getWidth(), mPictureSize.getHeight());
|
||||
mCameraParameters.setPictureSize(pictureSize.getWidth(), pictureSize.getHeight());
|
||||
if (mOrientation != Constants.ORIENTATION_AUTO) {
|
||||
mCameraParameters.setRotation(calcCameraRotation(orientationEnumToRotation(mOrientation)));
|
||||
} else {
|
||||
@ -966,6 +1130,8 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
setZoomInternal(mZoom);
|
||||
setWhiteBalanceInternal(mWhiteBalance);
|
||||
setScanningInternal(mIsScanning);
|
||||
setPlaySoundInternal(mPlaySoundOnCapture);
|
||||
|
||||
try{
|
||||
mCamera.setParameters(mCameraParameters);
|
||||
}
|
||||
@ -1008,7 +1174,6 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
if (mCamera != null) {
|
||||
mCamera.release();
|
||||
mCamera = null;
|
||||
mPictureSize = null;
|
||||
mCallback.onCameraClosed();
|
||||
|
||||
// reset these flags
|
||||
@ -1024,17 +1189,9 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
public void run() {
|
||||
synchronized(Camera1.this){
|
||||
if (mCamera != null) {
|
||||
Camera.Parameters parameters = null;
|
||||
|
||||
// This might crash on some devices if the camera is not
|
||||
// available/locked, with a RuntimeException("getParameters failed (empty parameters)")
|
||||
try{
|
||||
parameters = mCamera.getParameters();
|
||||
}
|
||||
catch(Exception e){
|
||||
Log.e("CAMERA_1::", "setFocusArea.getParameters failed", e);
|
||||
parameters = null;
|
||||
}
|
||||
// do not create a new object, use existing.
|
||||
Camera.Parameters parameters = mCameraParameters;
|
||||
|
||||
if (parameters == null) return;
|
||||
|
||||
@ -1129,14 +1286,9 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
if (mCamera != null) {
|
||||
mCamera.cancelAutoFocus();
|
||||
|
||||
Camera.Parameters parameters = null;
|
||||
try{
|
||||
parameters = mCamera.getParameters();
|
||||
}
|
||||
catch(Exception e){
|
||||
Log.e("CAMERA_1::", "resetFocus.getParameters failed", e);
|
||||
parameters = null;
|
||||
}
|
||||
// do not create a new object, use existing.
|
||||
Camera.Parameters parameters = mCameraParameters;
|
||||
|
||||
if (parameters == null) return;
|
||||
|
||||
if (parameters.getFocusMode() != Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) {
|
||||
@ -1234,6 +1386,8 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
final List<String> modes = mCameraParameters.getSupportedFocusModes();
|
||||
if (autoFocus && modes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
|
||||
mCameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
|
||||
} else if (mIsScanning && modes.contains(Camera.Parameters.FOCUS_MODE_MACRO)) {
|
||||
mCameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_MACRO);
|
||||
} else if (modes.contains(Camera.Parameters.FOCUS_MODE_FIXED)) {
|
||||
mCameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_FIXED);
|
||||
} else if (modes.contains(Camera.Parameters.FOCUS_MODE_INFINITY)) {
|
||||
@ -1344,13 +1498,49 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
}
|
||||
}
|
||||
|
||||
private void setPlaySoundInternal(boolean playSoundOnCapture){
|
||||
mPlaySoundOnCapture = playSoundOnCapture;
|
||||
if(mCamera != null){
|
||||
try{
|
||||
// Always disable shutter sound, and play our own.
|
||||
// This is because not all devices honor this value when set to true
|
||||
boolean res = mCamera.enableShutterSound(false);
|
||||
|
||||
// if we fail to disable the shutter sound
|
||||
// set mPlaySoundOnCapture to false since it means
|
||||
// we cannot change it and the system will play it
|
||||
// playing the sound ourselves also makes it consistent with Camera2
|
||||
if(!res){
|
||||
mPlaySoundOnCapture = false;
|
||||
}
|
||||
}
|
||||
catch(Exception ex){
|
||||
Log.e("CAMERA_1::", "setPlaySoundInternal failed", ex);
|
||||
mPlaySoundOnCapture = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void setPlaySoundOnCapture(boolean playSoundOnCapture) {
|
||||
if (playSoundOnCapture == mPlaySoundOnCapture) {
|
||||
return;
|
||||
}
|
||||
setPlaySoundInternal(playSoundOnCapture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getPlaySoundOnCapture(){
|
||||
return mPlaySoundOnCapture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreviewFrame(byte[] data, Camera camera) {
|
||||
Camera.Size previewSize = mCameraParameters.getPreviewSize();
|
||||
mCallback.onFramePreview(data, previewSize.width, previewSize.height, mDeviceOrientation);
|
||||
}
|
||||
|
||||
private void setUpMediaRecorder(String path, int maxDuration, int maxFileSize, boolean recordAudio, CamcorderProfile profile) {
|
||||
private void setUpMediaRecorder(String path, int maxDuration, int maxFileSize, boolean recordAudio, CamcorderProfile profile, int fps) {
|
||||
|
||||
mMediaRecorder = new MediaRecorder();
|
||||
mCamera.unlock();
|
||||
@ -1372,7 +1562,7 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
camProfile = CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_HIGH);
|
||||
}
|
||||
camProfile.videoBitRate = profile.videoBitRate;
|
||||
setCamcorderProfile(camProfile, recordAudio);
|
||||
setCamcorderProfile(camProfile, recordAudio, fps);
|
||||
|
||||
mMediaRecorder.setOrientationHint(calcCameraRotation(mOrientation != Constants.ORIENTATION_AUTO ? orientationEnumToRotation(mOrientation) : mDeviceOrientation));
|
||||
|
||||
@ -1390,31 +1580,73 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
|
||||
private void stopMediaRecorder() {
|
||||
|
||||
if (mMediaRecorder != null) {
|
||||
try {
|
||||
mMediaRecorder.stop();
|
||||
} catch (RuntimeException ex) {
|
||||
Log.e("CAMERA_1::", "stopMediaRecorder failed", ex);
|
||||
synchronized(this){
|
||||
if (mMediaRecorder != null) {
|
||||
try {
|
||||
mMediaRecorder.stop();
|
||||
} catch (RuntimeException ex) {
|
||||
Log.e("CAMERA_1::", "stopMediaRecorder stop failed", ex);
|
||||
}
|
||||
|
||||
try{
|
||||
mMediaRecorder.reset();
|
||||
mMediaRecorder.release();
|
||||
} catch (RuntimeException ex) {
|
||||
Log.e("CAMERA_1::", "stopMediaRecorder reset failed", ex);
|
||||
}
|
||||
|
||||
mMediaRecorder = null;
|
||||
}
|
||||
mMediaRecorder.reset();
|
||||
mMediaRecorder.release();
|
||||
mMediaRecorder = null;
|
||||
|
||||
mCallback.onRecordingEnd();
|
||||
|
||||
int deviceOrientation = displayOrientationToOrientationEnum(mDeviceOrientation);
|
||||
|
||||
if (mVideoPath == null || !new File(mVideoPath).exists()) {
|
||||
mCallback.onVideoRecorded(null, mOrientation != Constants.ORIENTATION_AUTO ? mOrientation : deviceOrientation, deviceOrientation);
|
||||
return;
|
||||
}
|
||||
|
||||
mCallback.onVideoRecorded(mVideoPath, mOrientation != Constants.ORIENTATION_AUTO ? mOrientation : deviceOrientation, deviceOrientation);
|
||||
mVideoPath = null;
|
||||
}
|
||||
|
||||
int deviceOrientation = displayOrientationToOrientationEnum(mDeviceOrientation);
|
||||
if (mVideoPath == null || !new File(mVideoPath).exists()) {
|
||||
mCallback.onVideoRecorded(null, mOrientation != Constants.ORIENTATION_AUTO ? mOrientation : deviceOrientation, deviceOrientation);
|
||||
return;
|
||||
}
|
||||
|
||||
mCallback.onVideoRecorded(mVideoPath, mOrientation != Constants.ORIENTATION_AUTO ? mOrientation : deviceOrientation, deviceOrientation);
|
||||
mVideoPath = null;
|
||||
|
||||
}
|
||||
|
||||
private void setCamcorderProfile(CamcorderProfile profile, boolean recordAudio) {
|
||||
private void pauseMediaRecorder() {
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
mMediaRecorder.pause();
|
||||
}
|
||||
}
|
||||
|
||||
private void resumeMediaRecorder() {
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
mMediaRecorder.resume();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<int[]> getSupportedPreviewFpsRange() {
|
||||
return (ArrayList<int[]>) mCameraParameters.getSupportedPreviewFpsRange();
|
||||
}
|
||||
|
||||
private boolean isCompatibleWithDevice(int fps) {
|
||||
ArrayList<int[]> validValues;
|
||||
validValues = getSupportedPreviewFpsRange();
|
||||
int accurate_fps = fps * 1000;
|
||||
for(int[] row : validValues) {
|
||||
boolean is_included = accurate_fps >= row[0] && accurate_fps <= row[1];
|
||||
boolean greater_then_zero = accurate_fps > 0;
|
||||
boolean compatible_with_device = is_included && greater_then_zero;
|
||||
if (compatible_with_device) return true;
|
||||
}
|
||||
Log.w("CAMERA_1::", "fps (framePerSecond) received an unsupported value and will be ignored.");
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setCamcorderProfile(CamcorderProfile profile, boolean recordAudio, int fps) {
|
||||
int compatible_fps = isCompatibleWithDevice(fps) ? fps : profile.videoFrameRate;
|
||||
mMediaRecorder.setOutputFormat(profile.fileFormat);
|
||||
mMediaRecorder.setVideoFrameRate(profile.videoFrameRate);
|
||||
mMediaRecorder.setVideoFrameRate(compatible_fps);
|
||||
mMediaRecorder.setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight);
|
||||
mMediaRecorder.setVideoEncodingBitRate(profile.videoBitRate);
|
||||
mMediaRecorder.setVideoEncoder(profile.videoCodec);
|
||||
|
||||
@ -37,12 +37,14 @@ import android.media.CamcorderProfile;
|
||||
import android.media.Image;
|
||||
import android.media.ImageReader;
|
||||
import android.media.MediaRecorder;
|
||||
import android.media.MediaActionSound;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.Surface;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Build;
|
||||
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
|
||||
@ -212,6 +214,8 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
|
||||
CameraDevice mCamera;
|
||||
|
||||
MediaActionSound sound = new MediaActionSound();
|
||||
|
||||
CameraCaptureSession mCaptureSession;
|
||||
|
||||
CaptureRequest.Builder mPreviewRequestBuilder;
|
||||
@ -262,6 +266,8 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
|
||||
private boolean mIsScanning;
|
||||
|
||||
private Boolean mPlaySoundOnCapture = false;
|
||||
|
||||
private Surface mPreviewSurface;
|
||||
|
||||
private Rect mInitialCropRegion;
|
||||
@ -300,6 +306,7 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
boolean start() {
|
||||
if (!chooseCameraIdByFacing()) {
|
||||
mAspectRatio = mInitialRatio;
|
||||
mCallback.onMountError();
|
||||
return false;
|
||||
}
|
||||
collectCameraInfo();
|
||||
@ -338,6 +345,8 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
mMediaRecorder = null;
|
||||
|
||||
if (mIsRecording) {
|
||||
mCallback.onRecordingEnd();
|
||||
|
||||
// @TODO: implement videoOrientation and deviceOrientation calculation
|
||||
mCallback.onVideoRecorded(mVideoPath, 0, 0);
|
||||
mIsRecording = false;
|
||||
@ -367,6 +376,13 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
return mFacing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<int[]> getSupportedPreviewFpsRange() {
|
||||
Log.e("CAMERA_2:: ", "getSupportedPreviewFpsRange is not currently supported for Camera2");
|
||||
ArrayList<int[]> validValues = new ArrayList<int[]>();
|
||||
return validValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
void setCameraId(String id) {
|
||||
if(!ObjectUtils.equals(_mCameraId, id)){
|
||||
@ -554,7 +570,7 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean record(String path, int maxDuration, int maxFileSize, boolean recordAudio, CamcorderProfile profile, int orientation) {
|
||||
boolean record(String path, int maxDuration, int maxFileSize, boolean recordAudio, CamcorderProfile profile, int orientation, int fps) {
|
||||
if (!mIsRecording) {
|
||||
setUpMediaRecorder(path, maxDuration, maxFileSize, recordAudio, profile);
|
||||
try {
|
||||
@ -577,6 +593,11 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
mSessionCallback, null);
|
||||
mMediaRecorder.start();
|
||||
mIsRecording = true;
|
||||
|
||||
// @TODO: implement videoOrientation and deviceOrientation calculation
|
||||
// same TODO as onVideoRecorded
|
||||
mCallback.onRecordingStart(mVideoPath, 0, 0);
|
||||
|
||||
return true;
|
||||
} catch (CameraAccessException | IOException e) {
|
||||
e.printStackTrace();
|
||||
@ -599,6 +620,16 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void pauseRecording() {
|
||||
pauseMediaRecorder();
|
||||
}
|
||||
|
||||
@Override
|
||||
void resumeRecording() {
|
||||
resumeMediaRecorder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFocusDepth(float value) {
|
||||
if (mFocusDepth == value) {
|
||||
@ -668,6 +699,16 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
return mWhiteBalance;
|
||||
}
|
||||
|
||||
@Override
|
||||
void setPlaySoundOnCapture(boolean playSoundOnCapture) {
|
||||
mPlaySoundOnCapture = playSoundOnCapture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getPlaySoundOnCapture(){
|
||||
return mPlaySoundOnCapture;
|
||||
}
|
||||
|
||||
@Override
|
||||
void setScanning(boolean isScanning) {
|
||||
if (mIsScanning == isScanning) {
|
||||
@ -709,6 +750,33 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
//mPreview.setDisplayOrientation(deviceOrientation); // this is not needed and messes up the display orientation
|
||||
}
|
||||
|
||||
|
||||
// This is a helper method to query Camera2 legacy status so we don't need
|
||||
// to instantiate and set all its props in order to check if it is legacy or not
|
||||
// and then fallback to Camera1. This way, legacy devices can fall back to Camera1 right away
|
||||
// This method makes sure all cameras are not legacy, so further checks are not needed.
|
||||
public static boolean isLegacy(Context context){
|
||||
try{
|
||||
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
|
||||
String[] ids = manager.getCameraIdList();
|
||||
for (String id : ids) {
|
||||
CameraCharacteristics characteristics = manager.getCameraCharacteristics(id);
|
||||
Integer level = characteristics.get(
|
||||
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
|
||||
if (level == null ||
|
||||
level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
|
||||
Log.w(TAG, "Camera2 can only run in legacy mode and should not be used.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch(CameraAccessException ex){
|
||||
Log.e(TAG, "Failed to check camera legacy status, returning true.", ex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Chooses a camera ID by the specified camera facing ({@link #mFacing}).</p>
|
||||
* <p>This rewrites {@link #mCameraId}, {@link #mCameraCharacteristics}, and optionally
|
||||
@ -720,19 +788,16 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
int internalFacing = INTERNAL_FACINGS.get(mFacing);
|
||||
final String[] ids = mCameraManager.getCameraIdList();
|
||||
if (ids.length == 0) { // No camera
|
||||
throw new RuntimeException("No camera available.");
|
||||
Log.e(TAG, "No cameras available.");
|
||||
return false;
|
||||
}
|
||||
for (String id : ids) {
|
||||
CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(id);
|
||||
Integer level = characteristics.get(
|
||||
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
|
||||
if (level == null ||
|
||||
level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Integer internal = characteristics.get(CameraCharacteristics.LENS_FACING);
|
||||
if (internal == null) {
|
||||
throw new NullPointerException("Unexpected state: LENS_FACING null");
|
||||
Log.e(TAG, "Unexpected state: LENS_FACING null");
|
||||
continue;
|
||||
}
|
||||
if (internal == internalFacing) {
|
||||
mCameraId = id;
|
||||
@ -743,15 +808,11 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
// Not found
|
||||
mCameraId = ids[0];
|
||||
mCameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraId);
|
||||
Integer level = mCameraCharacteristics.get(
|
||||
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
|
||||
if (level == null ||
|
||||
level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Integer internal = mCameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
|
||||
if (internal == null) {
|
||||
throw new NullPointerException("Unexpected state: LENS_FACING null");
|
||||
Log.e(TAG, "Unexpected state: LENS_FACING null");
|
||||
return false;
|
||||
}
|
||||
for (int i = 0, count = INTERNAL_FACINGS.size(); i < count; i++) {
|
||||
if (INTERNAL_FACINGS.valueAt(i) == internal) {
|
||||
@ -764,7 +825,8 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
mFacing = Constants.FACING_BACK;
|
||||
return true;
|
||||
} catch (CameraAccessException e) {
|
||||
throw new RuntimeException("Failed to get a list of camera devices", e);
|
||||
Log.e(TAG, "Failed to get a list of camera devices", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else{
|
||||
@ -774,17 +836,11 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
// for legacy hardware
|
||||
mCameraCharacteristics = mCameraManager.getCameraCharacteristics(_mCameraId);
|
||||
|
||||
Integer level = mCameraCharacteristics.get(
|
||||
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
|
||||
if (level == null ||
|
||||
level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// set our facing variable so orientation also works as expected
|
||||
Integer internal = mCameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
|
||||
if (internal == null) {
|
||||
throw new NullPointerException("Unexpected state: LENS_FACING null");
|
||||
Log.e(TAG, "Unexpected state: LENS_FACING null");
|
||||
return false;
|
||||
}
|
||||
for (int i = 0, count = INTERNAL_FACINGS.size(); i < count; i++) {
|
||||
if (INTERNAL_FACINGS.valueAt(i) == internal) {
|
||||
@ -797,7 +853,8 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
return true;
|
||||
}
|
||||
catch(Exception e){
|
||||
throw new RuntimeException("Failed to get camera characteristics", e);
|
||||
Log.e(TAG, "Failed to get camera characteristics", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -898,6 +955,7 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
mCamera.createCaptureSession(Arrays.asList(surface, mStillImageReader.getSurface(),
|
||||
mScanImageReader.getSurface()), mSessionCallback, null);
|
||||
} catch (CameraAccessException e) {
|
||||
Log.e(TAG, "Failed to start capture session", e);
|
||||
mCallback.onMountError();
|
||||
}
|
||||
}
|
||||
@ -1277,6 +1335,9 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
&& !mCaptureCallback.getOptions().getBoolean("pauseAfterCapture")) {
|
||||
unlockFocus();
|
||||
}
|
||||
if (mPlaySoundOnCapture) {
|
||||
sound.play(MediaActionSound.SHUTTER_CLICK);
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
} catch (CameraAccessException e) {
|
||||
@ -1366,6 +1427,8 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
mMediaRecorder.release();
|
||||
mMediaRecorder = null;
|
||||
|
||||
mCallback.onRecordingEnd();
|
||||
|
||||
if (mVideoPath == null || !new File(mVideoPath).exists()) {
|
||||
// @TODO: implement videoOrientation and deviceOrientation calculation
|
||||
mCallback.onVideoRecorded(null, 0 , 0);
|
||||
@ -1376,6 +1439,18 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
mVideoPath = null;
|
||||
}
|
||||
|
||||
private void pauseMediaRecorder() {
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
mMediaRecorder.pause();
|
||||
}
|
||||
}
|
||||
|
||||
private void resumeMediaRecorder() {
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
mMediaRecorder.resume();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlocks the auto-focus and restart camera preview. This is supposed to be called after
|
||||
* capturing a still picture.
|
||||
|
||||
@ -126,7 +126,7 @@ public class CameraView extends FrameLayout {
|
||||
// Internal setup
|
||||
final PreviewImpl preview = createPreviewImpl(context);
|
||||
mCallbacks = new CallbackBridge();
|
||||
if (fallbackToOldApi || Build.VERSION.SDK_INT < 21) {
|
||||
if (fallbackToOldApi || Build.VERSION.SDK_INT < 21 || Camera2.isLegacy(context)) {
|
||||
mImpl = new Camera1(mCallbacks, preview, mBgHandler);
|
||||
} else if (Build.VERSION.SDK_INT < 23) {
|
||||
mImpl = new Camera2(mCallbacks, preview, context, mBgHandler);
|
||||
@ -146,7 +146,13 @@ public class CameraView extends FrameLayout {
|
||||
|
||||
public void cleanup(){
|
||||
if(mBgThread != null){
|
||||
mBgThread.quitSafely();
|
||||
if(Build.VERSION.SDK_INT < 18){
|
||||
mBgThread.quit();
|
||||
}
|
||||
else{
|
||||
mBgThread.quitSafely();
|
||||
}
|
||||
|
||||
mBgThread = null;
|
||||
}
|
||||
}
|
||||
@ -250,6 +256,7 @@ public class CameraView extends FrameLayout {
|
||||
state.focusDepth = getFocusDepth();
|
||||
state.zoom = getZoom();
|
||||
state.whiteBalance = getWhiteBalance();
|
||||
state.playSoundOnCapture = getPlaySoundOnCapture();
|
||||
state.scanning = getScanning();
|
||||
state.pictureSize = getPictureSize();
|
||||
return state;
|
||||
@ -272,6 +279,7 @@ public class CameraView extends FrameLayout {
|
||||
setFocusDepth(ss.focusDepth);
|
||||
setZoom(ss.zoom);
|
||||
setWhiteBalance(ss.whiteBalance);
|
||||
setPlaySoundOnCapture(ss.playSoundOnCapture);
|
||||
setScanning(ss.scanning);
|
||||
setPictureSize(ss.pictureSize);
|
||||
}
|
||||
@ -284,7 +292,7 @@ public class CameraView extends FrameLayout {
|
||||
boolean wasOpened = isCameraOpened();
|
||||
Parcelable state = onSaveInstanceState();
|
||||
|
||||
if (useCamera2) {
|
||||
if (useCamera2 && !Camera2.isLegacy(mContext)) {
|
||||
if (wasOpened) {
|
||||
stop();
|
||||
}
|
||||
@ -305,7 +313,9 @@ public class CameraView extends FrameLayout {
|
||||
}
|
||||
mImpl = new Camera1(mCallbacks, mImpl.mPreview, mBgHandler);
|
||||
}
|
||||
start();
|
||||
if(wasOpened){
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -313,17 +323,20 @@ public class CameraView extends FrameLayout {
|
||||
* {@link Activity#onResume()}.
|
||||
*/
|
||||
public void start() {
|
||||
if (!mImpl.start()) {
|
||||
if (mImpl.getView() != null) {
|
||||
this.removeView(mImpl.getView());
|
||||
}
|
||||
//store the state and restore this state after fall back to Camera1
|
||||
Parcelable state = onSaveInstanceState();
|
||||
// Camera2 uses legacy hardware layer; fall back to Camera1
|
||||
mImpl = new Camera1(mCallbacks, createPreviewImpl(getContext()), mBgHandler);
|
||||
onRestoreInstanceState(state);
|
||||
mImpl.start();
|
||||
}
|
||||
mImpl.start();
|
||||
|
||||
// this fallback is no longer needed and was too buggy/slow
|
||||
// if (!mImpl.start()) {
|
||||
// if (mImpl.getView() != null) {
|
||||
// this.removeView(mImpl.getView());
|
||||
// }
|
||||
// //store the state and restore this state after fall back to Camera1
|
||||
// Parcelable state = onSaveInstanceState();
|
||||
// // Camera2 uses legacy hardware layer; fall back to Camera1
|
||||
// mImpl = new Camera1(mCallbacks, createPreviewImpl(getContext()), mBgHandler);
|
||||
// onRestoreInstanceState(state);
|
||||
// mImpl.start();
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -518,6 +531,10 @@ public class CameraView extends FrameLayout {
|
||||
mImpl.setFlash(flash);
|
||||
}
|
||||
|
||||
public ArrayList<int[]> getSupportedPreviewFpsRange() {
|
||||
return mImpl.getSupportedPreviewFpsRange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current flash mode.
|
||||
*
|
||||
@ -579,6 +596,14 @@ public class CameraView extends FrameLayout {
|
||||
return mImpl.getWhiteBalance();
|
||||
}
|
||||
|
||||
public void setPlaySoundOnCapture(boolean playSoundOnCapture) {
|
||||
mImpl.setPlaySoundOnCapture(playSoundOnCapture);
|
||||
}
|
||||
|
||||
public boolean getPlaySoundOnCapture() {
|
||||
return mImpl.getPlaySoundOnCapture();
|
||||
}
|
||||
|
||||
public void setScanning(boolean isScanning) { mImpl.setScanning(isScanning);}
|
||||
|
||||
public boolean getScanning() { return mImpl.getScanning(); }
|
||||
@ -598,16 +623,26 @@ public class CameraView extends FrameLayout {
|
||||
* @param maxDuration Maximum duration of the recording, in seconds.
|
||||
* @param maxFileSize Maximum recording file size, in bytes.
|
||||
* @param profile Quality profile of the recording.
|
||||
*
|
||||
* fires {@link Callback#onRecordingStart(CameraView, String, int, int)} and {@link Callback#onRecordingEnd(CameraView)}.
|
||||
*/
|
||||
public boolean record(String path, int maxDuration, int maxFileSize,
|
||||
boolean recordAudio, CamcorderProfile profile, int orientation) {
|
||||
return mImpl.record(path, maxDuration, maxFileSize, recordAudio, profile, orientation);
|
||||
boolean recordAudio, CamcorderProfile profile, int orientation, int fps) {
|
||||
return mImpl.record(path, maxDuration, maxFileSize, recordAudio, profile, orientation, fps);
|
||||
}
|
||||
|
||||
public void stopRecording() {
|
||||
mImpl.stopRecording();
|
||||
}
|
||||
|
||||
public void pauseRecording() {
|
||||
mImpl.pauseRecording();
|
||||
}
|
||||
|
||||
public void resumeRecording() {
|
||||
mImpl.resumeRecording();
|
||||
}
|
||||
|
||||
public void resumePreview() {
|
||||
mImpl.resumePreview();
|
||||
}
|
||||
@ -666,6 +701,20 @@ public class CameraView extends FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecordingStart(String path, int videoOrientation, int deviceOrientation) {
|
||||
for (Callback callback : mCallbacks) {
|
||||
callback.onRecordingStart(CameraView.this, path, videoOrientation, deviceOrientation);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecordingEnd() {
|
||||
for (Callback callback : mCallbacks) {
|
||||
callback.onRecordingEnd(CameraView.this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoRecorded(String path, int videoOrientation, int deviceOrientation) {
|
||||
for (Callback callback : mCallbacks) {
|
||||
@ -714,6 +763,8 @@ public class CameraView extends FrameLayout {
|
||||
|
||||
int whiteBalance;
|
||||
|
||||
boolean playSoundOnCapture;
|
||||
|
||||
boolean scanning;
|
||||
|
||||
Size pictureSize;
|
||||
@ -730,6 +781,7 @@ public class CameraView extends FrameLayout {
|
||||
focusDepth = source.readFloat();
|
||||
zoom = source.readFloat();
|
||||
whiteBalance = source.readInt();
|
||||
playSoundOnCapture = source.readByte() != 0;
|
||||
scanning = source.readByte() != 0;
|
||||
pictureSize = source.readParcelable(loader);
|
||||
}
|
||||
@ -750,6 +802,7 @@ public class CameraView extends FrameLayout {
|
||||
out.writeFloat(focusDepth);
|
||||
out.writeFloat(zoom);
|
||||
out.writeInt(whiteBalance);
|
||||
out.writeByte((byte) (playSoundOnCapture ? 1 : 0));
|
||||
out.writeByte((byte) (scanning ? 1 : 0));
|
||||
out.writeParcelable(pictureSize, flags);
|
||||
}
|
||||
@ -782,16 +835,14 @@ public class CameraView extends FrameLayout {
|
||||
*
|
||||
* @param cameraView The associated {@link CameraView}.
|
||||
*/
|
||||
public void onCameraOpened(CameraView cameraView) {
|
||||
}
|
||||
public void onCameraOpened(CameraView cameraView) {}
|
||||
|
||||
/**
|
||||
* Called when camera is closed.
|
||||
*
|
||||
* @param cameraView The associated {@link CameraView}.
|
||||
*/
|
||||
public void onCameraClosed(CameraView cameraView) {
|
||||
}
|
||||
public void onCameraClosed(CameraView cameraView) {}
|
||||
|
||||
/**
|
||||
* Called when a picture is taken.
|
||||
@ -799,8 +850,23 @@ public class CameraView extends FrameLayout {
|
||||
* @param cameraView The associated {@link CameraView}.
|
||||
* @param data JPEG data.
|
||||
*/
|
||||
public void onPictureTaken(CameraView cameraView, byte[] data, int deviceOrientation) {
|
||||
}
|
||||
public void onPictureTaken(CameraView cameraView, byte[] data, int deviceOrientation) {}
|
||||
|
||||
/**
|
||||
* Called when a video recording starts
|
||||
*
|
||||
* @param cameraView The associated {@link CameraView}.
|
||||
* @param path Path to recoredd video file.
|
||||
*/
|
||||
public void onRecordingStart(CameraView cameraView, String path, int videoOrientation, int deviceOrientation) {}
|
||||
|
||||
/**
|
||||
* Called when a video recording ends, but before video is saved/processed.
|
||||
*
|
||||
* @param cameraView The associated {@link CameraView}.
|
||||
* @param path Path to recoredd video file.
|
||||
*/
|
||||
public void onRecordingEnd(CameraView cameraView){}
|
||||
|
||||
/**
|
||||
* Called when a video is recorded.
|
||||
@ -808,11 +874,9 @@ public class CameraView extends FrameLayout {
|
||||
* @param cameraView The associated {@link CameraView}.
|
||||
* @param path Path to recoredd video file.
|
||||
*/
|
||||
public void onVideoRecorded(CameraView cameraView, String path, int videoOrientation, int deviceOrientation) {
|
||||
}
|
||||
public void onVideoRecorded(CameraView cameraView, String path, int videoOrientation, int deviceOrientation) {}
|
||||
|
||||
public void onFramePreview(CameraView cameraView, byte[] data, int width, int height, int orientation) {
|
||||
}
|
||||
public void onFramePreview(CameraView cameraView, byte[] data, int width, int height, int orientation) {}
|
||||
|
||||
public void onMountError(CameraView cameraView) {}
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ import android.os.Handler;
|
||||
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
@ -61,7 +62,7 @@ abstract class CameraViewImpl {
|
||||
abstract boolean isCameraOpened();
|
||||
|
||||
abstract void setFacing(int facing);
|
||||
|
||||
|
||||
abstract int getFacing();
|
||||
|
||||
abstract void setCameraId(String id);
|
||||
@ -100,10 +101,14 @@ abstract class CameraViewImpl {
|
||||
abstract void takePicture(ReadableMap options);
|
||||
|
||||
abstract boolean record(String path, int maxDuration, int maxFileSize,
|
||||
boolean recordAudio, CamcorderProfile profile, int orientation);
|
||||
boolean recordAudio, CamcorderProfile profile, int orientation, int fps);
|
||||
|
||||
abstract void stopRecording();
|
||||
|
||||
abstract void pauseRecording();
|
||||
|
||||
abstract void resumeRecording();
|
||||
|
||||
abstract int getCameraOrientation();
|
||||
|
||||
abstract void setDisplayOrientation(int displayOrientation);
|
||||
@ -120,10 +125,16 @@ abstract class CameraViewImpl {
|
||||
|
||||
abstract float getZoom();
|
||||
|
||||
abstract public ArrayList<int[]> getSupportedPreviewFpsRange();
|
||||
|
||||
abstract void setWhiteBalance(int whiteBalance);
|
||||
|
||||
abstract int getWhiteBalance();
|
||||
|
||||
abstract void setPlaySoundOnCapture(boolean playSoundOnCapture);
|
||||
|
||||
abstract boolean getPlaySoundOnCapture();
|
||||
|
||||
abstract void setScanning(boolean isScanning);
|
||||
|
||||
abstract boolean getScanning();
|
||||
@ -146,6 +157,10 @@ abstract class CameraViewImpl {
|
||||
|
||||
void onVideoRecorded(String path, int videoOrientation, int deviceOrientation);
|
||||
|
||||
void onRecordingStart(String path, int videoOrientation, int deviceOrientation);
|
||||
|
||||
void onRecordingEnd();
|
||||
|
||||
void onFramePreview(byte[] data, int width, int height, int orientation);
|
||||
|
||||
void onMountError();
|
||||
|
||||
@ -20,6 +20,7 @@ import com.google.android.cameraview.Size;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Properties;
|
||||
import java.util.List;
|
||||
@ -323,6 +324,48 @@ public class CameraModule extends ReactContextBaseJavaModule {
|
||||
});
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void pauseRecording(final int viewTag) {
|
||||
final ReactApplicationContext context = getReactApplicationContext();
|
||||
UIManagerModule uiManager = context.getNativeModule(UIManagerModule.class);
|
||||
uiManager.addUIBlock(new UIBlock() {
|
||||
@Override
|
||||
public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) {
|
||||
final RNCameraView cameraView;
|
||||
|
||||
try {
|
||||
cameraView = (RNCameraView) nativeViewHierarchyManager.resolveView(viewTag);
|
||||
if (cameraView.isCameraOpened()) {
|
||||
cameraView.pauseRecording();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void resumeRecording(final int viewTag) {
|
||||
final ReactApplicationContext context = getReactApplicationContext();
|
||||
UIManagerModule uiManager = context.getNativeModule(UIManagerModule.class);
|
||||
uiManager.addUIBlock(new UIBlock() {
|
||||
@Override
|
||||
public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) {
|
||||
final RNCameraView cameraView;
|
||||
|
||||
try {
|
||||
cameraView = (RNCameraView) nativeViewHierarchyManager.resolveView(viewTag);
|
||||
if (cameraView.isCameraOpened()) {
|
||||
cameraView.resumeRecording();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getSupportedRatios(final int viewTag, final Promise promise) {
|
||||
final ReactApplicationContext context = getReactApplicationContext();
|
||||
@ -422,4 +465,31 @@ public class CameraModule extends ReactContextBaseJavaModule {
|
||||
}
|
||||
promise.resolve(false);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getSupportedPreviewFpsRange(final int viewTag, final Promise promise) {
|
||||
final ReactApplicationContext context = getReactApplicationContext();
|
||||
UIManagerModule uiManager = context.getNativeModule(UIManagerModule.class);
|
||||
uiManager.addUIBlock(new UIBlock() {
|
||||
@Override
|
||||
public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) {
|
||||
final RNCameraView cameraView;
|
||||
|
||||
try {
|
||||
cameraView = (RNCameraView) nativeViewHierarchyManager.resolveView(viewTag);
|
||||
WritableArray result = Arguments.createArray();
|
||||
ArrayList<int[]> ranges = cameraView.getSupportedPreviewFpsRange();
|
||||
for (int[] range : ranges) {
|
||||
WritableMap m = new WritableNativeMap();
|
||||
m.putInt("MAXIMUM_FPS", range[0]);
|
||||
m.putInt("MINIMUM_FPS", range[1]);
|
||||
result.pushMap(m);
|
||||
}
|
||||
promise.resolve(result);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,7 +25,11 @@ public class CameraViewManager extends ViewGroupManager<RNCameraView> {
|
||||
EVENT_ON_BARCODE_DETECTION_ERROR("onGoogleVisionBarcodeDetectionError"),
|
||||
EVENT_ON_TEXT_RECOGNIZED("onTextRecognized"),
|
||||
EVENT_ON_PICTURE_TAKEN("onPictureTaken"),
|
||||
EVENT_ON_PICTURE_SAVED("onPictureSaved");
|
||||
EVENT_ON_PICTURE_SAVED("onPictureSaved"),
|
||||
EVENT_ON_RECORDING_START("onRecordingStart"),
|
||||
EVENT_ON_RECORDING_END("onRecordingEnd"),
|
||||
EVENT_ON_TOUCH("onTouch");
|
||||
|
||||
|
||||
private final String mName;
|
||||
|
||||
@ -117,6 +121,10 @@ public class CameraViewManager extends ViewGroupManager<RNCameraView> {
|
||||
view.setZoom(zoom);
|
||||
}
|
||||
|
||||
@ReactProp(name = "useNativeZoom")
|
||||
public void setUseNativeZoom(RNCameraView view, boolean useNativeZoom) {
|
||||
view.setUseNativeZoom(useNativeZoom);
|
||||
}
|
||||
@ReactProp(name = "whiteBalance")
|
||||
public void setWhiteBalance(RNCameraView view, int whiteBalance) {
|
||||
view.setWhiteBalance(whiteBalance);
|
||||
@ -127,6 +135,11 @@ public class CameraViewManager extends ViewGroupManager<RNCameraView> {
|
||||
view.setPictureSize(size.equals("None") ? null : Size.parse(size));
|
||||
}
|
||||
|
||||
@ReactProp(name = "playSoundOnCapture")
|
||||
public void setPlaySoundOnCapture(RNCameraView view, boolean playSoundOnCapture) {
|
||||
view.setPlaySoundOnCapture(playSoundOnCapture);
|
||||
}
|
||||
|
||||
@ReactProp(name = "barCodeTypes")
|
||||
public void setBarCodeTypes(RNCameraView view, ReadableArray barCodeTypes) {
|
||||
if (barCodeTypes == null) {
|
||||
@ -139,6 +152,11 @@ public class CameraViewManager extends ViewGroupManager<RNCameraView> {
|
||||
view.setBarCodeTypes(result);
|
||||
}
|
||||
|
||||
@ReactProp(name = "detectedImageInEvent")
|
||||
public void setDetectedImageInEvent(RNCameraView view, boolean detectedImageInEvent) {
|
||||
view.setDetectedImageInEvent(detectedImageInEvent);
|
||||
}
|
||||
|
||||
@ReactProp(name = "barCodeScannerEnabled")
|
||||
public void setBarCodeScanning(RNCameraView view, boolean barCodeScannerEnabled) {
|
||||
view.setShouldScanBarCodes(barCodeScannerEnabled);
|
||||
@ -149,9 +167,9 @@ public class CameraViewManager extends ViewGroupManager<RNCameraView> {
|
||||
view.setUsingCamera2Api(useCamera2Api);
|
||||
}
|
||||
|
||||
@ReactProp(name = "playSoundOnCapture")
|
||||
public void setPlaySoundOnCapture(RNCameraView view, boolean playSoundOnCapture) {
|
||||
view.setPlaySoundOnCapture(playSoundOnCapture);
|
||||
@ReactProp(name = "touchDetectorEnabled")
|
||||
public void setTouchDetectorEnabled(RNCameraView view, boolean touchDetectorEnabled) {
|
||||
view.setShouldDetectTouches(touchDetectorEnabled);
|
||||
}
|
||||
|
||||
@ReactProp(name = "faceDetectorEnabled")
|
||||
@ -198,4 +216,26 @@ public class CameraViewManager extends ViewGroupManager<RNCameraView> {
|
||||
public void setTextRecognizing(RNCameraView view, boolean textRecognizerEnabled) {
|
||||
view.setShouldRecognizeText(textRecognizerEnabled);
|
||||
}
|
||||
|
||||
/**---limit scan area addition---**/
|
||||
@ReactProp(name = "rectOfInterest")
|
||||
public void setRectOfInterest(RNCameraView view, ReadableMap coordinates) {
|
||||
if(coordinates != null){
|
||||
float x = (float) coordinates.getDouble("x");
|
||||
float y = (float) coordinates.getDouble("y");
|
||||
float width = (float) coordinates.getDouble("width");
|
||||
float height = (float) coordinates.getDouble("height");
|
||||
view.setRectOfInterest(x, y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactProp(name = "cameraViewDimensions")
|
||||
public void setCameraViewDimensions(RNCameraView view, ReadableMap dimensions) {
|
||||
if(dimensions != null){
|
||||
int cameraViewWidth = (int) dimensions.getDouble("width");
|
||||
int cameraViewHeight = (int) dimensions.getDouble("height");
|
||||
view.setCameraViewDimensions(cameraViewWidth, cameraViewHeight);
|
||||
}
|
||||
}
|
||||
/**---limit scan area addition---**/
|
||||
}
|
||||
|
||||
@ -3,11 +3,20 @@ package org.reactnative.camera;
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ImageFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.YuvImage;
|
||||
import android.media.CamcorderProfile;
|
||||
import android.media.MediaActionSound;
|
||||
import android.os.Build;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ScaleGestureDetector;
|
||||
import android.view.View;
|
||||
import android.os.AsyncTask;
|
||||
import com.facebook.react.bridge.*;
|
||||
@ -22,6 +31,7 @@ import org.reactnative.camera.tasks.*;
|
||||
import org.reactnative.camera.utils.RNFileUtils;
|
||||
import org.reactnative.facedetector.RNFaceDetector;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
@ -36,13 +46,18 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
private Map<Promise, File> mPictureTakenDirectories = new ConcurrentHashMap<>();
|
||||
private Promise mVideoRecordedPromise;
|
||||
private List<String> mBarCodeTypes = null;
|
||||
private Boolean mPlaySoundOnCapture = false;
|
||||
private boolean mDetectedImageInEvent = false;
|
||||
|
||||
private ScaleGestureDetector mScaleGestureDetector;
|
||||
private GestureDetector mGestureDetector;
|
||||
|
||||
|
||||
private boolean mIsPaused = false;
|
||||
private boolean mIsNew = true;
|
||||
private boolean invertImageData = false;
|
||||
private Boolean mIsRecording = false;
|
||||
private Boolean mIsRecordingInterrupted = false;
|
||||
private boolean mUseNativeZoom=false;
|
||||
|
||||
// Concurrency lock for scanners to avoid flooding the runtime
|
||||
public volatile boolean barCodeScannerTaskLock = false;
|
||||
@ -58,6 +73,7 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
private boolean mShouldGoogleDetectBarcodes = false;
|
||||
private boolean mShouldScanBarCodes = false;
|
||||
private boolean mShouldRecognizeText = false;
|
||||
private boolean mShouldDetectTouches = false;
|
||||
private int mFaceDetectorMode = RNFaceDetector.FAST_MODE;
|
||||
private int mFaceDetectionLandmarks = RNFaceDetector.NO_LANDMARKS;
|
||||
private int mFaceDetectionClassifications = RNFaceDetector.NO_CLASSIFICATIONS;
|
||||
@ -67,6 +83,15 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
private int mPaddingX;
|
||||
private int mPaddingY;
|
||||
|
||||
// Limit Android Scan Area
|
||||
private boolean mLimitScanArea = false;
|
||||
private float mScanAreaX = 0.0f;
|
||||
private float mScanAreaY = 0.0f;
|
||||
private float mScanAreaWidth = 0.0f;
|
||||
private float mScanAreaHeight = 0.0f;
|
||||
private int mCameraViewWidth = 0;
|
||||
private int mCameraViewHeight = 0;
|
||||
|
||||
public RNCameraView(ThemedReactContext themedReactContext) {
|
||||
super(themedReactContext, true);
|
||||
mThemedReactContext = themedReactContext;
|
||||
@ -101,6 +126,20 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
RNCameraViewHelper.emitPictureTakenEvent(cameraView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecordingStart(CameraView cameraView, String path, int videoOrientation, int deviceOrientation) {
|
||||
WritableMap result = Arguments.createMap();
|
||||
result.putInt("videoOrientation", videoOrientation);
|
||||
result.putInt("deviceOrientation", deviceOrientation);
|
||||
result.putString("uri", RNFileUtils.uriFromFile(new File(path)).toString());
|
||||
RNCameraViewHelper.emitRecordingStartEvent(cameraView, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecordingEnd(CameraView cameraView) {
|
||||
RNCameraViewHelper.emitRecordingEndEvent(cameraView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoRecorded(CameraView cameraView, String path, int videoOrientation, int deviceOrientation) {
|
||||
if (mVideoRecordedPromise != null) {
|
||||
@ -138,7 +177,7 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
if (willCallBarCodeTask) {
|
||||
barCodeScannerTaskLock = true;
|
||||
BarCodeScannerAsyncTaskDelegate delegate = (BarCodeScannerAsyncTaskDelegate) cameraView;
|
||||
new BarCodeScannerAsyncTask(delegate, mMultiFormatReader, data, width, height).execute();
|
||||
new BarCodeScannerAsyncTask(delegate, mMultiFormatReader, data, width, height, mLimitScanArea, mScanAreaX, mScanAreaY, mScanAreaWidth, mScanAreaHeight, mCameraViewWidth, mCameraViewHeight, getAspectRatio().toFloat()).execute();
|
||||
}
|
||||
|
||||
if (willCallFaceTask) {
|
||||
@ -162,7 +201,9 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
}
|
||||
}
|
||||
BarcodeDetectorAsyncTaskDelegate delegate = (BarcodeDetectorAsyncTaskDelegate) cameraView;
|
||||
new BarcodeDetectorAsyncTask(delegate, mGoogleBarcodeDetector, data, width, height, correctRotation, getResources().getDisplayMetrics().density, getFacing(), getWidth(), getHeight(), mPaddingX, mPaddingY).execute();
|
||||
new BarcodeDetectorAsyncTask(delegate, mGoogleBarcodeDetector, data, width, height,
|
||||
correctRotation, getResources().getDisplayMetrics().density, getFacing(),
|
||||
getWidth(), getHeight(), mPaddingX, mPaddingY).execute();
|
||||
}
|
||||
|
||||
if (willCallTextTask) {
|
||||
@ -222,8 +263,8 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
initBarcodeReader();
|
||||
}
|
||||
|
||||
public void setPlaySoundOnCapture(Boolean playSoundOnCapture) {
|
||||
mPlaySoundOnCapture = playSoundOnCapture;
|
||||
public void setDetectedImageInEvent(boolean detectedImageInEvent) {
|
||||
this.mDetectedImageInEvent = detectedImageInEvent;
|
||||
}
|
||||
|
||||
public void takePicture(final ReadableMap options, final Promise promise, final File cacheDirectory) {
|
||||
@ -233,10 +274,7 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
mPictureTakenPromises.add(promise);
|
||||
mPictureTakenOptions.put(promise, options);
|
||||
mPictureTakenDirectories.put(promise, cacheDirectory);
|
||||
if (mPlaySoundOnCapture) {
|
||||
MediaActionSound sound = new MediaActionSound();
|
||||
sound.play(MediaActionSound.SHUTTER_CLICK);
|
||||
}
|
||||
|
||||
try {
|
||||
RNCameraView.super.takePicture(options);
|
||||
} catch (Exception e) {
|
||||
@ -263,6 +301,7 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
String path = options.hasKey("path") ? options.getString("path") : RNFileUtils.getOutputFilePath(cacheDirectory, ".mp4");
|
||||
int maxDuration = options.hasKey("maxDuration") ? options.getInt("maxDuration") : -1;
|
||||
int maxFileSize = options.hasKey("maxFileSize") ? options.getInt("maxFileSize") : -1;
|
||||
int fps = options.hasKey("fps") ? options.getInt("fps") : -1;
|
||||
|
||||
CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
|
||||
if (options.hasKey("quality")) {
|
||||
@ -282,7 +321,7 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
orientation = options.getInt("orientation");
|
||||
}
|
||||
|
||||
if (RNCameraView.super.record(path, maxDuration * 1000, maxFileSize, recordAudio, profile, orientation)) {
|
||||
if (RNCameraView.super.record(path, maxDuration * 1000, maxFileSize, recordAudio, profile, orientation, fps)) {
|
||||
mIsRecording = true;
|
||||
mVideoRecordedPromise = promise;
|
||||
} else {
|
||||
@ -326,13 +365,28 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
setScanning(mShouldDetectFaces || mShouldGoogleDetectBarcodes || mShouldScanBarCodes || mShouldRecognizeText);
|
||||
}
|
||||
|
||||
public void onBarCodeRead(Result barCode, int width, int height) {
|
||||
public void onBarCodeRead(Result barCode, int width, int height, byte[] imageData) {
|
||||
String barCodeType = barCode.getBarcodeFormat().toString();
|
||||
if (!mShouldScanBarCodes || !mBarCodeTypes.contains(barCodeType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
RNCameraViewHelper.emitBarCodeReadEvent(this, barCode, width, height);
|
||||
final byte[] compressedImage;
|
||||
if (mDetectedImageInEvent) {
|
||||
try {
|
||||
// https://stackoverflow.com/a/32793908/122441
|
||||
final YuvImage yuvImage = new YuvImage(imageData, ImageFormat.NV21, width, height, null);
|
||||
final ByteArrayOutputStream imageStream = new ByteArrayOutputStream();
|
||||
yuvImage.compressToJpeg(new Rect(0, 0, width, height), 100, imageStream);
|
||||
compressedImage = imageStream.toByteArray();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(String.format("Error decoding imageData from NV21 format (%d bytes)", imageData.length), e);
|
||||
}
|
||||
} else {
|
||||
compressedImage = null;
|
||||
}
|
||||
|
||||
RNCameraViewHelper.emitBarCodeReadEvent(this, barCode, width, height, compressedImage);
|
||||
}
|
||||
|
||||
public void onBarCodeScanningTaskCompleted() {
|
||||
@ -342,6 +396,49 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
}
|
||||
}
|
||||
|
||||
// Limit Scan Area
|
||||
public void setRectOfInterest(float x, float y, float width, float height) {
|
||||
this.mLimitScanArea = true;
|
||||
this.mScanAreaX = x;
|
||||
this.mScanAreaY = y;
|
||||
this.mScanAreaWidth = width;
|
||||
this.mScanAreaHeight = height;
|
||||
}
|
||||
public void setCameraViewDimensions(int width, int height) {
|
||||
this.mCameraViewWidth = width;
|
||||
this.mCameraViewHeight = height;
|
||||
}
|
||||
|
||||
|
||||
public void setShouldDetectTouches(boolean shouldDetectTouches) {
|
||||
if(!mShouldDetectTouches && shouldDetectTouches){
|
||||
mGestureDetector=new GestureDetector(mThemedReactContext,onGestureListener);
|
||||
}else{
|
||||
mGestureDetector=null;
|
||||
}
|
||||
this.mShouldDetectTouches = shouldDetectTouches;
|
||||
}
|
||||
|
||||
public void setUseNativeZoom(boolean useNativeZoom){
|
||||
if(!mUseNativeZoom && useNativeZoom){
|
||||
mScaleGestureDetector = new ScaleGestureDetector(mThemedReactContext,onScaleGestureListener);
|
||||
}else{
|
||||
mScaleGestureDetector=null;
|
||||
}
|
||||
mUseNativeZoom=useNativeZoom;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if(mUseNativeZoom) {
|
||||
mScaleGestureDetector.onTouchEvent(event);
|
||||
}
|
||||
if(mShouldDetectTouches){
|
||||
mGestureDetector.onTouchEvent(event);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initial setup of the face detector
|
||||
*/
|
||||
@ -437,11 +534,28 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
mGoogleVisionBarCodeMode = barcodeMode;
|
||||
}
|
||||
|
||||
public void onBarcodesDetected(WritableArray barcodesDetected) {
|
||||
public void onBarcodesDetected(WritableArray barcodesDetected, int width, int height, byte[] imageData) {
|
||||
if (!mShouldGoogleDetectBarcodes) {
|
||||
return;
|
||||
}
|
||||
RNCameraViewHelper.emitBarcodesDetectedEvent(this, barcodesDetected);
|
||||
|
||||
// See discussion in https://github.com/react-native-community/react-native-camera/issues/2786
|
||||
final byte[] compressedImage;
|
||||
if (mDetectedImageInEvent) {
|
||||
try {
|
||||
// https://stackoverflow.com/a/32793908/122441
|
||||
final YuvImage yuvImage = new YuvImage(imageData, ImageFormat.NV21, width, height, null);
|
||||
final ByteArrayOutputStream imageStream = new ByteArrayOutputStream();
|
||||
yuvImage.compressToJpeg(new Rect(0, 0, width, height), 100, imageStream);
|
||||
compressedImage = imageStream.toByteArray();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(String.format("Error decoding imageData from NV21 format (%d bytes)", imageData.length), e);
|
||||
}
|
||||
} else {
|
||||
compressedImage = null;
|
||||
}
|
||||
|
||||
RNCameraViewHelper.emitBarcodesDetectedEvent(this, barcodesDetected, compressedImage);
|
||||
}
|
||||
|
||||
public void onBarcodeDetectionError(RNBarcodeDetector barcodeDetector) {
|
||||
@ -522,10 +636,28 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
mGoogleBarcodeDetector.release();
|
||||
}
|
||||
mMultiFormatReader = null;
|
||||
stop();
|
||||
mThemedReactContext.removeLifecycleEventListener(this);
|
||||
|
||||
this.cleanup();
|
||||
// camera release can be quite expensive. Run in on bg handler
|
||||
// and cleanup last once everything has finished
|
||||
mBgHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
stop();
|
||||
cleanup();
|
||||
}
|
||||
});
|
||||
}
|
||||
private void onZoom(float scale){
|
||||
float currentZoom=getZoom();
|
||||
float nextZoom=currentZoom+(scale-1.0f);
|
||||
|
||||
if(nextZoom > currentZoom){
|
||||
setZoom(Math.min(nextZoom,1.0f));
|
||||
}else{
|
||||
setZoom(Math.max(nextZoom,0.0f));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean hasCameraPermissions() {
|
||||
@ -536,4 +668,43 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
return true;
|
||||
}
|
||||
}
|
||||
private int scalePosition(float raw){
|
||||
Resources resources = getResources();
|
||||
Configuration config = resources.getConfiguration();
|
||||
DisplayMetrics dm = resources.getDisplayMetrics();
|
||||
return (int)(raw/ dm.density);
|
||||
}
|
||||
private GestureDetector.SimpleOnGestureListener onGestureListener = new GestureDetector.SimpleOnGestureListener(){
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent e) {
|
||||
RNCameraViewHelper.emitTouchEvent(RNCameraView.this,false,scalePosition(e.getX()),scalePosition(e.getY()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDoubleTap(MotionEvent e) {
|
||||
RNCameraViewHelper.emitTouchEvent(RNCameraView.this,true,scalePosition(e.getX()),scalePosition(e.getY()));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
private ScaleGestureDetector.OnScaleGestureListener onScaleGestureListener = new ScaleGestureDetector.OnScaleGestureListener() {
|
||||
|
||||
@Override
|
||||
public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
|
||||
onZoom(scaleGestureDetector.getScaleFactor());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
|
||||
onZoom(scaleGestureDetector.getScaleFactor());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -10,7 +10,6 @@ import androidx.exifinterface.media.ExifInterface;
|
||||
import android.view.ViewGroup;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
@ -219,6 +218,44 @@ public class RNCameraViewHelper {
|
||||
});
|
||||
}
|
||||
|
||||
// video recording start/end events
|
||||
|
||||
public static void emitRecordingStartEvent(final ViewGroup view, final WritableMap response) {
|
||||
|
||||
final ReactContext reactContext = (ReactContext) view.getContext();
|
||||
reactContext.runOnNativeModulesQueueThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
RecordingStartEvent event = RecordingStartEvent.obtain(view.getId(), response);
|
||||
reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void emitRecordingEndEvent(final ViewGroup view) {
|
||||
|
||||
final ReactContext reactContext = (ReactContext) view.getContext();
|
||||
reactContext.runOnNativeModulesQueueThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
RecordingEndEvent event = RecordingEndEvent.obtain(view.getId());
|
||||
reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Touch event
|
||||
public static void emitTouchEvent(final ViewGroup view, final boolean isDoubleTap, final int x, final int y) {
|
||||
|
||||
final ReactContext reactContext = (ReactContext) view.getContext();
|
||||
reactContext.runOnNativeModulesQueueThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
TouchEvent event = TouchEvent.obtain(view.getId(), isDoubleTap, x, y);
|
||||
reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent(event);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
// Face detection events
|
||||
|
||||
public static void emitFacesDetectedEvent(final ViewGroup view, final WritableArray data) {
|
||||
@ -247,13 +284,13 @@ public class RNCameraViewHelper {
|
||||
|
||||
// Barcode detection events
|
||||
|
||||
public static void emitBarcodesDetectedEvent(final ViewGroup view, final WritableArray barcodes) {
|
||||
public static void emitBarcodesDetectedEvent(final ViewGroup view, final WritableArray barcodes, final byte[] compressedImage) {
|
||||
|
||||
final ReactContext reactContext = (ReactContext) view.getContext();
|
||||
reactContext.runOnNativeModulesQueueThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
BarcodesDetectedEvent event = BarcodesDetectedEvent.obtain(view.getId(), barcodes);
|
||||
BarcodesDetectedEvent event = BarcodesDetectedEvent.obtain(view.getId(), barcodes, compressedImage);
|
||||
reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent(event);
|
||||
}
|
||||
});
|
||||
@ -273,12 +310,12 @@ public class RNCameraViewHelper {
|
||||
|
||||
// Bar code read event
|
||||
|
||||
public static void emitBarCodeReadEvent(final ViewGroup view, final Result barCode, final int width, final int height) {
|
||||
public static void emitBarCodeReadEvent(final ViewGroup view, final Result barCode, final int width, final int height, final byte[] compressedImage) {
|
||||
final ReactContext reactContext = (ReactContext) view.getContext();
|
||||
reactContext.runOnNativeModulesQueueThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
BarCodeReadEvent event = BarCodeReadEvent.obtain(view.getId(), barCode, width, height);
|
||||
BarCodeReadEvent event = BarCodeReadEvent.obtain(view.getId(), barCode, width, height, compressedImage);
|
||||
reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent(event);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package org.reactnative.camera.events;
|
||||
|
||||
import android.util.Base64;
|
||||
|
||||
import androidx.core.util.Pools;
|
||||
|
||||
import org.reactnative.camera.CameraViewManager;
|
||||
@ -20,23 +22,25 @@ public class BarCodeReadEvent extends Event<BarCodeReadEvent> {
|
||||
private Result mBarCode;
|
||||
private int mWidth;
|
||||
private int mHeight;
|
||||
private byte[] mCompressedImage;
|
||||
|
||||
private BarCodeReadEvent() {}
|
||||
|
||||
public static BarCodeReadEvent obtain(int viewTag, Result barCode, int width, int height) {
|
||||
public static BarCodeReadEvent obtain(int viewTag, Result barCode, int width, int height, byte[] compressedImage) {
|
||||
BarCodeReadEvent event = EVENTS_POOL.acquire();
|
||||
if (event == null) {
|
||||
event = new BarCodeReadEvent();
|
||||
}
|
||||
event.init(viewTag, barCode, width, height);
|
||||
event.init(viewTag, barCode, width, height, compressedImage);
|
||||
return event;
|
||||
}
|
||||
|
||||
private void init(int viewTag, Result barCode, int width, int height) {
|
||||
private void init(int viewTag, Result barCode, int width, int height, byte[] compressedImage) {
|
||||
super.init(viewTag);
|
||||
mBarCode = barCode;
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
mCompressedImage = compressedImage;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,6 +99,9 @@ public class BarCodeReadEvent extends Event<BarCodeReadEvent> {
|
||||
eventOrigin.putInt("height", mHeight);
|
||||
eventOrigin.putInt("width", mWidth);
|
||||
event.putMap("bounds", eventOrigin);
|
||||
if (mCompressedImage != null) {
|
||||
event.putString("image", Base64.encodeToString(mCompressedImage, Base64.NO_WRAP));
|
||||
}
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
package org.reactnative.camera.events;
|
||||
|
||||
import android.util.Base64;
|
||||
|
||||
import androidx.core.util.Pools;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
@ -15,28 +17,30 @@ public class BarcodesDetectedEvent extends Event<BarcodesDetectedEvent> {
|
||||
new Pools.SynchronizedPool<>(3);
|
||||
|
||||
private WritableArray mBarcodes;
|
||||
private byte[] mCompressedImage;
|
||||
|
||||
private BarcodesDetectedEvent() {
|
||||
}
|
||||
|
||||
public static BarcodesDetectedEvent obtain(
|
||||
int viewTag,
|
||||
WritableArray barcodes
|
||||
) {
|
||||
int viewTag,
|
||||
WritableArray barcodes,
|
||||
byte[] compressedImage) {
|
||||
BarcodesDetectedEvent event = EVENTS_POOL.acquire();
|
||||
if (event == null) {
|
||||
event = new BarcodesDetectedEvent();
|
||||
}
|
||||
event.init(viewTag, barcodes);
|
||||
event.init(viewTag, barcodes, compressedImage);
|
||||
return event;
|
||||
}
|
||||
|
||||
private void init(
|
||||
int viewTag,
|
||||
WritableArray barcodes
|
||||
) {
|
||||
int viewTag,
|
||||
WritableArray barcodes,
|
||||
byte[] compressedImage) {
|
||||
super.init(viewTag);
|
||||
mBarcodes = barcodes;
|
||||
mCompressedImage = compressedImage;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -68,6 +72,9 @@ public class BarcodesDetectedEvent extends Event<BarcodesDetectedEvent> {
|
||||
event.putString("type", "barcode");
|
||||
event.putArray("barcodes", mBarcodes);
|
||||
event.putInt("target", getViewTag());
|
||||
if (mCompressedImage != null) {
|
||||
event.putString("image", Base64.encodeToString(mCompressedImage, Base64.NO_WRAP));
|
||||
}
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
package org.reactnative.camera.events;
|
||||
|
||||
import androidx.core.util.Pools;
|
||||
|
||||
import org.reactnative.camera.CameraViewManager;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.uimanager.events.Event;
|
||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||
|
||||
public class RecordingEndEvent extends Event<RecordingEndEvent> {
|
||||
private static final Pools.SynchronizedPool<RecordingEndEvent> EVENTS_POOL = new Pools.SynchronizedPool<>(3);
|
||||
private RecordingEndEvent() {}
|
||||
|
||||
public static RecordingEndEvent obtain(int viewTag) {
|
||||
RecordingEndEvent event = EVENTS_POOL.acquire();
|
||||
if (event == null) {
|
||||
event = new RecordingEndEvent();
|
||||
}
|
||||
event.init(viewTag);
|
||||
return event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getCoalescingKey() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventName() {
|
||||
return CameraViewManager.Events.EVENT_ON_RECORDING_END.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatch(RCTEventEmitter rctEventEmitter) {
|
||||
rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData());
|
||||
}
|
||||
|
||||
private WritableMap serializeEventData() {
|
||||
return Arguments.createMap();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package org.reactnative.camera.events;
|
||||
|
||||
import androidx.core.util.Pools;
|
||||
|
||||
import org.reactnative.camera.CameraViewManager;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.uimanager.events.Event;
|
||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||
|
||||
public class RecordingStartEvent extends Event<RecordingStartEvent> {
|
||||
private static final Pools.SynchronizedPool<RecordingStartEvent> EVENTS_POOL = new Pools.SynchronizedPool<>(3);
|
||||
private RecordingStartEvent() {}
|
||||
|
||||
private WritableMap mResponse;
|
||||
|
||||
public static RecordingStartEvent obtain(int viewTag, WritableMap response) {
|
||||
RecordingStartEvent event = EVENTS_POOL.acquire();
|
||||
if (event == null) {
|
||||
event = new RecordingStartEvent();
|
||||
}
|
||||
event.init(viewTag, response);
|
||||
return event;
|
||||
}
|
||||
|
||||
private void init(int viewTag, WritableMap response) {
|
||||
super.init(viewTag);
|
||||
mResponse = response;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public short getCoalescingKey() {
|
||||
// int hashCode = mResponse.getString("uri").hashCode() % Short.MAX_VALUE;
|
||||
// return (short) hashCode;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public String getEventName() {
|
||||
return CameraViewManager.Events.EVENT_ON_RECORDING_START.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatch(RCTEventEmitter rctEventEmitter) {
|
||||
rctEventEmitter.receiveEvent(getViewTag(), getEventName(), mResponse);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
package org.reactnative.camera.events;
|
||||
|
||||
import androidx.core.util.Pools;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.uimanager.events.Event;
|
||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||
|
||||
|
||||
import org.reactnative.camera.CameraViewManager;
|
||||
|
||||
|
||||
public class TouchEvent extends Event<TouchEvent> {
|
||||
private static final Pools.SynchronizedPool<TouchEvent> EVENTS_POOL =
|
||||
new Pools.SynchronizedPool<>(3);
|
||||
|
||||
private int mX;
|
||||
private int mY;
|
||||
private boolean mIsDoubleTap;
|
||||
|
||||
private TouchEvent() {}
|
||||
|
||||
public static TouchEvent obtain(int viewTag, boolean isDoubleTap, int x, int y) {
|
||||
TouchEvent event = EVENTS_POOL.acquire();
|
||||
if (event == null) {
|
||||
event = new TouchEvent();
|
||||
}
|
||||
event.init(viewTag, isDoubleTap, x, y);
|
||||
return event;
|
||||
}
|
||||
|
||||
private void init(int viewTag, boolean isDoubleTap, int x, int y) {
|
||||
super.init(viewTag);
|
||||
mX = x;
|
||||
mY = y;
|
||||
mIsDoubleTap=isDoubleTap;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public short getCoalescingKey() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventName() {
|
||||
return CameraViewManager.Events.EVENT_ON_TOUCH.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatch(RCTEventEmitter rctEventEmitter) {
|
||||
rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData());
|
||||
}
|
||||
|
||||
private WritableMap serializeEventData() {
|
||||
WritableMap event = Arguments.createMap();
|
||||
|
||||
event.putInt("target", getViewTag());
|
||||
|
||||
WritableMap touchOrigin = Arguments.createMap();
|
||||
touchOrigin.putInt("x", mX);
|
||||
touchOrigin.putInt("y",mY);
|
||||
|
||||
event.putBoolean("isDoubleTap", mIsDoubleTap);
|
||||
event.putMap("touchOrigin", touchOrigin);
|
||||
return event;
|
||||
}
|
||||
}
|
||||
@ -13,6 +13,14 @@ public class BarCodeScannerAsyncTask extends android.os.AsyncTask<Void, Void, Re
|
||||
private int mHeight;
|
||||
private BarCodeScannerAsyncTaskDelegate mDelegate;
|
||||
private final MultiFormatReader mMultiFormatReader;
|
||||
private boolean mLimitScanArea;
|
||||
private float mScanAreaX;
|
||||
private float mScanAreaY;
|
||||
private float mScanAreaWidth;
|
||||
private float mScanAreaHeight;
|
||||
private int mCameraViewWidth;
|
||||
private int mCameraViewHeight;
|
||||
private float mRatio;
|
||||
|
||||
// note(sjchmiela): From my short research it's ok to ignore rotation of the image.
|
||||
public BarCodeScannerAsyncTask(
|
||||
@ -20,13 +28,29 @@ public class BarCodeScannerAsyncTask extends android.os.AsyncTask<Void, Void, Re
|
||||
MultiFormatReader multiFormatReader,
|
||||
byte[] imageData,
|
||||
int width,
|
||||
int height
|
||||
int height,
|
||||
boolean limitScanArea,
|
||||
float scanAreaX,
|
||||
float scanAreaY,
|
||||
float scanAreaWidth,
|
||||
float scanAreaHeight,
|
||||
int cameraViewWidth,
|
||||
int cameraViewHeight,
|
||||
float ratio
|
||||
) {
|
||||
mImageData = imageData;
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
mDelegate = delegate;
|
||||
mMultiFormatReader = multiFormatReader;
|
||||
mLimitScanArea = limitScanArea;
|
||||
mScanAreaX = scanAreaX;
|
||||
mScanAreaY = scanAreaY;
|
||||
mScanAreaWidth = scanAreaWidth;
|
||||
mScanAreaHeight = scanAreaHeight;
|
||||
mCameraViewWidth = cameraViewWidth;
|
||||
mCameraViewHeight = cameraViewHeight;
|
||||
mRatio = ratio;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -36,13 +60,29 @@ public class BarCodeScannerAsyncTask extends android.os.AsyncTask<Void, Void, Re
|
||||
}
|
||||
|
||||
Result result = null;
|
||||
/**
|
||||
* mCameraViewWidth and mCameraViewHeight are obtained from portait orientation
|
||||
* mWidth and mHeight are measured with landscape orientation with Home button to the right
|
||||
* adjustedCamViewWidth is the adjusted width from the Aspect ratio setting
|
||||
*/
|
||||
int adjustedCamViewWidth = (int) (mCameraViewHeight / mRatio);
|
||||
float adjustedScanY = (((adjustedCamViewWidth - mCameraViewWidth) / 2) + (mScanAreaY * mCameraViewWidth)) / adjustedCamViewWidth;
|
||||
|
||||
int left = (int) (mScanAreaX * mWidth);
|
||||
int top = (int) (adjustedScanY * mHeight);
|
||||
int scanWidth = (int) (mScanAreaWidth * mWidth);
|
||||
int scanHeight = (int) (((mScanAreaHeight * mCameraViewWidth) / adjustedCamViewWidth) * mHeight);
|
||||
|
||||
try {
|
||||
BinaryBitmap bitmap = generateBitmapFromImageData(
|
||||
mImageData,
|
||||
mWidth,
|
||||
mHeight,
|
||||
false
|
||||
false,
|
||||
left,
|
||||
top,
|
||||
scanWidth,
|
||||
scanHeight
|
||||
);
|
||||
result = mMultiFormatReader.decodeWithState(bitmap);
|
||||
} catch (NotFoundException e) {
|
||||
@ -50,7 +90,11 @@ public class BarCodeScannerAsyncTask extends android.os.AsyncTask<Void, Void, Re
|
||||
rotateImage(mImageData,mWidth, mHeight),
|
||||
mHeight,
|
||||
mWidth,
|
||||
false
|
||||
false,
|
||||
mHeight - scanHeight - top,
|
||||
left,
|
||||
scanHeight,
|
||||
scanWidth
|
||||
);
|
||||
try {
|
||||
result = mMultiFormatReader.decodeWithState(bitmap);
|
||||
@ -59,7 +103,11 @@ public class BarCodeScannerAsyncTask extends android.os.AsyncTask<Void, Void, Re
|
||||
mImageData,
|
||||
mWidth,
|
||||
mHeight,
|
||||
true
|
||||
true,
|
||||
mWidth - scanWidth - left,
|
||||
mHeight - scanHeight - top,
|
||||
scanWidth,
|
||||
scanHeight
|
||||
);
|
||||
try {
|
||||
result = mMultiFormatReader.decodeWithState(invertedBitmap);
|
||||
@ -68,7 +116,11 @@ public class BarCodeScannerAsyncTask extends android.os.AsyncTask<Void, Void, Re
|
||||
rotateImage(mImageData,mWidth, mHeight),
|
||||
mHeight,
|
||||
mWidth,
|
||||
true
|
||||
true,
|
||||
top,
|
||||
mWidth - scanWidth - left,
|
||||
scanHeight,
|
||||
scanWidth
|
||||
);
|
||||
try {
|
||||
result = mMultiFormatReader.decodeWithState(invertedRotatedBitmap);
|
||||
@ -96,13 +148,26 @@ public class BarCodeScannerAsyncTask extends android.os.AsyncTask<Void, Void, Re
|
||||
protected void onPostExecute(Result result) {
|
||||
super.onPostExecute(result);
|
||||
if (result != null) {
|
||||
mDelegate.onBarCodeRead(result, mWidth, mHeight);
|
||||
mDelegate.onBarCodeRead(result, mWidth, mHeight, mImageData);
|
||||
}
|
||||
mDelegate.onBarCodeScanningTaskCompleted();
|
||||
}
|
||||
|
||||
private BinaryBitmap generateBitmapFromImageData(byte[] imageData, int width, int height, boolean inverse) {
|
||||
PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(
|
||||
private BinaryBitmap generateBitmapFromImageData(byte[] imageData, int width, int height, boolean inverse, int left, int top, int sWidth, int sHeight) {
|
||||
PlanarYUVLuminanceSource source;
|
||||
if (mLimitScanArea) {
|
||||
source = new PlanarYUVLuminanceSource(
|
||||
imageData, // byte[] yuvData
|
||||
width, // int dataWidth
|
||||
height, // int dataHeight
|
||||
left, // int left
|
||||
top, // int top
|
||||
sWidth, // int width
|
||||
sHeight, // int height
|
||||
false // boolean reverseHorizontal
|
||||
);
|
||||
} else {
|
||||
source = new PlanarYUVLuminanceSource(
|
||||
imageData, // byte[] yuvData
|
||||
width, // int dataWidth
|
||||
height, // int dataHeight
|
||||
@ -111,7 +176,8 @@ public class BarCodeScannerAsyncTask extends android.os.AsyncTask<Void, Void, Re
|
||||
width, // int width
|
||||
height, // int height
|
||||
false // boolean reverseHorizontal
|
||||
);
|
||||
);
|
||||
}
|
||||
if (inverse) {
|
||||
return new BinaryBitmap(new HybridBinarizer(source.invert()));
|
||||
} else {
|
||||
|
||||
@ -3,6 +3,6 @@ package org.reactnative.camera.tasks;
|
||||
import com.google.zxing.Result;
|
||||
|
||||
public interface BarCodeScannerAsyncTaskDelegate {
|
||||
void onBarCodeRead(Result barCode, int width, int height);
|
||||
void onBarCodeRead(Result barCode, int width, int height, byte[] imageData);
|
||||
void onBarCodeScanningTaskCompleted();
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ import org.reactnative.barcodedetector.RNBarcodeDetector;
|
||||
|
||||
public interface BarcodeDetectorAsyncTaskDelegate {
|
||||
|
||||
void onBarcodesDetected(WritableArray barcodes);
|
||||
void onBarcodesDetected(WritableArray barcodes, int width, int height, byte[] imageData);
|
||||
|
||||
void onBarcodeDetectionError(RNBarcodeDetector barcodeDetector);
|
||||
|
||||
|
||||
@ -180,8 +180,10 @@ public class ResolveTakenPictureAsyncTask extends AsyncTask<Void, Void, Writable
|
||||
if (!mOptions.hasKey("doNotSave") || !mOptions.getBoolean("doNotSave")) {
|
||||
|
||||
// Prepare file output
|
||||
File imageFile = new File(RNFileUtils.getOutputFilePath(mCacheDirectory, ".jpg"));
|
||||
File imageFile = new File(getImagePath());
|
||||
|
||||
imageFile.createNewFile();
|
||||
|
||||
FileOutputStream fOut = new FileOutputStream(imageFile);
|
||||
|
||||
// Save byte array (it is already a JPEG)
|
||||
@ -311,13 +313,20 @@ public class ResolveTakenPictureAsyncTask extends AsyncTask<Void, Void, Writable
|
||||
return rotationDegrees;
|
||||
}
|
||||
|
||||
private String getImagePath() throws IOException{
|
||||
if(mOptions.hasKey("path")){
|
||||
return mOptions.getString("path");
|
||||
}
|
||||
return RNFileUtils.getOutputFilePath(mCacheDirectory, ".jpg");
|
||||
}
|
||||
|
||||
private String writeStreamToFile(ByteArrayOutputStream inputStream) throws IOException {
|
||||
String outputPath = null;
|
||||
IOException exception = null;
|
||||
FileOutputStream outputStream = null;
|
||||
|
||||
try {
|
||||
outputPath = RNFileUtils.getOutputFilePath(mCacheDirectory, ".jpg");
|
||||
outputPath = getImagePath();
|
||||
outputStream = new FileOutputStream(outputPath);
|
||||
inputStream.writeTo(outputStream);
|
||||
} catch (IOException e) {
|
||||
|
||||
@ -82,7 +82,7 @@ public class BarcodeDetectorAsyncTask extends android.os.AsyncTask<Void, Void, V
|
||||
@Override
|
||||
public void onSuccess(List<FirebaseVisionBarcode> barcodes) {
|
||||
WritableArray serializedBarcodes = serializeEventData(barcodes);
|
||||
mDelegate.onBarcodesDetected(serializedBarcodes);
|
||||
mDelegate.onBarcodesDetected(serializedBarcodes, mWidth, mHeight, mImageData);
|
||||
mDelegate.onBarcodeDetectingTaskCompleted();
|
||||
}
|
||||
})
|
||||
|
||||
156
docs/API.md
156
docs/API.md
@ -2,19 +2,134 @@
|
||||
id: api
|
||||
title: Work in progress
|
||||
---
|
||||
## API props
|
||||
|
||||
## Props Index
|
||||
|
||||
[**wip**]
|
||||
|
||||
- [`zoom`](API.md#zoom)
|
||||
- [`maxZoom`](API.md#maxzoom)
|
||||
- [`type`](API.md#type)
|
||||
- [`cameraId`](API.md#cameraid)
|
||||
- [`flashMode`](API.md#flashmode)
|
||||
- [`exposure`](API.md#exposure)
|
||||
- [`whiteBalance`](API.md#whiteBalance)
|
||||
- [`autoFocus`](API.md#autoFocus)
|
||||
- [`ratio`](API.md#ratio)
|
||||
- [`pictureSize`](API.md#pictureSize)
|
||||
- [`focusDepth`](API.md#focusDepth)
|
||||
- [`onMountError`](API.md#onMountError)
|
||||
- [`onCameraReady`](API.md#onCameraReady)
|
||||
|
||||
## Methods Index
|
||||
|
||||
- [`takePictureAsync`](API.md#takepictureasync)
|
||||
- [`recordAsync`](API.md#recordasync)
|
||||
- [`refreshAuthorizationStatus`](API.md#refreshauthorizationstatus)
|
||||
- [`stopRecording`](API.md#stoprecording)
|
||||
- [`pausePreview`](API.md#pausepreview)
|
||||
- [`resumePreview`](API.md#resumepreview)
|
||||
- [`getAvailablePictureSizes`](API.md#getavailablepicturesizes)
|
||||
- [`getSupportedRatiosAsync`](API.md#getsupportedratiosasync-android-only)
|
||||
- [`isRecording`](API.md#isrecording-ios-only)
|
||||
- [`getSupportedPreviewFpsRange`](API.md#getsupportedpreviewfpsrange-android-only)
|
||||
|
||||
## Props
|
||||
|
||||
---
|
||||
|
||||
### `zoom`
|
||||
|
||||
This property specifies the zoom value of the camera. Ranges from 0 to 1. Default to 0.
|
||||
|
||||
| Type | Default Value |
|
||||
| ------ | ------------- |
|
||||
| number | 0 |
|
||||
|
||||
---
|
||||
|
||||
### `maxZoom`
|
||||
|
||||
The maximum zoom value of the camera. Defaults to 0.
|
||||
|
||||
| Type | Default Value |
|
||||
| ------ | ------------- |
|
||||
| number | 0 |
|
||||
|
||||
---
|
||||
|
||||
### `type`
|
||||
|
||||
This property defines which camera on the phone the component is using.
|
||||
Possible values:
|
||||
|
||||
- `front`
|
||||
- `back`
|
||||
|
||||
| Type | Default Value |
|
||||
| ------ | ------------- |
|
||||
| number | 'back' |
|
||||
|
||||
---
|
||||
|
||||
### `cameraId`
|
||||
|
||||
For selecting from multiple cameras on Android devices. See [2492](https://github.com/react-native-community/react-native-camera/pull/2492) for more info. Can be retrieved with `getCameraIds()`
|
||||
|
||||
| Type | Default Value | Platform |
|
||||
| ------ | ------------- | -------- |
|
||||
| String | `null` | Android |
|
||||
|
||||
---
|
||||
|
||||
### `flashMode`
|
||||
|
||||
Determines the state of the camera flash. Has the following possible states.
|
||||
|
||||
```off: '1',
|
||||
on: 'auto',
|
||||
auto: 'torch',
|
||||
torch: 'off'
|
||||
```
|
||||
|
||||
| Type | Default Value |
|
||||
| ------ | ------------- |
|
||||
| object | `{ off: 1 }` |
|
||||
|
||||
### `ratio`
|
||||
|
||||
A string representing the camera ratio in the format 'height:width'. Default is `"4:3"`.
|
||||
|
||||
Use `getSupportedRatiosAsync` method to get ratio strings supported by your camera on Android.
|
||||
|
||||
| Type | Default Value |
|
||||
| ------ | ------------- |
|
||||
| string | `4:3` |
|
||||
|
||||
### `pictureSize`
|
||||
|
||||
This prop has a different behaviour for Android and iOS and should rarely be set.
|
||||
|
||||
For Android, this prop attempts to control the camera sensor capture resolution, similar to how `ratio` behaves. This is useful for cases where a low resolution image is required, and makes further resizing less intensive on the device's memory. The list of possible values can be requested with `getAvailablePictureSizes`, and the value should be set in the format of `<width>x<height>`. Internally, the native code will attempt to get the best suited resolution for the given `pictureSize` value if the provided value is invalid, and will default to the highest resolution available.
|
||||
|
||||
For iOS, this prop controls the internal camera preset value and should rarely be changed. However, this value can be set to setup the sensor to match the video recording's quality in order to prevent flickering. The list of valid values can be gathered from https://developer.apple.com/documentation/avfoundation/avcapturesessionpreset and can also be requested with `getAvailablePictureSizes`.
|
||||
|
||||
| Type | Default Value |
|
||||
| ------ | ------------- |
|
||||
| string | `None` |
|
||||
|
||||
## Methods
|
||||
|
||||
## takePictureAsync()
|
||||
|
||||
Returns a promise with TakePictureResponse.
|
||||
|
||||
### Method type
|
||||
|
||||
```ts
|
||||
takePictureAsync(options?: TakePictureOptions): Promise<TakePictureResponse>;
|
||||
```
|
||||
|
||||
```ts
|
||||
interface TakePictureOptions {
|
||||
quality?: number;
|
||||
@ -55,6 +170,7 @@ takePicture = async () => {
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## recordAsync()
|
||||
@ -66,6 +182,7 @@ Returns a promise with RecordResponse.
|
||||
```ts
|
||||
recordAsync(options?: RecordOptions): Promise<RecordResponse>;
|
||||
```
|
||||
|
||||
```ts
|
||||
interface RecordOptions {
|
||||
quality?: keyof VideoQuality;
|
||||
@ -76,6 +193,7 @@ interface RecordOptions {
|
||||
mirrorVideo?: boolean;
|
||||
path?: string;
|
||||
videoBitrate?: number;
|
||||
fps?: number;
|
||||
|
||||
/** iOS only */
|
||||
codec?: keyof VideoCodec | VideoCodec[keyof VideoCodec];
|
||||
@ -90,7 +208,6 @@ interface RecordResponse {
|
||||
/** iOS only */
|
||||
codec: VideoCodec[keyof VideoCodec];
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Usage example
|
||||
@ -116,7 +233,6 @@ takeVideo = async () => {
|
||||
|
||||
---
|
||||
|
||||
|
||||
## refreshAuthorizationStatus()
|
||||
|
||||
Allows to make RNCamera check Permissions again and set status accordingly.
|
||||
@ -159,7 +275,6 @@ stopRecording(): void;
|
||||
|
||||
---
|
||||
|
||||
|
||||
## pausePreview()
|
||||
|
||||
Pauses the preview. The preview can be resumed again by using resumePreview().
|
||||
@ -180,7 +295,6 @@ pausePreview(): void;
|
||||
|
||||
---
|
||||
|
||||
|
||||
## resumePreview()
|
||||
|
||||
Resumes the preview after pausePreview() has been called.
|
||||
@ -222,7 +336,6 @@ getAvailablePictureSizes(): Promise<string[]>;
|
||||
|
||||
---
|
||||
|
||||
|
||||
## getSupportedRatiosAsync() - Android only
|
||||
|
||||
Android only. Returns a promise. The promise will be fulfilled with an object with an array containing strings with all camera aspect ratios supported by the device.
|
||||
@ -243,12 +356,12 @@ getSupportedRatiosAsync(): Promise<string[]>;
|
||||
|
||||
---
|
||||
|
||||
|
||||
## isRecording() - iOS only
|
||||
|
||||
iOS only. Returns a promise. The promise will be fulfilled with a boolean indicating if currently recording is started or stopped.
|
||||
|
||||
### Method type
|
||||
|
||||
```ts
|
||||
isRecording(): Promise<boolean>;
|
||||
|
||||
@ -263,4 +376,33 @@ const isRecording = await isRecording();
|
||||
} */
|
||||
```
|
||||
|
||||
- [`getSupportedPreviewFpsRange`](API.md#getSupportedPreviewFpsRange`)
|
||||
|
||||
## getSupportedPreviewFpsRange - Android only
|
||||
|
||||
Android only. Returns a promise. The promise will be fulfilled with a json object including the fps ranges available for those devices ([android docs](<https://developer.android.com/reference/android/hardware/Camera.Parameters#getSupportedPreviewFpsRange()>))
|
||||
|
||||
### Method type
|
||||
|
||||
```ts
|
||||
getSupportedPreviewFpsRange(): Promise<[{MINIMUM_FPS: string, MAXIMUM_FPS: string}]>;
|
||||
|
||||
```
|
||||
|
||||
### Usage example
|
||||
|
||||
```js
|
||||
const previewRange = await this.camera.getSupportedPreviewFpsRange();
|
||||
/* -> [
|
||||
{
|
||||
MINIMUM_FPS: "15000",
|
||||
MAXIMUM_FPS: "15000"
|
||||
},
|
||||
{
|
||||
MINIMUM_FPS: "20000",
|
||||
MAXIMUM_FPS: "20000"
|
||||
}
|
||||
] */
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@ -83,12 +83,12 @@ dependencies {
|
||||
## How can I resize captured images?
|
||||
|
||||
Currently, `RNCamera` does not allow for specifying the desired resolution of the captured image, nor does it natively expose any functionality to resize images.
|
||||
One way to achieve this (without any additional dependencies )is using [react-native.ImageEditor.cropImage](https://facebook.github.io/react-native/docs/imageeditor.html#cropimage).
|
||||
One way to achieve this (without any additional dependencies ) is using [react-native.ImageEditor.cropImage](https://facebook.github.io/react-native/docs/imageeditor.html#cropimage).
|
||||
|
||||
The strategy is:
|
||||
|
||||
1. Capture an image using `RNCamera`, which uses the device's max resolution.
|
||||
2. Use `react-native.ImageEditor.cropImage()` to crop the image using the image's native size as the crop size (thus maintaiing the original image), and the desired new size as the `displaySize` attribute (thus resizing the image).
|
||||
2. Use `react-native.ImageEditor.cropImage()` to crop the image using the image's native size as the crop size (thus maintaining the original image), and the desired new size as the `displaySize` attribute (thus resizing the image).
|
||||
|
||||
```javascript
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
@ -51,7 +51,7 @@ class ExampleApp extends PureComponent {
|
||||
);
|
||||
}
|
||||
|
||||
takePicture = async() => {
|
||||
takePicture = async () => {
|
||||
if (this.camera) {
|
||||
const options = { quality: 0.5, base64: true };
|
||||
const data = await this.camera.takePictureAsync(options);
|
||||
@ -208,7 +208,7 @@ Use the `autoFocus` property to specify the auto focus setting of your camera. `
|
||||
Values: Object `{ x: 0.5, y: 0.5 }`.
|
||||
Values (iOS): Object `{ x: 0.5, y: 0.5, autoExposure }`.
|
||||
|
||||
Setting this property causes the auto focus feature of the camera to attempt to focus on the part of the image at this coordiate.
|
||||
Setting this property causes the auto focus feature of the camera to attempt to focus on the part of the image at this coordinate.
|
||||
|
||||
Coordinates values are measured as floats from `0` to `1.0`. `{ x: 0, y: 0 }` will focus on the top left of the image, `{ x: 1, y: 1 }` will be the bottom right. Values are based on landscape mode with the home button on the right—this applies even if the device is in portrait mode.
|
||||
|
||||
@ -217,8 +217,8 @@ On iOS, focusing will not change the exposure automatically unless autoExposure
|
||||
Hint:
|
||||
for portrait orientation, apply 90° clockwise rotation + translation: [Example](https://gist.github.com/Craigtut/6632a9ac7cfff55e74fb561862bc4edb)
|
||||
|
||||
|
||||
### iOS `onSubjectAreaChanged`
|
||||
|
||||
iOS only.
|
||||
|
||||
if autoFocusPointOfInterest is set, this event will be fired when a substancial change is detected with the following object: `{ nativeEvent: { prevPoint: { x: number; y: number; } } }`
|
||||
@ -286,6 +286,24 @@ The idea is that you select the appropriate white balance setting for the type o
|
||||
|
||||
Use the `whiteBalance` property to specify which white balance setting the camera should use.
|
||||
|
||||
On iOS it's also possible to specify custom temperature, tint and rgb offset values instead of using one of the presets (auto, sunny, ...):
|
||||
|
||||
Value: Object (e.g. `{temperature: 4000, tint: -10.0, redGainOffset: 0.0, greenGainOffset: 0.0, blueGainOffset: 0.0}`)
|
||||
|
||||
The rgb offset values are applied to the calculated device specific white balance gains for the given temperature and tint values.
|
||||
|
||||
### `exposure`
|
||||
|
||||
Value: float from `0` to `1.0`, or `-1` (default) for auto.
|
||||
|
||||
Sets the camera's exposure value.
|
||||
|
||||
### `useNativeZoom`
|
||||
|
||||
Boolean to turn on native pinch to zoom. Works with the `maxZoom` property on iOS.
|
||||
|
||||
Warning: The Android Touch-Event-System causes childviews to catch the touch events. Therefore avoid using screenfilling touchables inside of the cameraview.
|
||||
|
||||
### `zoom`
|
||||
|
||||
Value: float from `0` to `1.0`
|
||||
@ -324,10 +342,20 @@ By default a `Camera not authorized` message will be displayed when access to th
|
||||
|
||||
By default a <ActivityIndicator> will be displayed while the component is waiting for the user to grant/deny access to the camera, if set displays the passed react element instead of the default one.
|
||||
|
||||
#### `iOS` `rectOfInterest`
|
||||
#### `rectOfInterest`
|
||||
|
||||
An `{x: , y:, width:, height: }` object which defines the rect of interst as normalized coordinates from `(0,0)` top left corner to `(1,1)` bottom right corner.
|
||||
|
||||
Note: Must also provide cameraViewDimensions prop for Android device
|
||||
|
||||
### `Android` `cameraViewDimensions`
|
||||
|
||||
An `{width:, height: }` object which defines the width and height of the cameraView. This prop is used to adjust the effect of Aspect Raio for rectOfInterest area on Android
|
||||
|
||||
### `Android` `playSoundOnCapture`
|
||||
|
||||
Boolean to turn on or off the camera's shutter sound (default false). Note that in some countries, the shutter sound cannot be turned off.
|
||||
|
||||
### `iOS` `videoStabilizationMode`
|
||||
|
||||
The video stabilization mode used for a video recording. The possible values are:
|
||||
@ -365,6 +393,14 @@ This option specifies the quality of the video to be taken. The possible values
|
||||
If nothing is passed the device's highest camera quality will be used as default.
|
||||
Note: This solve the flicker video recording issue for iOS
|
||||
|
||||
### `pictureSize`
|
||||
|
||||
This prop has a different behaviour for Android and iOS and should rarely be set.
|
||||
|
||||
For Android, this prop attempts to control the camera sensor capture resolution, similar to how `ratio` behaves. This is useful for cases where a low resolution image is required, and makes further resizing less intensive on the device's memory. The list of possible values can be requested with `getAvailablePictureSizes`, and the value should be set in the format of `<width>x<height>`. Internally, the native code will attempt to get the best suited resolution for the given `pictureSize` value if the provided value is invalid, and will default to the highest resolution available.
|
||||
|
||||
For iOS, this prop controls the internal camera preset value and should rarely be changed. However, this value can be set to setup the sensor to match the video recording's quality in order to prevent flickering. The list of valid values can be gathered from https://developer.apple.com/documentation/avfoundation/avcapturesessionpreset and can also be requested with `getAvailablePictureSizes`.
|
||||
|
||||
### Native Event callbacks props
|
||||
|
||||
### `onCameraReady`
|
||||
@ -392,11 +428,40 @@ iOS only. Function to be called when the camera audio session is interrupted or
|
||||
|
||||
iOS only. Function to be called when the camera audio session is connected. This will be fired the first time the camera is mounted with `captureAudio={true}`, and any time the audio device connection is established. Note that this event might not always fire after an interruption due to iOS' behavior. For example, if the audio was already interrupted before the camera was mounted, this event will only fire once a recording is attempted.
|
||||
|
||||
|
||||
### `onPictureTaken`
|
||||
|
||||
Function to be called when native code emit onPictureTaken event, when camera has taken a picture, but before all extra processing happens. This can be useful to allow the UI to take other pictures while the processing of the current picture is still taking place.
|
||||
|
||||
### `onRecordingStart`
|
||||
|
||||
Function to be called when native code actually starts video recording. Note that video recording might take a few miliseconds to setup depending on the camera settings and hardware features. Use this event to detect when video is actually being recorded.
|
||||
Event will contain the following fields:
|
||||
|
||||
- `uri` - Video file URI, as returned by `recordAsync`
|
||||
- `videoOrientation` - Video orientation, as returned by `recordAsync`
|
||||
- `deviceOrientation` - Video orientation, as returned by `recordAsync`
|
||||
|
||||
### `onRecordingEnd`
|
||||
|
||||
Function to be called when native code stops recording video, but before all video processing takes place. This event will only fire after a successful video recording, and it will not fire if video recording fails (use the error returned from `recordAsync` instead).
|
||||
|
||||
### `onTap`
|
||||
|
||||
Function to be called when a touch within the camera view is recognized.
|
||||
The function is also called on the first touch of double tap.
|
||||
Event will contain the following fields:
|
||||
|
||||
- `x`
|
||||
- `y`
|
||||
|
||||
### `onDoubleTap`
|
||||
|
||||
Function to be called when a double touch within the camera view is recognized.
|
||||
Event will contain the following fields:
|
||||
|
||||
- `x`
|
||||
- `y`
|
||||
|
||||
### Bar Code Related props
|
||||
|
||||
### `onBarCodeRead`
|
||||
@ -407,6 +472,7 @@ Event contains the following fields
|
||||
|
||||
- `data` - a textual representation of the barcode, if available
|
||||
- `rawData` - The raw data encoded in the barcode, if available
|
||||
- `uri`: (iOS only) string representing the path to the image saved on your app's cache directory. Applicable only for `onGoogleVisionBarcodesDetected`.
|
||||
- `type` - the type of the barcode detected
|
||||
- `bounds` -
|
||||
|
||||
@ -472,6 +538,7 @@ Like `onBarCodeRead`, but using Firebase MLKit to scan barcodes. More info can b
|
||||
Like `barCodeTypes`, but applies to the Firebase MLKit barcode detector.
|
||||
Example: `<RNCamera googleVisionBarcodeType={RNCamera.Constants.GoogleVisionBarcodeDetection.BarcodeType.DATA_MATRIX} />`
|
||||
Available settings:
|
||||
|
||||
- CODE_128
|
||||
- CODE_39
|
||||
- CODE_93
|
||||
@ -487,11 +554,18 @@ Available settings:
|
||||
- DATA_MATRIX
|
||||
- ALL
|
||||
|
||||
### `Android` `googleVisionBarcodeMode`
|
||||
### `googleVisionBarcodeMode`
|
||||
|
||||
Change the mode in order to scan "inverted" barcodes. You can either change it to `alternate`, which will inverted the image data every second screen and be able to read both normal and inverted barcodes, or `inverted`, which will only read inverted barcodes. Default is `normal`, which only reads "normal" barcodes. Note: this property only applies to the Google Vision barcode detector.
|
||||
Example: `<RNCamera googleVisionBarcodeMode={RNCamera.Constants.GoogleVisionBarcodeDetection.BarcodeMode.ALTERNATE} />`
|
||||
|
||||
### `detectedImageInEvent`
|
||||
|
||||
When `detectedImageInEvent` is `false` (default), `onBarCodeRead` / `onBarcodesDetected` only gives metadata, but not the image (bytes/base64) itself.
|
||||
|
||||
When `detectedImageInEvent` is `true`, `onBarCodeRead` / `onGoogleVisionBarcodesDetected` will fill the `image` field inside the object given to the callback handler.
|
||||
It contains raw image bytes in JPEG format (quality 100) as Base64-encoded string.
|
||||
|
||||
### Face Detection Related props
|
||||
|
||||
RNCamera uses the Firebase MLKit for Face Detection, you can read more about it [here](https://firebase.google.com/docs/ml-kit/detect-faces).
|
||||
@ -549,12 +623,13 @@ Supported options:
|
||||
- `mirrorImage` (boolean true or false). Use this with `true` if you want the resulting rendered picture to be mirrored (inverted in the vertical axis). If no value is specified `mirrorImage:false` is used.
|
||||
|
||||
- `writeExif`: (boolean or object, defaults to true). Setting this to a boolean indicates if the image exif should be preserved after capture, or removed. Setting it to an object, merges any data with the final exif output. This is useful, for example, to add GPS metadata (note that GPS info is correctly transalted from double values to the EXIF format, so there's no need to read the EXIF protocol).
|
||||
|
||||
```js
|
||||
writeExif = {
|
||||
"GPSLatitude": latitude,
|
||||
"GPSLongitude": longitude,
|
||||
"GPSAltitude": altitude
|
||||
}
|
||||
GPSLatitude: latitude,
|
||||
GPSLongitude: longitude,
|
||||
GPSAltitude: altitude,
|
||||
};
|
||||
```
|
||||
|
||||
- `exif` (boolean true or false) Use this with `true` if you want a exif data map of the picture taken on the return data of your promise. If no value is specified `exif:false` is used.
|
||||
@ -569,6 +644,8 @@ writeExif = {
|
||||
|
||||
- `orientation` (string or number). Specifies the orientation that us used for taking the picture. Possible values: `"portrait"`, `"portraitUpsideDown"`, `"landscapeLeft"` or `"landscapeRight"`.
|
||||
|
||||
- `path` (file path on disk). Specifies the path on disk to save picture to.
|
||||
|
||||
The promise will be fulfilled with an object with some of the following properties:
|
||||
|
||||
- `width`: returns the image's width (taking image orientation into account)
|
||||
@ -701,6 +778,7 @@ Read more about [react-native-barcode-mask](https://github.com/shahnawaz/react-n
|
||||
### @nartc/react-native-barcode-mask
|
||||
|
||||
A rewritten version of `react-native-barcode-mask` using `Hooks` and `Reanimated`. If you're already using `react-native-reanimated` (`react-navigation` dependency) then you might benefit from this rewritten component.
|
||||
|
||||
- Customizable
|
||||
- Provide custom hook to "scan barcode within finder area"
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@ Passing `null` to `onFaceDetected`, `onGoogleVisionBarcodesDetected`, `onTextRec
|
||||
|
||||
## Events continue if screen is mounted but not on top of stack
|
||||
|
||||
Lets say you use Face Detection, you take a picture and then takes the user to another screen to see that picture. Meanwhile, RNCamera is still mounted on the previous screen. `onFaceDetected` will still be called if you do not prevent it. For example (using [`react-navigation`](https://github.com/react-navigation/react-navigation):
|
||||
Lets say you use Face Detection, you take a picture and then takes the user to another screen to see that picture. Meanwhile, RNCamera is still mounted on the previous screen. `onFaceDetected` will still be called if you do not prevent it. For example (using [`react-navigation`](https://github.com/react-navigation/react-navigation)):
|
||||
|
||||
```
|
||||
const takePictureAndShow = () => {
|
||||
@ -47,7 +47,7 @@ const { navigation } = this.props;
|
||||
|
||||
## Sending the image to a server
|
||||
|
||||
A good way is to get the base64 string representation of your image. You can get it from RNCamera by passing the `base64: true` option lto `takePictureAsync` like:
|
||||
A good way is to get the base64 string representation of your image. You can get it from RNCamera by passing the `base64: true` option to `takePictureAsync` like:
|
||||
|
||||
```
|
||||
if (this.camera) {
|
||||
@ -85,7 +85,7 @@ Use this package https://github.com/phuochau/react-native-thumbnail
|
||||
|
||||
Because of different project requirements there is no gesture zoom (like pinch zoom or slide-up zoom) implemented in this package. All implementation should be done in user-land.
|
||||
|
||||
However we have some recipies for common zoom behaviours. If you implemented your own solution feel free to add it to the list!
|
||||
However we have some recipes for common zoom behaviours. If you implemented your own solution feel free to add it to the list!
|
||||
|
||||
## SlideUp Zoom
|
||||
|
||||
|
||||
@ -1,440 +1,468 @@
|
||||
---
|
||||
id: installation
|
||||
title: Installation
|
||||
---
|
||||
|
||||
This document is split into two main sections:
|
||||
1. Required installation steps for basic usage of `react-native-camera`
|
||||
2. Additional installation steps for usage of Face Detection/Text Recognition/BarCode with [MLKit](https://developers.google.com/ml-kit)
|
||||
|
||||
# Required installation steps
|
||||
|
||||
_These steps assume installation for iOS/Android. To install it with Windows, see manual install [below](#windows)_
|
||||
|
||||
## Mostly automatic install with autolinking (RN > 0.60)
|
||||
|
||||
1. `npm install react-native-camera --save`
|
||||
2. Run `cd ios && pod install && cd ..`
|
||||
|
||||
## Mostly automatic install with react-native link (RN < 0.60)
|
||||
|
||||
1. `npm install react-native-camera --save`
|
||||
2. `react-native link react-native-camera`
|
||||
|
||||
## Manual install - iOS (not recommended)
|
||||
|
||||
1. `npm install react-native-camera --save`
|
||||
2. In XCode, in the project navigator, right click `Libraries` ➜ `Add Files to [your project's name]`
|
||||
3. Go to `node_modules` ➜ `react-native-camera` and add `RNCamera.xcodeproj`
|
||||
4. Expand the `RNCamera.xcodeproj` ➜ `Products` folder
|
||||
5. In XCode, in the project navigator, select your project. Add `libRNCamera.a` to your project's `Build Phases` ➜ `Link Binary With Libraries`
|
||||
6. Click `RNCamera.xcodeproj` in the project navigator and go the `Build Settings` tab. Make sure 'All' is toggled on (instead of 'Basic'). In the `Search Paths` section, look for `Header Search Paths` and make sure it contains both `$(SRCROOT)/../../react-native/React` and `$(SRCROOT)/../../../React` - mark both as `recursive`.
|
||||
|
||||
## Manual install - Android (not recommended)
|
||||
|
||||
1. `npm install react-native-camera --save`
|
||||
2. Open up `android/app/src/main/java/[...]/MainApplication.java`
|
||||
|
||||
- Add `import org.reactnative.camera.RNCameraPackage;` to the imports at the top of the file
|
||||
- Add `new RNCameraPackage()` to the list returned by the `getPackages()` method. Add a comma to the previous item if there's already something there.
|
||||
|
||||
3. Append the following lines to `android/settings.gradle`:
|
||||
|
||||
```gradle
|
||||
include ':react-native-camera'
|
||||
project(':react-native-camera').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-camera/android')
|
||||
```
|
||||
|
||||
4. Insert the following lines in `android/app/build.gradle` inside the dependencies block:
|
||||
|
||||
```gradle
|
||||
implementation project(':react-native-camera')
|
||||
```
|
||||
|
||||
## iOS - other required steps
|
||||
|
||||
Add permissions with usage descriptions to your app `Info.plist`:
|
||||
|
||||
```xml
|
||||
<!-- Required with iOS 10 and higher -->
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Your message to user when the camera is accessed for the first time</string>
|
||||
|
||||
<!-- Required with iOS 11 and higher: include this only if you are planning to use the camera roll -->
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>Your message to user when the photo library is accessed for the first time</string>
|
||||
|
||||
<!-- Include this only if you are planning to use the camera roll -->
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Your message to user when the photo library is accessed for the first time</string>
|
||||
|
||||
<!-- Include this only if you are planning to use the microphone for video recording -->
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Your message to user when the microphone is accessed for the first time</string>
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Additional information in case of problems</summary>
|
||||
|
||||
You might need to adjust your Podfile following the example below:
|
||||
|
||||
```ruby
|
||||
target 'yourTargetName' do
|
||||
# See http://facebook.github.io/react-native/docs/integration-with-existing-apps.html#configuring-cocoapods-dependencies
|
||||
pod 'React', :path => '../node_modules/react-native', :subspecs => [
|
||||
'Core',
|
||||
'CxxBridge', # Include this for RN >= 0.47
|
||||
'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43
|
||||
'RCTText',
|
||||
'RCTNetwork',
|
||||
'RCTWebSocket', # Needed for debugging
|
||||
'RCTAnimation', # Needed for FlatList and animations running on native UI thread
|
||||
# Add any other subspecs you want to use in your project
|
||||
]
|
||||
|
||||
# Explicitly include Yoga if you are using RN >= 0.42.0
|
||||
pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
|
||||
|
||||
# Third party deps podspec link
|
||||
pod 'react-native-camera', path: '../node_modules/react-native-camera'
|
||||
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
if target.name == "React"
|
||||
target.remove_from_project
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
</details>
|
||||
|
||||
## Android - other required steps
|
||||
|
||||
Add permissions to your app `android/app/src/main/AndroidManifest.xml` file:
|
||||
|
||||
```xml
|
||||
<!-- Required -->
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
<!-- Include this only if you are planning to use the camera roll -->
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<!-- Include this only if you are planning to use the microphone for video recording -->
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
```
|
||||
|
||||
Insert the following lines in `android/app/build.gradle`:
|
||||
|
||||
```gradle
|
||||
android {
|
||||
...
|
||||
defaultConfig {
|
||||
...
|
||||
missingDimensionStrategy 'react-native-camera', 'general' // <--- insert this line
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Additional information in case of problems</summary>
|
||||
|
||||
1. Make sure you use `JDK >= 1.7` and your `buildToolsVersion >= 25.0.2`
|
||||
|
||||
2. Make sure you have jitpack added in `android/build.gradle`
|
||||
|
||||
```gradle
|
||||
allprojects {
|
||||
repositories {
|
||||
maven { url "https://www.jitpack.io" }
|
||||
maven { url "https://maven.google.com" }
|
||||
}
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
# Additional installation steps
|
||||
|
||||
Follow these optional steps if you want to use Face Detection/Text Recognition/BarCode with [MLKit](https://developers.google.com/ml-kit).
|
||||
You will need to set-up Firebase project for your app (detailed steps below).
|
||||
|
||||
_Note:_ Installing [react-native-firebase](https://github.com/invertase/react-native-firebase) package is NOT necessary.
|
||||
|
||||
## iOS
|
||||
|
||||
If you want any of these optional features, you will need to use CocoaPods.
|
||||
|
||||
### Modifying Podfile
|
||||
|
||||
Add dependency towards `react-native-camera` in your `Podfile` with `subspecs` using one of the following:
|
||||
|
||||
* For Face Detection:
|
||||
|
||||
```ruby
|
||||
pod 'react-native-camera', path: '../node_modules/react-native-camera', subspecs: [
|
||||
'FaceDetectorMLKit'
|
||||
]
|
||||
```
|
||||
|
||||
* For Text Recognition:
|
||||
|
||||
```ruby
|
||||
pod 'react-native-camera', path: '../node_modules/react-native-camera', subspecs: [
|
||||
'TextDetector'
|
||||
]
|
||||
```
|
||||
|
||||
* For BarCode Recognition:
|
||||
|
||||
```ruby
|
||||
pod 'react-native-camera', path: '../node_modules/react-native-camera', subspecs: [
|
||||
'BarcodeDetectorMLKit'
|
||||
]
|
||||
```
|
||||
|
||||
* For all possible detections:
|
||||
|
||||
```ruby
|
||||
pod 'react-native-camera', path: '../node_modules/react-native-camera', subspecs: [
|
||||
'TextDetector',
|
||||
'FaceDetectorMLKit',
|
||||
'BarcodeDetectorMLKit'
|
||||
]
|
||||
```
|
||||
|
||||
Then run `cd ios && pod install && cd ..`
|
||||
|
||||
### Setting up Firebase
|
||||
|
||||
Text/Face recognition for iOS uses Firebase MLKit which requires setting up Firebase project for your app.
|
||||
If you have not already added Firebase to your app, please follow the steps described in [getting started guide](https://firebase.google.com/docs/ios/setup).
|
||||
In short, you would need to
|
||||
|
||||
1. Register your app in Firebase console.
|
||||
2. Download `GoogleService-Info.plist` and add it to your project
|
||||
3. Add `pod 'Firebase/Core'` to your podfile
|
||||
4. In your `AppDelegate.m` file add the following lines:
|
||||
|
||||
```objective-c
|
||||
#import <Firebase.h> // <--- add this
|
||||
...
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
[FIRApp configure]; // <--- add this
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Additional information in case of problems</summary>
|
||||
|
||||
- If you have issues with duplicate symbols you will need to enable dead code stripping option in your Xcode (Target > Build Settings > search for "Dead code stripping") [see here](https://github.com/firebase/quickstart-ios/issues/487#issuecomment-415313053).
|
||||
- If you are using `pod Firebase/Core` with a version set below 5.13 you might want to add `pod 'GoogleAppMeasurement', '~> 5.3.0'` to your podfile
|
||||
</details>
|
||||
|
||||
|
||||
## Android
|
||||
|
||||
### Modifying build.gradle
|
||||
|
||||
Modify the following lines in `android/app/build.gradle`:
|
||||
|
||||
```gradle
|
||||
android {
|
||||
...
|
||||
defaultConfig {
|
||||
...
|
||||
missingDimensionStrategy 'react-native-camera', 'mlkit' // <--- replace general with mlkit
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Setting up Firebase
|
||||
|
||||
Using Firebase MLKit requires seting up Firebase project for your app. If you have not already added Firebase to your app, please follow the steps described in [getting started guide](https://firebase.google.com/docs/android/setup).
|
||||
In short, you would need to
|
||||
|
||||
1. Register your app in Firebase console.
|
||||
2. Download google-services.json and place it in `android/app/`
|
||||
3. Add the folowing to project level `build.gradle`:
|
||||
|
||||
```gradle
|
||||
buildscript {
|
||||
dependencies {
|
||||
// Add this line
|
||||
classpath 'com.google.gms:google-services:4.0.1' // <--- you might want to use different version
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
4. add to the bottom of `android/app/build.gradle` file
|
||||
|
||||
```gradle
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
```
|
||||
|
||||
5. Configure your app to automatically download the ML model to the device after your app is installed from the Play Store. If you do not enable install-time model downloads, the model will be downloaded the first time you run the on-device detector. Requests you make before the download has completed will produce no results.
|
||||
|
||||
```xml
|
||||
<application ...>
|
||||
...
|
||||
<meta-data
|
||||
android:name="com.google.firebase.ml.vision.DEPENDENCIES"
|
||||
android:value="ocr, face" /> <!-- choose models that you will use -->
|
||||
</application>
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Additional information in case of problems</summary>
|
||||
The current Android library defaults to the below values for the Google SDK and Libraries,
|
||||
|
||||
```gradle
|
||||
def DEFAULT_COMPILE_SDK_VERSION = 26
|
||||
def DEFAULT_BUILD_TOOLS_VERSION = "26.0.2"
|
||||
def DEFAULT_TARGET_SDK_VERSION = 26
|
||||
def DEFAULT_GOOGLE_PLAY_SERVICES_VERSION = "12.0.1"
|
||||
def DEFAULT_SUPPORT_LIBRARY_VERSION = "27.1.0"
|
||||
```
|
||||
|
||||
You can override this settings by adding a Project-wide gradle configuration properties for
|
||||
use by all modules in your ReactNative project by adding the below to `android/build.gradle`
|
||||
file,
|
||||
|
||||
```gradle
|
||||
buildscript {...}
|
||||
|
||||
allprojects {...}
|
||||
|
||||
/**
|
||||
* Project-wide gradle configuration properties for use by all modules
|
||||
*/
|
||||
ext {
|
||||
compileSdkVersion = 26
|
||||
targetSdkVersion = 26
|
||||
buildToolsVersion = "26.0.2"
|
||||
googlePlayServicesVersion = "12.0.1"
|
||||
googlePlayServicesVisionVersion = "15.0.2"
|
||||
supportLibVersion = "27.1.0"
|
||||
}
|
||||
```
|
||||
|
||||
The above settings in the ReactNative project over-rides the values present in the `react-native-camera`
|
||||
module. For your reference below is the `android/build.gradle` file of the module.
|
||||
|
||||
```gradle
|
||||
def safeExtGet(prop, fallback) {
|
||||
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
||||
}
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
maven {
|
||||
url 'https://maven.google.com'
|
||||
}
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.3.1'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion safeExtGet('compileSdkVersion', 28)
|
||||
buildToolsVersion safeExtGet('buildToolsVersion', '28.0.3')
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion safeExtGet('minSdkVersion', 16)
|
||||
targetSdkVersion safeExtGet('targetSdkVersion', 28)
|
||||
}
|
||||
|
||||
flavorDimensions "react-native-camera"
|
||||
|
||||
productFlavors {
|
||||
general {
|
||||
dimension "react-native-camera"
|
||||
}
|
||||
mlkit {
|
||||
dimension "react-native-camera"
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java.srcDirs = ['src/main/java']
|
||||
}
|
||||
general {
|
||||
java.srcDirs = ['src/general/java']
|
||||
}
|
||||
mlkit {
|
||||
java.srcDirs = ['src/mlkit/java']
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
warning 'InvalidPackage'
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven {
|
||||
url 'https://maven.google.com'
|
||||
}
|
||||
maven { url "https://jitpack.io" }
|
||||
maven {
|
||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||
url "$rootDir/../node_modules/react-native/android"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
def googlePlayServicesVisionVersion = safeExtGet('googlePlayServicesVisionVersion', safeExtGet('googlePlayServicesVersion', '17.0.2'))
|
||||
|
||||
implementation 'com.facebook.react:react-native:+'
|
||||
implementation "com.google.zxing:core:3.3.3"
|
||||
implementation "com.drewnoakes:metadata-extractor:2.11.0"
|
||||
generalImplementation "com.google.android.gms:play-services-vision:$googlePlayServicesVisionVersion"
|
||||
implementation "com.android.support:exifinterface:${safeExtGet('supportLibVersion', '28.0.0')}"
|
||||
implementation "com.android.support:support-annotations:${safeExtGet('supportLibVersion', '28.0.0')}"
|
||||
implementation "com.android.support:support-v4:${safeExtGet('supportLibVersion', '28.0.0')}"
|
||||
mlkitImplementation "com.google.firebase:firebase-ml-vision:${safeExtGet('firebase-ml-vision', '19.0.3')}"
|
||||
mlkitImplementation "com.google.firebase:firebase-ml-vision-face-model:${safeExtGet('firebase-ml-vision-face-model', '17.0.2')}"
|
||||
}
|
||||
```
|
||||
|
||||
If you are using a version of `googlePlayServicesVersion` that does not have `play-services-vision`, you can specify a different version of `play-services-vision` by adding `googlePlayServicesVisionVersion` to the project-wide properties
|
||||
|
||||
```gradle
|
||||
ext {
|
||||
compileSdkVersion = 26
|
||||
targetSdkVersion = 26
|
||||
buildToolsVersion = "26.0.2"
|
||||
googlePlayServicesVersion = "16.0.1"
|
||||
googlePlayServicesVisionVersion = "15.0.2"
|
||||
supportLibVersion = "27.1.0"
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
# Windows
|
||||
|
||||
1. `npm install react-native-camera --save`
|
||||
2. Link the library as described here: [react-native-windows / LinkingLibrariesWindows.md](https://github.com/microsoft/react-native-windows/blob/master/current/docs/LinkingLibrariesWindows.md)
|
||||
For the last step of this guide, you have to add the following things to your `MainReactNativeHost.cs`:
|
||||
|
||||
- in the import section at the very top: `using RNCamera;`
|
||||
- in `protected override List<IReactPackage> Packages => new List<IReactPackage>` add a new line with `new RNCameraPackage()`
|
||||
|
||||
3. Add the capabilities (permissions) for the webcam and microphone as described here: [docs.microsoft / audio-video-camera](https://docs.microsoft.com/en-us/windows/uwp/audio-video-camera/simple-camera-preview-access#add-capability-declarations-to-the-app-manifest)
|
||||
4. Use `RCTCamera` (RNCamera is not supported yet) like described above
|
||||
|
||||
Follow the [Q & A](QA.md) section if you are having compilation issues.
|
||||
---
|
||||
id: installation
|
||||
title: Installation
|
||||
---
|
||||
|
||||
This document is split into two main sections:
|
||||
|
||||
1. Required installation steps for basic usage of `react-native-camera`
|
||||
2. Additional installation steps for usage of Face Detection/Text Recognition/BarCode with [MLKit](https://developers.google.com/ml-kit)
|
||||
|
||||
# Required installation steps
|
||||
|
||||
_These steps assume installation for iOS/Android. To install it with Windows, see [Windows](#windows) below_
|
||||
|
||||
## Mostly automatic install with autolinking (RN > 0.60)
|
||||
|
||||
1. `npm install react-native-camera --save`
|
||||
2. Run `cd ios && pod install && cd ..`
|
||||
|
||||
## Mostly automatic install with react-native link (RN < 0.60)
|
||||
|
||||
1. `npm install react-native-camera --save`
|
||||
2. `react-native link react-native-camera`
|
||||
|
||||
## Manual install - iOS (not recommended)
|
||||
|
||||
1. `npm install react-native-camera --save`
|
||||
2. In XCode, in the project navigator, right click `Libraries` ➜ `Add Files to [your project's name]`
|
||||
3. Go to `node_modules` ➜ `react-native-camera` and add `RNCamera.xcodeproj`
|
||||
4. Expand the `RNCamera.xcodeproj` ➜ `Products` folder
|
||||
5. In XCode, in the project navigator, select your project. Add `libRNCamera.a` to your project's `Build Phases` ➜ `Link Binary With Libraries`
|
||||
6. Click `RNCamera.xcodeproj` in the project navigator and go the `Build Settings` tab. Make sure 'All' is toggled on (instead of 'Basic'). In the `Search Paths` section, look for `Header Search Paths` and make sure it contains both `$(SRCROOT)/../../react-native/React` and `$(SRCROOT)/../../../React` - mark both as `recursive`.
|
||||
|
||||
## Manual install - Android (not recommended)
|
||||
|
||||
1. `npm install react-native-camera --save`
|
||||
2. Open up `android/app/src/main/java/[...]/MainApplication.java`
|
||||
|
||||
- Add `import org.reactnative.camera.RNCameraPackage;` to the imports at the top of the file
|
||||
- Add `new RNCameraPackage()` to the list returned by the `getPackages()` method. Add a comma to the previous item if there's already something there.
|
||||
|
||||
3. Append the following lines to `android/settings.gradle`:
|
||||
|
||||
```gradle
|
||||
include ':react-native-camera'
|
||||
project(':react-native-camera').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-camera/android')
|
||||
```
|
||||
|
||||
4. Insert the following lines in `android/app/build.gradle` inside the dependencies block:
|
||||
|
||||
```gradle
|
||||
implementation project(':react-native-camera')
|
||||
```
|
||||
|
||||
## iOS - other required steps
|
||||
|
||||
Add permissions with usage descriptions to your app `Info.plist`:
|
||||
|
||||
```xml
|
||||
<!-- Required with iOS 10 and higher -->
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Your message to user when the camera is accessed for the first time</string>
|
||||
|
||||
<!-- Required with iOS 11 and higher: include this only if you are planning to use the camera roll -->
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>Your message to user when the photo library is accessed for the first time</string>
|
||||
|
||||
<!-- Include this only if you are planning to use the camera roll -->
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Your message to user when the photo library is accessed for the first time</string>
|
||||
|
||||
<!-- Include this only if you are planning to use the microphone for video recording -->
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Your message to user when the microphone is accessed for the first time</string>
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Additional information in case of problems</summary>
|
||||
|
||||
You might need to adjust your Podfile following the example below:
|
||||
|
||||
```ruby
|
||||
target 'yourTargetName' do
|
||||
# See http://facebook.github.io/react-native/docs/integration-with-existing-apps.html#configuring-cocoapods-dependencies
|
||||
pod 'React', :path => '../node_modules/react-native', :subspecs => [
|
||||
'Core',
|
||||
'CxxBridge', # Include this for RN >= 0.47
|
||||
'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43
|
||||
'RCTText',
|
||||
'RCTNetwork',
|
||||
'RCTWebSocket', # Needed for debugging
|
||||
'RCTAnimation', # Needed for FlatList and animations running on native UI thread
|
||||
# Add any other subspecs you want to use in your project
|
||||
]
|
||||
|
||||
# Explicitly include Yoga if you are using RN >= 0.42.0
|
||||
pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
|
||||
|
||||
# Third party deps podspec link
|
||||
pod 'react-native-camera', path: '../node_modules/react-native-camera'
|
||||
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
if target.name == "React"
|
||||
target.remove_from_project
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Android - other required steps
|
||||
|
||||
Add permissions to your app `android/app/src/main/AndroidManifest.xml` file:
|
||||
|
||||
```xml
|
||||
<!-- Required -->
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
<!-- Include this only if you are planning to use the camera roll -->
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<!-- Include this only if you are planning to use the microphone for video recording -->
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
```
|
||||
|
||||
Insert the following lines in `android/app/build.gradle`:
|
||||
|
||||
```gradle
|
||||
android {
|
||||
...
|
||||
defaultConfig {
|
||||
...
|
||||
missingDimensionStrategy 'react-native-camera', 'general' // <--- insert this line
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Additional information in case of problems</summary>
|
||||
|
||||
1. Make sure you use `JDK >= 1.7` and your `buildToolsVersion >= 25.0.2`
|
||||
|
||||
2. Make sure you have jitpack added in `android/build.gradle`
|
||||
|
||||
```gradle
|
||||
allprojects {
|
||||
repositories {
|
||||
maven { url "https://www.jitpack.io" }
|
||||
maven { url "https://maven.google.com" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
# Additional installation steps
|
||||
|
||||
Follow these optional steps if you want to use Face Detection/Text Recognition/BarCode with [MLKit](https://developers.google.com/ml-kit).
|
||||
You will need to set-up Firebase project for your app (detailed steps below).
|
||||
|
||||
_Note:_ Installing [react-native-firebase](https://github.com/invertase/react-native-firebase) package is NOT necessary.
|
||||
|
||||
## iOS
|
||||
|
||||
If you want any of these optional features, you will need to use CocoaPods.
|
||||
|
||||
### Modifying Podfile
|
||||
|
||||
Add dependency towards `react-native-camera` in your `Podfile` with `subspecs` using one of the following:
|
||||
|
||||
- For Face Detection:
|
||||
|
||||
```ruby
|
||||
pod 'react-native-camera', path: '../node_modules/react-native-camera', subspecs: [
|
||||
'FaceDetectorMLKit'
|
||||
]
|
||||
```
|
||||
|
||||
- For Text Recognition:
|
||||
|
||||
```ruby
|
||||
pod 'react-native-camera', path: '../node_modules/react-native-camera', subspecs: [
|
||||
'TextDetector'
|
||||
]
|
||||
```
|
||||
|
||||
- For BarCode Recognition:
|
||||
|
||||
```ruby
|
||||
pod 'react-native-camera', path: '../node_modules/react-native-camera', subspecs: [
|
||||
'BarcodeDetectorMLKit'
|
||||
]
|
||||
```
|
||||
|
||||
- For all possible detections:
|
||||
|
||||
```ruby
|
||||
pod 'react-native-camera', path: '../node_modules/react-native-camera', subspecs: [
|
||||
'TextDetector',
|
||||
'FaceDetectorMLKit',
|
||||
'BarcodeDetectorMLKit'
|
||||
]
|
||||
```
|
||||
|
||||
Then run `cd ios && pod install && cd ..`
|
||||
|
||||
### Setting up Firebase
|
||||
|
||||
Text/Face recognition for iOS uses Firebase MLKit which requires setting up Firebase project for your app.
|
||||
If you have not already added Firebase to your app, please follow the steps described in [getting started guide](https://firebase.google.com/docs/ios/setup).
|
||||
In short, you would need to
|
||||
|
||||
1. Register your app in Firebase console.
|
||||
2. Download `GoogleService-Info.plist` and add it to your project
|
||||
3. Add `pod 'Firebase/Core'` to your podfile
|
||||
4. In your `AppDelegate.m` file add the following lines:
|
||||
|
||||
```objective-c
|
||||
#import <Firebase.h> // <--- add this
|
||||
...
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
[FIRApp configure]; // <--- add this
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Additional information in case of problems</summary>
|
||||
|
||||
- If you have issues with duplicate symbols you will need to enable dead code stripping option in your Xcode (Target > Build Settings > search for "Dead code stripping") [see here](https://github.com/firebase/quickstart-ios/issues/487#issuecomment-415313053).
|
||||
- If you are using `pod Firebase/Core` with a version set below 5.13 you might want to add `pod 'GoogleAppMeasurement', '~> 5.3.0'` to your podfile
|
||||
</details>
|
||||
|
||||
## Android
|
||||
|
||||
### Modifying build.gradle
|
||||
|
||||
Modify the following lines in `android/app/build.gradle`:
|
||||
|
||||
```gradle
|
||||
android {
|
||||
...
|
||||
defaultConfig {
|
||||
...
|
||||
missingDimensionStrategy 'react-native-camera', 'mlkit' // <--- replace general with mlkit
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Setting up Firebase
|
||||
|
||||
Using Firebase MLKit requires seting up Firebase project for your app. If you have not already added Firebase to your app, please follow the steps described in [getting started guide](https://firebase.google.com/docs/android/setup).
|
||||
In short, you would need to
|
||||
|
||||
1. Register your app in Firebase console.
|
||||
2. Download google-services.json and place it in `android/app/`
|
||||
3. Add the folowing to project level `build.gradle`:
|
||||
|
||||
```gradle
|
||||
buildscript {
|
||||
dependencies {
|
||||
// Add this line
|
||||
classpath 'com.google.gms:google-services:4.0.1' // <--- you might want to use different version
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
4. add to the bottom of `android/app/build.gradle` file
|
||||
|
||||
```gradle
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
```
|
||||
|
||||
5. Configure your app to automatically download the ML model to the device after your app is installed from the Play Store. If you do not enable install-time model downloads, the model will be downloaded the first time you run the on-device detector. Requests you make before the download has completed will produce no results.
|
||||
|
||||
```xml
|
||||
<application ...>
|
||||
...
|
||||
<meta-data
|
||||
android:name="com.google.firebase.ml.vision.DEPENDENCIES"
|
||||
android:value="ocr, face" /> <!-- choose models that you will use -->
|
||||
</application>
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Additional information in case of problems</summary>
|
||||
The current Android library defaults to the below values for the Google SDK and Libraries,
|
||||
|
||||
```gradle
|
||||
def DEFAULT_COMPILE_SDK_VERSION = 26
|
||||
def DEFAULT_BUILD_TOOLS_VERSION = "26.0.2"
|
||||
def DEFAULT_TARGET_SDK_VERSION = 26
|
||||
def DEFAULT_GOOGLE_PLAY_SERVICES_VERSION = "12.0.1"
|
||||
def DEFAULT_SUPPORT_LIBRARY_VERSION = "27.1.0"
|
||||
```
|
||||
|
||||
You can override this settings by adding a Project-wide gradle configuration properties for
|
||||
use by all modules in your ReactNative project by adding the below to `android/build.gradle`
|
||||
file,
|
||||
|
||||
```gradle
|
||||
buildscript {...}
|
||||
|
||||
allprojects {...}
|
||||
|
||||
/**
|
||||
* Project-wide gradle configuration properties for use by all modules
|
||||
*/
|
||||
ext {
|
||||
compileSdkVersion = 26
|
||||
targetSdkVersion = 26
|
||||
buildToolsVersion = "26.0.2"
|
||||
googlePlayServicesVersion = "12.0.1"
|
||||
googlePlayServicesVisionVersion = "15.0.2"
|
||||
supportLibVersion = "27.1.0"
|
||||
}
|
||||
```
|
||||
|
||||
The above settings in the ReactNative project over-rides the values present in the `react-native-camera`
|
||||
module. For your reference below is the `android/build.gradle` file of the module.
|
||||
|
||||
```gradle
|
||||
def safeExtGet(prop, fallback) {
|
||||
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
||||
}
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
maven {
|
||||
url 'https://maven.google.com'
|
||||
}
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.3.1'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion safeExtGet('compileSdkVersion', 28)
|
||||
buildToolsVersion safeExtGet('buildToolsVersion', '28.0.3')
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion safeExtGet('minSdkVersion', 16)
|
||||
targetSdkVersion safeExtGet('targetSdkVersion', 28)
|
||||
}
|
||||
|
||||
flavorDimensions "react-native-camera"
|
||||
|
||||
productFlavors {
|
||||
general {
|
||||
dimension "react-native-camera"
|
||||
}
|
||||
mlkit {
|
||||
dimension "react-native-camera"
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java.srcDirs = ['src/main/java']
|
||||
}
|
||||
general {
|
||||
java.srcDirs = ['src/general/java']
|
||||
}
|
||||
mlkit {
|
||||
java.srcDirs = ['src/mlkit/java']
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
warning 'InvalidPackage'
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven {
|
||||
url 'https://maven.google.com'
|
||||
}
|
||||
maven { url "https://jitpack.io" }
|
||||
maven {
|
||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||
url "$rootDir/../node_modules/react-native/android"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
def googlePlayServicesVisionVersion = safeExtGet('googlePlayServicesVisionVersion', safeExtGet('googlePlayServicesVersion', '17.0.2'))
|
||||
|
||||
implementation 'com.facebook.react:react-native:+'
|
||||
implementation "com.google.zxing:core:3.3.3"
|
||||
implementation "com.drewnoakes:metadata-extractor:2.11.0"
|
||||
generalImplementation "com.google.android.gms:play-services-vision:$googlePlayServicesVisionVersion"
|
||||
implementation "com.android.support:exifinterface:${safeExtGet('supportLibVersion', '28.0.0')}"
|
||||
implementation "com.android.support:support-annotations:${safeExtGet('supportLibVersion', '28.0.0')}"
|
||||
implementation "com.android.support:support-v4:${safeExtGet('supportLibVersion', '28.0.0')}"
|
||||
mlkitImplementation "com.google.firebase:firebase-ml-vision:${safeExtGet('firebase-ml-vision', '19.0.3')}"
|
||||
mlkitImplementation "com.google.firebase:firebase-ml-vision-face-model:${safeExtGet('firebase-ml-vision-face-model', '17.0.2')}"
|
||||
}
|
||||
```
|
||||
|
||||
If you are using a version of `googlePlayServicesVersion` that does not have `play-services-vision`, you can specify a different version of `play-services-vision` by adding `googlePlayServicesVisionVersion` to the project-wide properties
|
||||
|
||||
```gradle
|
||||
ext {
|
||||
compileSdkVersion = 26
|
||||
targetSdkVersion = 26
|
||||
buildToolsVersion = "26.0.2"
|
||||
googlePlayServicesVersion = "16.0.1"
|
||||
googlePlayServicesVisionVersion = "15.0.2"
|
||||
supportLibVersion = "27.1.0"
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
# Windows
|
||||
|
||||
## Mostly automatic install with autolinking (RNW >= 0.63)
|
||||
|
||||
1. `npm install react-native-camera --save`
|
||||
2. See [Additional steps - Windows](#additional-steps-windows) below
|
||||
|
||||
## Manual install - Windows (RNW 0.62)
|
||||
|
||||
1. `npm install react-native-camera --save`
|
||||
2. Link the library as described below:
|
||||
1. Add the _ReactNativeCameraCPP_ project to your solution (eg. `windows\yourapp.sln`)
|
||||
1. Open your solution in Visual Studio 2019
|
||||
2. Right-click Solution icon in Solution Explorer > Add > Existing Project...
|
||||
3. Select `node_modules\react-native-camera\windows\ReactNativeCameraCPP\ReactNativeCameraCPP.vcxproj`
|
||||
2. Add a reference to _ReactNativeCameraCPP_ to your main application project (eg. `windows\yourapp\yourapp.vcxproj`)
|
||||
1. Open your solution in Visual Studio 2019
|
||||
2. Right-click main application project > Add > Reference...
|
||||
3. Check _ReactNativeCameraCPP_ from Solution Projects
|
||||
3. Modify files below to add the package providers to your main application project
|
||||
1. `pch.h`
|
||||
1. Add `#include "winrt/ReactNativeCameraCPP.h"`
|
||||
2. `App.cpp`
|
||||
1. Add `PackageProviders().Append(winrt::ReactNativeCameraCPP::ReactPackageProvider());` before `InitializeComponent();`
|
||||
4. See [Additional steps - Windows](#additional-steps-windows) below
|
||||
|
||||
## Manual install - Windows (RNW 0.61)
|
||||
|
||||
Follow [Manual install - Windows (RNW 0.62)](#manual-install-windows-rnw-062) above, but for step 2 substitute _ReactNativeCameraCPP61_ for _ReactNativeCameraCPP_.
|
||||
|
||||
## Additional steps - Windows
|
||||
|
||||
You need to declare that your app wants to access the camera:
|
||||
|
||||
1. Add the capabilities (permissions) for the webcam and microphone as described here: [Add capability declarations to the app manifest](https://docs.microsoft.com/en-us/windows/uwp/audio-video-camera/simple-camera-preview-access#add-capability-declarations-to-the-app-manifest)
|
||||
2. If you plan on capturing images to the Pictures Library, or videos to the Videos Library, be sure to enable those capabilities too
|
||||
|
||||
Follow the [Q & A](QA.md) section if you are having compilation issues.
|
||||
|
||||
@ -18,9 +18,6 @@ import com.android.build.OutputFile
|
||||
* // the entry file for bundle generation
|
||||
* entryFile: "index.android.js",
|
||||
*
|
||||
* // https://facebook.github.io/react-native/docs/performance#enable-the-ram-format
|
||||
* bundleCommand: "ram-bundle",
|
||||
*
|
||||
* // whether to bundle JS and assets in debug mode
|
||||
* bundleInDebug: false,
|
||||
*
|
||||
@ -76,7 +73,7 @@ import com.android.build.OutputFile
|
||||
*/
|
||||
|
||||
project.ext.react = [
|
||||
entryFile: "index.js",
|
||||
entryFile: "index.js"
|
||||
enableHermes: false, // clean and rebuild if changing
|
||||
]
|
||||
|
||||
@ -200,4 +197,17 @@ task copyDownloadableDepsToLibs(type: Copy) {
|
||||
into 'libs'
|
||||
}
|
||||
|
||||
task downloadDependencies() {
|
||||
description 'Download all dependencies to the Gradle cache'
|
||||
doLast {
|
||||
configurations.findAll().each { config ->
|
||||
if (config.name.contains("minReactNative") && config.canBeResolved) {
|
||||
print config.name
|
||||
print '\n'
|
||||
config.files
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
|
||||
* directory of this source tree.
|
||||
*/
|
||||
package com.advanced;
|
||||
|
||||
import android.content.Context;
|
||||
import com.facebook.flipper.android.AndroidFlipperClient;
|
||||
import com.facebook.flipper.android.utils.FlipperUtils;
|
||||
import com.facebook.flipper.core.FlipperClient;
|
||||
import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
|
||||
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
|
||||
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
|
||||
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.modules.network.NetworkingModule;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
public class ReactNativeFlipper {
|
||||
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
|
||||
if (FlipperUtils.shouldEnableFlipper(context)) {
|
||||
final FlipperClient client = AndroidFlipperClient.getInstance(context);
|
||||
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
|
||||
client.addPlugin(new ReactFlipperPlugin());
|
||||
client.addPlugin(new DatabasesFlipperPlugin(context));
|
||||
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
|
||||
client.addPlugin(CrashReporterPlugin.getInstance());
|
||||
NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
|
||||
NetworkingModule.setCustomClientBuilder(
|
||||
new NetworkingModule.CustomClientBuilder() {
|
||||
@Override
|
||||
public void apply(OkHttpClient.Builder builder) {
|
||||
builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
|
||||
}
|
||||
});
|
||||
client.addPlugin(networkFlipperPlugin);
|
||||
client.start();
|
||||
// Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
|
||||
// Hence we run if after all native modules have been initialized
|
||||
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
|
||||
if (reactContext == null) {
|
||||
reactInstanceManager.addReactInstanceEventListener(
|
||||
new ReactInstanceManager.ReactInstanceEventListener() {
|
||||
@Override
|
||||
public void onReactContextInitialized(ReactContext reactContext) {
|
||||
reactInstanceManager.removeReactInstanceEventListener(this);
|
||||
reactContext.runOnNativeModulesQueueThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
client.addPlugin(new FrescoFlipperPlugin());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
client.addPlugin(new FrescoFlipperPlugin());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -21,7 +21,7 @@
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTask"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
0
examples/advanced/advanced/android/app/src/main/assets/fonts/AntDesign.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/AntDesign.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/Entypo.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/Entypo.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/EvilIcons.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/EvilIcons.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/Feather.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/Feather.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/FontAwesome.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/FontAwesome.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/Fontisto.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/Fontisto.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/Foundation.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/Foundation.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/Ionicons.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/Ionicons.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/MaterialIcons.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/MaterialIcons.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/Octicons.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/Octicons.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/Roboto.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/Roboto.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/Roboto_medium.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/Roboto_medium.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/SimpleLineIcons.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/SimpleLineIcons.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/Zocial.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/Zocial.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/rubicon-icon-font.ttf
Executable file → Normal file
0
examples/advanced/advanced/android/app/src/main/assets/fonts/rubicon-icon-font.ttf
Executable file → Normal file
@ -182,13 +182,15 @@ PODS:
|
||||
- React-cxxreact (= 0.61.4)
|
||||
- React-jsi (= 0.61.4)
|
||||
- React-jsinspector (0.61.4)
|
||||
- react-native-camera (3.9.0):
|
||||
- react-native-camera (3.28.0):
|
||||
- React
|
||||
- react-native-camera/RCT (= 3.9.0)
|
||||
- react-native-camera/RN (= 3.9.0)
|
||||
- react-native-camera/RCT (3.9.0):
|
||||
- react-native-camera/RCT (= 3.28.0)
|
||||
- react-native-camera/RN (= 3.28.0)
|
||||
- react-native-camera/RCT (3.28.0):
|
||||
- React
|
||||
- react-native-camera/RN (3.9.0):
|
||||
- react-native-camera/RN (3.28.0):
|
||||
- React
|
||||
- react-native-slider (3.0.0):
|
||||
- React
|
||||
- React-RCTActionSheet (0.61.4):
|
||||
- React-Core/RCTActionSheetHeaders (= 0.61.4)
|
||||
@ -247,6 +249,7 @@ DEPENDENCIES:
|
||||
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
|
||||
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
|
||||
- react-native-camera (from `../node_modules/react-native-camera`)
|
||||
- "react-native-slider (from `../node_modules/@react-native-community/slider`)"
|
||||
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
|
||||
- React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
|
||||
- React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
|
||||
@ -262,7 +265,7 @@ DEPENDENCIES:
|
||||
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/cocoapods/specs.git:
|
||||
trunk:
|
||||
- boost-for-react-native
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
@ -296,6 +299,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native/ReactCommon/jsinspector"
|
||||
react-native-camera:
|
||||
:path: "../node_modules/react-native-camera"
|
||||
react-native-slider:
|
||||
:path: "../node_modules/@react-native-community/slider"
|
||||
React-RCTActionSheet:
|
||||
:path: "../node_modules/react-native/Libraries/ActionSheetIOS"
|
||||
React-RCTAnimation:
|
||||
@ -337,7 +342,8 @@ SPEC CHECKSUMS:
|
||||
React-jsi: ca921f4041505f9d5197139b2d09eeb020bb12e8
|
||||
React-jsiexecutor: 8dfb73b987afa9324e4009bdce62a18ce23d983c
|
||||
React-jsinspector: d15478d0a8ada19864aa4d1cc1c697b41b3fa92f
|
||||
react-native-camera: 8ad12cae113fd60b07236983532acc8e595c0fc3
|
||||
react-native-camera: 17d755c4334e77dad0ec6337faaded5274f18464
|
||||
react-native-slider: 05f11678260cb27c3d00a2dd1558b623be3ec8d2
|
||||
React-RCTActionSheet: 7369b7c85f99b6299491333affd9f01f5a130c22
|
||||
React-RCTAnimation: d07be15b2bd1d06d89417eb0343f98ffd2b099a7
|
||||
React-RCTBlob: 8e0b23d95c9baa98f6b0e127e07666aaafd96c34
|
||||
@ -353,4 +359,4 @@ SPEC CHECKSUMS:
|
||||
|
||||
PODFILE CHECKSUM: 27b3847fe4d98bb58146c377f86ebed45b40a77d
|
||||
|
||||
COCOAPODS: 1.7.5
|
||||
COCOAPODS: 1.9.1
|
||||
|
||||
3862
examples/advanced/advanced/package-lock.json
generated
3862
examples/advanced/advanced/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,23 +1,16 @@
|
||||
{
|
||||
"name": "advanced",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"android": "react-native run-android",
|
||||
"ios": "react-native run-ios",
|
||||
"start": "react-native start",
|
||||
"test": "jest",
|
||||
"lint": "eslint ."
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "16.11.0",
|
||||
"react-native": "0.61.4",
|
||||
"@react-native-community/slider": "^3.0.0",
|
||||
"native-base": "2.13.8",
|
||||
"react": "16.13.1",
|
||||
"react-native": "0.63.2",
|
||||
"react-native-camera": "^3.28.0",
|
||||
"react-native-gesture-handler": "1.5.0",
|
||||
"react-navigation": "4.0.10",
|
||||
"react-navigation-stack": "1.10.3",
|
||||
"native-base": "2.13.8",
|
||||
"react-native-gesture-handler": "1.5.0",
|
||||
"underscore": "1.9.1",
|
||||
"react-native-camera": "*"
|
||||
"underscore": "1.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.7.2",
|
||||
@ -26,10 +19,19 @@
|
||||
"babel-jest": "24.9.0",
|
||||
"eslint": "6.6.0",
|
||||
"jest": "24.9.0",
|
||||
"metro-react-native-babel-preset": "0.56.3",
|
||||
"react-test-renderer": "16.9.0"
|
||||
"metro-react-native-babel-preset": "0.59.0",
|
||||
"react-test-renderer": "16.13.1"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "react-native"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"android": "react-native run-android",
|
||||
"clear": "react-native start --reset-cache",
|
||||
"ios": "react-native run-ios",
|
||||
"lint": "eslint .",
|
||||
"start": "react-native start",
|
||||
"test": "jest"
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import {
|
||||
AppState,
|
||||
TouchableOpacity
|
||||
} from 'react-native';
|
||||
import Slider from '@react-native-community/slider';
|
||||
import _ from 'underscore';
|
||||
import { Container, Button, Text, Icon, Footer, FooterTab, Spinner, H2, connectStyle, Toast } from 'native-base';
|
||||
import { RNCamera } from 'react-native-camera';
|
||||
@ -50,9 +51,18 @@ const WB_OPTIONS_MAP = {
|
||||
2: "CL",
|
||||
3: "SH",
|
||||
4: "IN",
|
||||
5: "FL"
|
||||
5: "FL",
|
||||
6: "CW"
|
||||
}
|
||||
|
||||
const CUSTOM_WB_OPTIONS_MAP = {
|
||||
temperature: {label: "Temp.", min: 1000, max: 10000, steps: 500},
|
||||
tint: {label: "Tint", min: -20, max: 20, steps: 0.5},
|
||||
redGainOffset: {label: "Red", min: -1.0, max: 1.0, steps: 0.05},
|
||||
greenGainOffset: {label: "Green", min: -1.0, max: 1.0, steps: 0.05},
|
||||
blueGainOffset: {label: "Blue", min: -1.0, max: 1.0, steps: 0.05},
|
||||
};
|
||||
|
||||
const getCameraType = (type) => {
|
||||
|
||||
if(type == 'AVCaptureDeviceTypeBuiltInTelephotoCamera'){
|
||||
@ -95,6 +105,7 @@ const styles = StyleSheet.create({
|
||||
|
||||
buttonsView: {
|
||||
flex: 1,
|
||||
backgroundColor: 'black',
|
||||
width: '100%',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
@ -111,6 +122,31 @@ const styles = StyleSheet.create({
|
||||
ratioButton: {
|
||||
width: 100 * conf.theme.variables.sizeScaling
|
||||
},
|
||||
|
||||
customWBView: {
|
||||
backgroundColor: '#00000080',
|
||||
flex: 1,
|
||||
width: '100%',
|
||||
height: 50,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 8,
|
||||
},
|
||||
|
||||
customWBViewButton: {
|
||||
backgroundColor: 'transparent',
|
||||
alignSelf: 'center',
|
||||
width: '25%',
|
||||
},
|
||||
|
||||
customWBViewText: {
|
||||
color: 'white',
|
||||
},
|
||||
|
||||
customWBViewSlider: {
|
||||
flex: 2,
|
||||
marginRight: 6,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@ -120,8 +156,16 @@ const defaultCameraOptions = {
|
||||
flashMode: 'off', // on, auto, off, torch
|
||||
wb: 0,
|
||||
zoom: 0, // 0-1
|
||||
focusCoords: undefined
|
||||
}
|
||||
focusCoords: undefined,
|
||||
currentCustomWBOption: "temperature",
|
||||
customWhiteBalance: {
|
||||
temperature: 6000,
|
||||
tint: 0.0,
|
||||
redGainOffset: 0.0,
|
||||
greenGainOffset: 0.0,
|
||||
blueGainOffset: 0.0,
|
||||
},
|
||||
};
|
||||
|
||||
function parseRatio(str){
|
||||
let [p1, p2] = str.split(":");
|
||||
@ -397,46 +441,46 @@ class Camera extends Component{
|
||||
}
|
||||
}
|
||||
|
||||
onTapToFocus = (event) => {
|
||||
onTapToFocus = (touchOrigin) => {
|
||||
|
||||
if(!this.cameraStyle || this.state.takingPic){
|
||||
return;
|
||||
}
|
||||
|
||||
const {pageX, pageY} = event.nativeEvent;
|
||||
const {x, y} = touchOrigin;
|
||||
let {width, height, top, left} = this.cameraStyle;
|
||||
|
||||
// compensate for top/left changes
|
||||
let pageX2 = pageX - left;
|
||||
let pageY2 = pageY - top;
|
||||
let pageX2 = x - left;
|
||||
let pageY2 = y - top;
|
||||
|
||||
// normalize coords as described by https://gist.github.com/Craigtut/6632a9ac7cfff55e74fb561862bc4edb
|
||||
const x0 = pageX2 / width;
|
||||
const y0 = pageY2 / height;
|
||||
|
||||
let x = x0;
|
||||
let y = y0;
|
||||
let computedX = x0;
|
||||
let computedY = y0;
|
||||
|
||||
// if portrait, need to apply a transform because RNCamera always measures coords in landscape mode
|
||||
// with the home button on the right. If the phone is rotated with the home button to the left
|
||||
// we will have issues here, and we have no way to detect that orientation!
|
||||
// TODO: Fix this, however, that orientation should never be used due to camera positon
|
||||
if(this.state.orientation.isPortrait){
|
||||
x = y0;
|
||||
y = -x0 + 1;
|
||||
computedX = y0;
|
||||
computedY = -x0 + 1;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
focusCoords: {
|
||||
x: x,
|
||||
y: y,
|
||||
x: computedX,
|
||||
y: computedY,
|
||||
autoExposure: true
|
||||
},
|
||||
touchCoords: {
|
||||
x: pageX2 - 50,
|
||||
y: pageY2 - 50
|
||||
}
|
||||
});
|
||||
},this.onSetFocus);
|
||||
|
||||
// remove focus rectangle
|
||||
if(this.focusTimeout){
|
||||
@ -446,14 +490,12 @@ class Camera extends Component{
|
||||
|
||||
}
|
||||
|
||||
onTapToFocusOut = () => {
|
||||
if(this.state.touchCoords){
|
||||
this.focusTimeout = setTimeout(()=>{
|
||||
if(this.mounted){
|
||||
onSetFocus = () => {
|
||||
this.focusTimeout = setTimeout(() => {
|
||||
if (this.mounted) {
|
||||
this.setState({touchCoords: null});
|
||||
}
|
||||
}, 1500);
|
||||
}
|
||||
}
|
||||
|
||||
onPinchStart = () => {
|
||||
@ -477,13 +519,38 @@ class Camera extends Component{
|
||||
this.setState({takingPic: false});
|
||||
}
|
||||
|
||||
onRecordingStart = () => {
|
||||
this.reportRequestPrompt = true;
|
||||
|
||||
if(this._recordingTimer){
|
||||
clearInterval(this._recordingTimer);
|
||||
this._recordingTimer = null;
|
||||
}
|
||||
|
||||
if(this.state.recording){
|
||||
this.setState({elapsed: 0})
|
||||
this._recordingTimer = setInterval(()=>{
|
||||
this.setState({elapsed: this.state.elapsed + 1})
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
onRecordingEnd = () => {
|
||||
this.reportRequestPrompt = true;
|
||||
|
||||
if(this._recordingTimer){
|
||||
clearInterval(this._recordingTimer);
|
||||
this._recordingTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
goBack = () => {
|
||||
this.props.navigation.goBack();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
let {orientation, takingPic, cameraReady, recording, audioDisabled, zoom, wb, cameraType, cameraId, cameraIds, flashMode} = this.state;
|
||||
let {orientation, takingPic, cameraReady, recording, audioDisabled, zoom, wb, cameraType, cameraId, cameraIds, flashMode, elapsed} = this.state;
|
||||
let {style} = this.props;
|
||||
|
||||
let isPortrait = orientation.isPortrait;
|
||||
@ -578,6 +645,12 @@ class Camera extends Component{
|
||||
|
||||
this.cameraStyle = cameraStyle;
|
||||
|
||||
let isCustomWhiteBalance = wb >= WB_OPTIONS.length;
|
||||
let whiteBalance = isCustomWhiteBalance ? this.state.customWhiteBalance : WB_OPTIONS[wb];
|
||||
const { currentCustomWBOption } = this.state;
|
||||
let customWhiteBalanceValue = this.state.customWhiteBalance[currentCustomWBOption];
|
||||
let customWhiteBalanceOption = CUSTOM_WB_OPTIONS_MAP[currentCustomWBOption]
|
||||
|
||||
return (
|
||||
|
||||
<Container fullBlack>
|
||||
@ -607,11 +680,15 @@ class Camera extends Component{
|
||||
onAudioInterrupted={this.onAudioInterrupted}
|
||||
onAudioConnected={this.onAudioConnected}
|
||||
onPictureTaken={this.onPictureTaken}
|
||||
onRecordingStart={this.onRecordingStart}
|
||||
onRecordingEnd={this.onRecordingEnd}
|
||||
ratio={this.state.aspectRatioStr}
|
||||
flashMode={flashMode}
|
||||
zoom={zoom}
|
||||
maxZoom={MAX_ZOOM}
|
||||
whiteBalance={WB_OPTIONS[wb]}
|
||||
useNativeZoom={true}
|
||||
onTap={this.onTapToFocus}
|
||||
whiteBalance={whiteBalance}
|
||||
autoFocusPointOfInterest={this.state.focusCoords}
|
||||
androidCameraPermissionOptions={{
|
||||
title: 'Permission to use camera',
|
||||
@ -639,21 +716,6 @@ class Camera extends Component{
|
||||
</View>
|
||||
}
|
||||
>
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.5}
|
||||
style={flex1}
|
||||
onPressIn={this.onTapToFocus}
|
||||
onPressOut={this.onTapToFocusOut}
|
||||
onLongPress={this.takePictureLong}
|
||||
delayLongPress={1500}
|
||||
>
|
||||
<ZoomView
|
||||
onPinchProgress={this.onPinchProgress}
|
||||
onPinchStart={this.onPinchStart}
|
||||
onPinchEnd={this.onPinchEnd}
|
||||
style={flex1}
|
||||
>
|
||||
|
||||
{this.state.touchCoords ?
|
||||
<View style={{
|
||||
borderWidth: 2,
|
||||
@ -666,8 +728,6 @@ class Camera extends Component{
|
||||
}}>
|
||||
</View>
|
||||
: null}
|
||||
</ZoomView>
|
||||
</TouchableOpacity>
|
||||
</RNCamera>
|
||||
|
||||
{!takingPic && !recording && !this.state.spinnerVisible && cameraReady ?
|
||||
@ -691,6 +751,28 @@ class Camera extends Component{
|
||||
/>
|
||||
</View>
|
||||
: null}
|
||||
{isCustomWhiteBalance && (
|
||||
<View style={styles.customWBView}>
|
||||
<Button style={styles.customWBViewButton} onPress={this.changeCustomWBOption}>
|
||||
<Text style={styles.customWBViewText}>
|
||||
{customWhiteBalanceOption.label}
|
||||
</Text>
|
||||
</Button>
|
||||
<Slider
|
||||
style={styles.customWBViewSlider}
|
||||
value={customWhiteBalanceValue}
|
||||
step={customWhiteBalanceOption.steps}
|
||||
minimumValue={customWhiteBalanceOption.min}
|
||||
maximumValue={customWhiteBalanceOption.max}
|
||||
minimumTrackTintColor="#FFFFFF"
|
||||
maximumTrackTintColor="#000000"
|
||||
onValueChange={this.changeCustomWBOptionValue}
|
||||
/>
|
||||
<Text style={[styles.customWBViewText, {minWidth: '15%'}]}>
|
||||
{customWhiteBalanceValue.toFixed(1)}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.buttonsView}>
|
||||
<Button
|
||||
transparent
|
||||
@ -744,7 +826,7 @@ class Camera extends Component{
|
||||
<View
|
||||
style={styles.capturingStyle}
|
||||
>
|
||||
{takingPic ? <H2 transparent>Capturing Picture...</H2> : <H2 transparent>{`Capturing Video${audioDisabled ? ' (muted)' : ''}... (${this.state.elapsed})`}</H2>}
|
||||
{takingPic ? <H2 transparent>Capturing Picture...</H2> : <H2 transparent>{`Capturing Video${audioDisabled ? ' (muted)' : ''}... (${elapsed != -1 ? elapsed : "Preparing Camera..."})`}</H2>}
|
||||
</View>
|
||||
: null}
|
||||
|
||||
@ -814,11 +896,7 @@ class Camera extends Component{
|
||||
};
|
||||
|
||||
|
||||
this.setState({recording: true, elapsed: 0}, async () => {
|
||||
|
||||
let timer = setInterval(()=>{
|
||||
this.setState({elapsed: this.state.elapsed + 1})
|
||||
}, 1000);
|
||||
this.setState({recording: true, elapsed: -1}, async () => {
|
||||
|
||||
let result = null;
|
||||
try {
|
||||
@ -839,7 +917,12 @@ class Camera extends Component{
|
||||
this.setState({recording: false});
|
||||
}, 500);
|
||||
|
||||
clearInterval(timer);
|
||||
// might be cleared on recording stop or
|
||||
// here if we had errors
|
||||
if(this._recordingTimer){
|
||||
clearInterval(this._recordingTimer);
|
||||
this._recordingTimer = null;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@ -866,9 +949,29 @@ class Camera extends Component{
|
||||
|
||||
|
||||
changeWB = () => {
|
||||
// The custom white balance feature is only available on iOS (#2774)
|
||||
const numberOfOptions = IS_IOS ? Object.keys(WB_OPTIONS_MAP).length : WB_OPTIONS.length;
|
||||
this.setState({
|
||||
wb: (this.state.wb + 1) % WB_OPTIONS.length
|
||||
})
|
||||
wb: (this.state.wb + 1) % numberOfOptions
|
||||
});
|
||||
}
|
||||
|
||||
changeCustomWBOption = () => {
|
||||
const optionKeys = Object.keys(CUSTOM_WB_OPTIONS_MAP);
|
||||
let currentOptionIndex = optionKeys.indexOf(this.state.currentCustomWBOption);
|
||||
let nextOptionIndex = (currentOptionIndex + 1) % optionKeys.length;
|
||||
this.setState({
|
||||
currentCustomWBOption: optionKeys[nextOptionIndex]
|
||||
});
|
||||
}
|
||||
|
||||
changeCustomWBOptionValue = (value) => {
|
||||
this.setState((state) => ({
|
||||
customWhiteBalance: {
|
||||
...state.customWhiteBalance,
|
||||
[state.currentCustomWBOption]: value,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
toggleRatio = () => {
|
||||
@ -934,4 +1037,4 @@ Camera.navigationOptions = ({ navigation }) => {
|
||||
Camera = connectStyle("Branding")(Camera);
|
||||
|
||||
|
||||
export default Camera;
|
||||
export default Camera;
|
||||
|
||||
6805
examples/advanced/advanced/yarn.lock
Normal file
6805
examples/advanced/advanced/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
/* eslint-disable no-console */
|
||||
import React, {useState, useReducer, useRef} from 'react';
|
||||
import React from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
Text,
|
||||
@ -30,87 +30,61 @@ const wbOrder = {
|
||||
|
||||
const landmarkSize = 2;
|
||||
|
||||
const stateReducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case 'toggleFlash':
|
||||
return { state.flash === flashModeOrder[ state.flash] }
|
||||
break;
|
||||
case 'toggleZoom':
|
||||
return { state.flash === flashModeOrder[ state.flash] }
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
flash: 'off',
|
||||
zoom: 0,
|
||||
autoFocus: 'on',
|
||||
depth: 0,
|
||||
type: 'back',
|
||||
whiteBalance: 'auto',
|
||||
ratio: '16:9',
|
||||
isRecording: false,
|
||||
canDetectFaces: false,
|
||||
canDetectText: false,
|
||||
canDetectBarcode: false,
|
||||
faces: [],
|
||||
textBlocks: [],
|
||||
barcodes: [],
|
||||
|
||||
recordOptions: {
|
||||
mute: false,
|
||||
maxDuration: 5,
|
||||
quality: RNCamera.Constants.VideoQuality['288p'],
|
||||
},
|
||||
autoFocusPoint: {
|
||||
normalized: { x: 0.5, y: 0.5 }, // normalized values required for autoFocusPointOfInterest
|
||||
drawRectPosition: {
|
||||
x: Dimensions.get('window').width * 0.5 - 32,
|
||||
y: Dimensions.get('window').height * 0.5 - 32,
|
||||
export default class CameraScreen extends React.Component {
|
||||
state = {
|
||||
flash: 'off',
|
||||
zoom: 0,
|
||||
autoFocus: 'on',
|
||||
autoFocusPoint: {
|
||||
normalized: { x: 0.5, y: 0.5 }, // normalized values required for autoFocusPointOfInterest
|
||||
drawRectPosition: {
|
||||
x: Dimensions.get('window').width * 0.5 - 32,
|
||||
y: Dimensions.get('window').height * 0.5 - 32,
|
||||
},
|
||||
},
|
||||
},
|
||||
depth: 0,
|
||||
type: 'back',
|
||||
whiteBalance: 'auto',
|
||||
ratio: '16:9',
|
||||
recordOptions: {
|
||||
mute: false,
|
||||
maxDuration: 5,
|
||||
quality: RNCamera.Constants.VideoQuality['288p'],
|
||||
},
|
||||
isRecording: false,
|
||||
canDetectFaces: false,
|
||||
canDetectText: false,
|
||||
canDetectBarcode: false,
|
||||
faces: [],
|
||||
textBlocks: [],
|
||||
barcodes: [],
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
const CameraScreen = () => {
|
||||
const [state, setState] = useState(initialState);
|
||||
const [recordOptions, togglerecordOptions] = useReducer(reducer, initialState.recordOptions);
|
||||
const [autoFocusPoint, touchToFocus] = useReducer(reducer, initialState.autoFocusPoint);
|
||||
const cameraRef = useRef(null);
|
||||
|
||||
toggleFacing =() =>{
|
||||
setState({
|
||||
...state,
|
||||
type: type === 'back' ? 'front' : 'back',
|
||||
})
|
||||
toggleFacing() {
|
||||
this.setState({
|
||||
type: this.state.type === 'back' ? 'front' : 'back',
|
||||
});
|
||||
}
|
||||
|
||||
toggleFlash =() => {
|
||||
setState({
|
||||
...state,
|
||||
flash: flashModeOrder[flash]
|
||||
})
|
||||
toggleFlash() {
|
||||
this.setState({
|
||||
flash: flashModeOrder[this.state.flash],
|
||||
});
|
||||
}
|
||||
|
||||
toggleWB =() => {
|
||||
setState({
|
||||
...state,
|
||||
whiteBalance: wbOrder[whiteBalance]
|
||||
})
|
||||
toggleWB() {
|
||||
this.setState({
|
||||
whiteBalance: wbOrder[this.state.whiteBalance],
|
||||
});
|
||||
}
|
||||
|
||||
toggleFocus =() =>{
|
||||
setState({
|
||||
...state,
|
||||
autoFocus: autoFocus === 'on' ? 'off' : 'on'
|
||||
})
|
||||
toggleFocus() {
|
||||
this.setState({
|
||||
autoFocus: this.state.autoFocus === 'on' ? 'off' : 'on',
|
||||
});
|
||||
}
|
||||
|
||||
touchToFocus =(event) => {
|
||||
touchToFocus(event) {
|
||||
const { pageX, pageY } = event.nativeEvent;
|
||||
const screenWidth = Dimensions.get('window').width;
|
||||
const screenHeight = Dimensions.get('window').height;
|
||||
@ -123,56 +97,49 @@ const CameraScreen = () => {
|
||||
x = pageY / screenHeight;
|
||||
y = -(pageX / screenWidth) + 1;
|
||||
}
|
||||
return {
|
||||
|
||||
this.setState({
|
||||
autoFocusPoint: {
|
||||
normalized: { x, y },
|
||||
drawRectPosition: { x: pageX, y: pageY },
|
||||
},
|
||||
};
|
||||
}
|
||||
zoomOut =() =>{
|
||||
setState({
|
||||
...state,
|
||||
zoom: state.zoom - 0.1 < 0 ? 0 : state.zoom - 0.1,
|
||||
})
|
||||
}
|
||||
|
||||
zoomIn =() => {
|
||||
setState({
|
||||
...state,
|
||||
zoom: state.zoom + 0.1 > 1 ? 1 : state.zoom + 0.1,
|
||||
});
|
||||
}
|
||||
|
||||
setFocusDepth = (depth) => {
|
||||
setState({
|
||||
...state,
|
||||
depth: depth
|
||||
zoomOut() {
|
||||
this.setState({
|
||||
zoom: this.state.zoom - 0.1 < 0 ? 0 : this.state.zoom - 0.1,
|
||||
});
|
||||
}
|
||||
|
||||
zoomIn() {
|
||||
this.setState({
|
||||
zoom: this.state.zoom + 0.1 > 1 ? 1 : this.state.zoom + 0.1,
|
||||
});
|
||||
}
|
||||
|
||||
setFocusDepth(depth) {
|
||||
this.setState({
|
||||
depth,
|
||||
});
|
||||
}
|
||||
|
||||
takePicture = async function() {
|
||||
if (cameraRef) {
|
||||
const data = await cameraRef.takePictureAsync();
|
||||
if (this.camera) {
|
||||
const data = await this.camera.takePictureAsync();
|
||||
console.warn('takePicture ', data);
|
||||
}
|
||||
};
|
||||
|
||||
takeVideo = async function() {
|
||||
if (cameraRef) {
|
||||
takeVideo = async () => {
|
||||
const { isRecording } = this.state;
|
||||
if (this.camera && !isRecording) {
|
||||
try {
|
||||
const promise = cameraRef.recordAsync( state.recordOptions);
|
||||
const promise = this.camera.recordAsync(this.state.recordOptions);
|
||||
|
||||
if (promise) {
|
||||
setState({
|
||||
...state,
|
||||
isRecording: true
|
||||
});
|
||||
this.setState({ isRecording: true });
|
||||
const data = await promise;
|
||||
setState({
|
||||
...state,
|
||||
isRecording: false
|
||||
});
|
||||
console.warn('takeVideo', data);
|
||||
}
|
||||
} catch (e) {
|
||||
@ -181,19 +148,9 @@ const CameraScreen = () => {
|
||||
}
|
||||
};
|
||||
|
||||
toggle = value => () => {
|
||||
setState({
|
||||
...state,
|
||||
[value]: !prevState[value]
|
||||
})
|
||||
};
|
||||
toggle = value => () => this.setState(prevState => ({ [value]: !prevState[value] }));
|
||||
|
||||
facesDetected = ({ faces }) => {
|
||||
setState({
|
||||
...state,
|
||||
faces: faces
|
||||
})
|
||||
};
|
||||
facesDetected = ({ faces }) => this.setState({ faces });
|
||||
|
||||
renderFace = ({ bounds, faceID, rollAngle, yawAngle }) => (
|
||||
<View
|
||||
@ -218,7 +175,7 @@ const CameraScreen = () => {
|
||||
</View>
|
||||
);
|
||||
|
||||
renderLandmarksOfFace =(face) => {
|
||||
renderLandmarksOfFace(face) {
|
||||
const renderLandmark = position =>
|
||||
position && (
|
||||
<View
|
||||
@ -250,19 +207,19 @@ const CameraScreen = () => {
|
||||
|
||||
renderFaces = () => (
|
||||
<View style={styles.facesContainer} pointerEvents="none">
|
||||
{ state.faces.map(() => renderFace())}
|
||||
{this.state.faces.map(this.renderFace)}
|
||||
</View>
|
||||
);
|
||||
|
||||
renderLandmarks = () => (
|
||||
<View style={styles.facesContainer} pointerEvents="none">
|
||||
{ state.faces.map(() => renderLandmarksOfFace())}
|
||||
{this.state.faces.map(this.renderLandmarksOfFace)}
|
||||
</View>
|
||||
);
|
||||
|
||||
renderTextBlocks = () => (
|
||||
<View style={styles.facesContainer} pointerEvents="none">
|
||||
{ state.textBlocks.map(() => renderTextBlock())}
|
||||
{this.state.textBlocks.map(this.renderTextBlock)}
|
||||
</View>
|
||||
);
|
||||
|
||||
@ -286,22 +243,14 @@ const CameraScreen = () => {
|
||||
|
||||
textRecognized = object => {
|
||||
const { textBlocks } = object;
|
||||
setState({
|
||||
...state,
|
||||
textBlocks: textBlocks
|
||||
});
|
||||
this.setState({ textBlocks });
|
||||
};
|
||||
|
||||
barcodeRecognized = ({ barcodes }) => {
|
||||
setState({
|
||||
...stat,
|
||||
barcodes
|
||||
})
|
||||
};
|
||||
barcodeRecognized = ({ barcodes }) => this.setState({ barcodes });
|
||||
|
||||
renderBarcodes = () => (
|
||||
<View style={styles.facesContainer} pointerEvents="none">
|
||||
{ state.barcodes.map( renderBarcode)}
|
||||
{this.state.barcodes.map(this.renderBarcode)}
|
||||
</View>
|
||||
);
|
||||
|
||||
@ -322,30 +271,65 @@ const CameraScreen = () => {
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
renderCamera = () => {
|
||||
const { canDetectFaces, canDetectText, canDetectBarcode } = state;
|
||||
renderRecording = () => {
|
||||
const { isRecording } = this.state;
|
||||
const backgroundColor = isRecording ? 'white' : 'darkred';
|
||||
const action = isRecording ? this.stopVideo : this.takeVideo;
|
||||
const button = isRecording ? this.renderStopRecBtn() : this.renderRecBtn();
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.flipButton,
|
||||
{
|
||||
flex: 0.3,
|
||||
alignSelf: 'flex-end',
|
||||
backgroundColor,
|
||||
},
|
||||
]}
|
||||
onPress={() => action()}
|
||||
>
|
||||
{button}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
stopVideo = async () => {
|
||||
await this.camera.stopRecording();
|
||||
this.setState({ isRecording: false });
|
||||
};
|
||||
|
||||
renderRecBtn() {
|
||||
return <Text style={styles.flipText}> REC </Text>;
|
||||
}
|
||||
|
||||
renderStopRecBtn() {
|
||||
return <Text style={styles.flipText}> ☕ </Text>;
|
||||
}
|
||||
|
||||
renderCamera() {
|
||||
const { canDetectFaces, canDetectText, canDetectBarcode } = this.state;
|
||||
|
||||
const drawFocusRingPosition = {
|
||||
top: state.autoFocusPoint.drawRectPosition.y - 32,
|
||||
left: state.autoFocusPoint.drawRectPosition.x - 32,
|
||||
top: this.state.autoFocusPoint.drawRectPosition.y - 32,
|
||||
left: this.state.autoFocusPoint.drawRectPosition.x - 32,
|
||||
};
|
||||
return (
|
||||
<RNCamera
|
||||
ref={ref => {
|
||||
cameraRef = ref;
|
||||
this.camera = ref;
|
||||
}}
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
type={ state.type}
|
||||
flashMode={ state.flash}
|
||||
autoFocus={ state.autoFocus}
|
||||
autoFocusPointOfInterest={ state.autoFocusPoint.normalized}
|
||||
zoom={ state.zoom}
|
||||
whiteBalance={ state.whiteBalance}
|
||||
ratio={ state.ratio}
|
||||
focusDepth={ state.depth}
|
||||
type={this.state.type}
|
||||
flashMode={this.state.flash}
|
||||
autoFocus={this.state.autoFocus}
|
||||
autoFocusPointOfInterest={this.state.autoFocusPoint.normalized}
|
||||
zoom={this.state.zoom}
|
||||
whiteBalance={this.state.whiteBalance}
|
||||
ratio={this.state.ratio}
|
||||
focusDepth={this.state.depth}
|
||||
androidCameraPermissionOptions={{
|
||||
title: 'Permission to use camera',
|
||||
message: 'We need your permission to use your camera',
|
||||
@ -357,13 +341,13 @@ const CameraScreen = () => {
|
||||
? RNCamera.Constants.FaceDetection.Landmarks.all
|
||||
: undefined
|
||||
}
|
||||
onFacesDetected={canDetectFaces ? facesDetected() : null}
|
||||
onTextRecognized={canDetectText ? textRecognized() : null}
|
||||
onGoogleVisionBarcodesDetected={canDetectBarcode ? barcodeRecognized() : null}
|
||||
onFacesDetected={canDetectFaces ? this.facesDetected : null}
|
||||
onTextRecognized={canDetectText ? this.textRecognized : null}
|
||||
onGoogleVisionBarcodesDetected={canDetectBarcode ? this.barcodeRecognized : null}
|
||||
>
|
||||
<View style={StyleSheet.absoluteFill}>
|
||||
<View style={[styles.autoFocusBox, drawFocusRingPosition]} />
|
||||
<TouchableWithoutFeedback onPress={() => touchToFocus()}>
|
||||
<TouchableWithoutFeedback onPress={this.touchToFocus.bind(this)}>
|
||||
<View style={{ flex: 1 }} />
|
||||
</TouchableWithoutFeedback>
|
||||
</View>
|
||||
@ -383,14 +367,14 @@ const CameraScreen = () => {
|
||||
justifyContent: 'space-around',
|
||||
}}
|
||||
>
|
||||
<TouchableOpacity style={styles.flipButton} onPress={() => toggleFacing()}>
|
||||
<TouchableOpacity style={styles.flipButton} onPress={this.toggleFacing.bind(this)}>
|
||||
<Text style={styles.flipText}> FLIP </Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={styles.flipButton} onPress={() => toggleFlash()}>
|
||||
<Text style={styles.flipText}> FLASH: { state.flash} </Text>
|
||||
<TouchableOpacity style={styles.flipButton} onPress={this.toggleFlash.bind(this)}>
|
||||
<Text style={styles.flipText}> FLASH: {this.state.flash} </Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={styles.flipButton} onPress={() => toggleWB()}>
|
||||
<Text style={styles.flipText}> WB: { state.whiteBalance} </Text>
|
||||
<TouchableOpacity style={styles.flipButton} onPress={this.toggleWB.bind(this)}>
|
||||
<Text style={styles.flipText}> WB: {this.state.whiteBalance} </Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<View
|
||||
@ -400,17 +384,17 @@ const CameraScreen = () => {
|
||||
justifyContent: 'space-around',
|
||||
}}
|
||||
>
|
||||
<TouchableOpacity onPress={()=> toggle('canDetectFaces')} style={styles.flipButton}>
|
||||
<TouchableOpacity onPress={this.toggle('canDetectFaces')} style={styles.flipButton}>
|
||||
<Text style={styles.flipText}>
|
||||
{!canDetectFaces ? 'Detect Faces' : 'Detecting Faces'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={()=> toggle('canDetectText')} style={styles.flipButton}>
|
||||
<TouchableOpacity onPress={this.toggle('canDetectText')} style={styles.flipButton}>
|
||||
<Text style={styles.flipText}>
|
||||
{!canDetectText ? 'Detect Text' : 'Detecting Text'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={()=> toggle('canDetectBarcode')} style={styles.flipButton}>
|
||||
<TouchableOpacity onPress={this.toggle('canDetectBarcode')} style={styles.flipButton}>
|
||||
<Text style={styles.flipText}>
|
||||
{!canDetectBarcode ? 'Detect Barcode' : 'Detecting Barcode'}
|
||||
</Text>
|
||||
@ -428,9 +412,9 @@ const CameraScreen = () => {
|
||||
>
|
||||
<Slider
|
||||
style={{ width: 150, marginTop: 15, alignSelf: 'flex-end' }}
|
||||
onValueChange={() => setFocusDepth()}
|
||||
onValueChange={this.setFocusDepth.bind(this)}
|
||||
step={0.1}
|
||||
disabled={ state.autoFocus === 'on'}
|
||||
disabled={this.state.autoFocus === 'on'}
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
@ -441,26 +425,10 @@ const CameraScreen = () => {
|
||||
alignSelf: 'flex-end',
|
||||
}}
|
||||
>
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.flipButton,
|
||||
{
|
||||
flex: 0.3,
|
||||
alignSelf: 'flex-end',
|
||||
backgroundColor: state.isRecording ? 'white' : 'darkred',
|
||||
},
|
||||
]}
|
||||
onPress={ state.isRecording ? () => {} : () => takeVideo()}
|
||||
>
|
||||
{ state.isRecording ? (
|
||||
<Text style={styles.flipText}> ☕ </Text>
|
||||
) : (
|
||||
<Text style={styles.flipText}> REC </Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
{this.renderRecording()}
|
||||
</View>
|
||||
{ state.zoom !== 0 && (
|
||||
<Text style={[styles.flipText, styles.zoomText]}>Zoom: { state.zoom}</Text>
|
||||
{this.state.zoom !== 0 && (
|
||||
<Text style={[styles.flipText, styles.zoomText]}>Zoom: {this.state.zoom}</Text>
|
||||
)}
|
||||
<View
|
||||
style={{
|
||||
@ -472,43 +440,42 @@ const CameraScreen = () => {
|
||||
>
|
||||
<TouchableOpacity
|
||||
style={[styles.flipButton, { flex: 0.1, alignSelf: 'flex-end' }]}
|
||||
onPress={() => zoomIn()}
|
||||
onPress={this.zoomIn.bind(this)}
|
||||
>
|
||||
<Text style={styles.flipText}> + </Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.flipButton, { flex: 0.1, alignSelf: 'flex-end' }]}
|
||||
onPress={() => zoomOut()}
|
||||
onPress={this.zoomOut.bind(this)}
|
||||
>
|
||||
<Text style={styles.flipText}> - </Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.flipButton, { flex: 0.25, alignSelf: 'flex-end' }]}
|
||||
onPress={() => toggleFocus()}
|
||||
onPress={this.toggleFocus.bind(this)}
|
||||
>
|
||||
<Text style={styles.flipText}> AF : { state.autoFocus} </Text>
|
||||
<Text style={styles.flipText}> AF : {this.state.autoFocus} </Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.flipButton, styles.picButton, { flex: 0.3, alignSelf: 'flex-end' }]}
|
||||
onPress={() => takePicture()}
|
||||
onPress={this.takePicture.bind(this)}
|
||||
>
|
||||
<Text style={styles.flipText}> SNAP </Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
{!!canDetectFaces && renderFaces()}
|
||||
{!!canDetectFaces && renderLandmarks()}
|
||||
{!!canDetectText && renderTextBlocks()}
|
||||
{!!canDetectBarcode && renderBarcodes()}
|
||||
{!!canDetectFaces && this.renderFaces()}
|
||||
{!!canDetectFaces && this.renderLandmarks()}
|
||||
{!!canDetectText && this.renderTextBlocks()}
|
||||
{!!canDetectBarcode && this.renderBarcodes()}
|
||||
</RNCamera>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<View style={styles.container}>{() => renderCamera()}</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default CameraScreen;
|
||||
render() {
|
||||
return <View style={styles.container}>{this.renderCamera()}</View>;
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
|
||||
@ -15,9 +15,14 @@ import com.android.build.OutputFile
|
||||
* // the name of the generated asset file containing your JS bundle
|
||||
* bundleAssetName: "index.android.bundle",
|
||||
*
|
||||
* // the entry file for bundle generation
|
||||
* // the entry file for bundle generation. If none specified and
|
||||
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
|
||||
* // default. Can be overridden with ENTRY_FILE environment variable.
|
||||
* entryFile: "index.android.js",
|
||||
*
|
||||
* // https://reactnative.dev/docs/performance#enable-the-ram-format
|
||||
* bundleCommand: "ram-bundle",
|
||||
*
|
||||
* // whether to bundle JS and assets in debug mode
|
||||
* bundleInDebug: false,
|
||||
*
|
||||
@ -73,7 +78,8 @@ import com.android.build.OutputFile
|
||||
*/
|
||||
|
||||
project.ext.react = [
|
||||
entryFile: "index.js"
|
||||
entryFile: "index.js",
|
||||
enableHermes: false, // clean and rebuild if changing
|
||||
]
|
||||
|
||||
apply from: "../../node_modules/react-native/react.gradle"
|
||||
@ -93,6 +99,27 @@ def enableSeparateBuildPerCPUArchitecture = false
|
||||
*/
|
||||
def enableProguardInReleaseBuilds = false
|
||||
|
||||
/**
|
||||
* The preferred build flavor of JavaScriptCore.
|
||||
*
|
||||
* For example, to use the international variant, you can use:
|
||||
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
|
||||
*
|
||||
* The international variant includes ICU i18n library and necessary data
|
||||
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
|
||||
* give correct results when using with locales other than en-US. Note that
|
||||
* this variant is about 6MiB larger per architecture than default.
|
||||
*/
|
||||
def jscFlavor = 'org.webkit:android-jsc:+'
|
||||
/**
|
||||
* Whether to enable the Hermes VM.
|
||||
*
|
||||
* This should be set on project.ext.react and mirrored here. If it is not set
|
||||
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
|
||||
* and the benefits of using Hermes will therefore be sharply reduced.
|
||||
*/
|
||||
def enableHermes = project.ext.react.get("enableHermes", false);
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
@ -113,8 +140,22 @@ android {
|
||||
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||
}
|
||||
}
|
||||
signingConfigs {
|
||||
debug {
|
||||
storeFile file('debug.keystore')
|
||||
storePassword 'android'
|
||||
keyAlias 'androiddebugkey'
|
||||
keyPassword 'android'
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
debug {
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
release {
|
||||
// Caution! In production, you need to generate your own keystore file.
|
||||
// see https://reactnative.dev/docs/signed-apk-android.
|
||||
signingConfig signingConfigs.debug
|
||||
minifyEnabled enableProguardInReleaseBuilds
|
||||
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||
}
|
||||
@ -123,8 +164,8 @@ android {
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
// For each separate APK per architecture, set a unique version code as described here:
|
||||
// http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
|
||||
def versionCodes = ["armeabi-v7a":1, "x86":2, "arm64-v8a": 3, "x86_64": 4]
|
||||
// https://developer.android.com/studio/build/configure-apk-splits.html
|
||||
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
|
||||
def abi = output.getFilter(OutputFile.ABI)
|
||||
if (abi != null) { // null for the universal-debug, universal-release variants
|
||||
output.versionCodeOverride =
|
||||
@ -135,10 +176,29 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':react-native-camera')
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
//noinspection GradleDynamicVersion
|
||||
implementation 'androidx.appcompat:appcompat:1.0.0'
|
||||
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
|
||||
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.fbjni'
|
||||
}
|
||||
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.flipper'
|
||||
exclude group:'com.squareup.okhttp3', module:'okhttp'
|
||||
}
|
||||
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.flipper'
|
||||
}
|
||||
if (enableHermes) {
|
||||
def hermesPath = "../../node_modules/hermes-engine/android/";
|
||||
debugImplementation files(hermesPath + "hermes-debug.aar")
|
||||
releaseImplementation files(hermesPath + "hermes-release.aar")
|
||||
} else {
|
||||
implementation jscFlavor
|
||||
}
|
||||
}
|
||||
|
||||
// Run this once to be able to run the application with BUCK
|
||||
@ -147,3 +207,18 @@ task copyDownloadableDepsToLibs(type: Copy) {
|
||||
from configurations.compile
|
||||
into 'libs'
|
||||
}
|
||||
|
||||
task downloadDependencies() {
|
||||
description 'Download all dependencies to the Gradle cache'
|
||||
doLast {
|
||||
configurations.findAll().each { config ->
|
||||
if (config.name.contains("minReactNative") && config.canBeResolved) {
|
||||
print config.name
|
||||
print '\n'
|
||||
config.files
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
|
||||
|
||||
BIN
examples/basic/android/app/debug.keystore
Normal file
BIN
examples/basic/android/app/debug.keystore
Normal file
Binary file not shown.
@ -9,13 +9,6 @@
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Disabling obfuscation is useful if you collect stack traces from production crashes
|
||||
# (unless you are using a system that supports de-obfuscate the stack traces).
|
||||
-dontobfuscate
|
||||
|
||||
8
examples/basic/android/app/src/debug/AndroidManifest.xml
Normal file
8
examples/basic/android/app/src/debug/AndroidManifest.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
|
||||
<application android:usesCleartextTraffic="true" tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" />
|
||||
</manifest>
|
||||
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
|
||||
* directory of this source tree.
|
||||
*/
|
||||
package com.rncameraexample;
|
||||
|
||||
import android.content.Context;
|
||||
import com.facebook.flipper.android.AndroidFlipperClient;
|
||||
import com.facebook.flipper.android.utils.FlipperUtils;
|
||||
import com.facebook.flipper.core.FlipperClient;
|
||||
import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
|
||||
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
|
||||
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
|
||||
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.modules.network.NetworkingModule;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
public class ReactNativeFlipper {
|
||||
public static void initializeFlipper(Context context, final ReactInstanceManager reactInstanceManager) {
|
||||
if (FlipperUtils.shouldEnableFlipper(context)) {
|
||||
final FlipperClient client = AndroidFlipperClient.getInstance(context);
|
||||
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
|
||||
client.addPlugin(new ReactFlipperPlugin());
|
||||
client.addPlugin(new DatabasesFlipperPlugin(context));
|
||||
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
|
||||
client.addPlugin(CrashReporterPlugin.getInstance());
|
||||
final NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
|
||||
NetworkingModule.setCustomClientBuilder(
|
||||
new NetworkingModule.CustomClientBuilder() {
|
||||
@Override
|
||||
public void apply(OkHttpClient.Builder builder) {
|
||||
builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
|
||||
}
|
||||
});
|
||||
client.addPlugin(networkFlipperPlugin);
|
||||
client.start();
|
||||
// Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
|
||||
// Hence we run if after all native modules have been initialized
|
||||
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
|
||||
if (reactContext == null) {
|
||||
reactInstanceManager.addReactInstanceEventListener(
|
||||
new ReactInstanceManager.ReactInstanceEventListener() {
|
||||
@Override
|
||||
public void onReactContextInitialized(ReactContext reactContext) {
|
||||
reactInstanceManager.removeReactInstanceEventListener(this);
|
||||
reactContext.runOnNativeModulesQueueThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
client.addPlugin(new FrescoFlipperPlugin());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
client.addPlugin(new FrescoFlipperPlugin());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,8 @@
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<application
|
||||
android:name=".MainApplication"
|
||||
@ -21,7 +23,8 @@
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
|
||||
android:launchMode="singleTask"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
@ -1,15 +1,13 @@
|
||||
package com.rncameraexample;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import android.content.Context;
|
||||
import com.facebook.react.PackageList;
|
||||
import com.facebook.react.ReactApplication;
|
||||
import org.reactnative.camera.RNCameraPackage;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.shell.MainReactPackage;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
public class MainApplication extends Application implements ReactApplication {
|
||||
@ -22,10 +20,11 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
|
||||
@Override
|
||||
protected List<ReactPackage> getPackages() {
|
||||
return Arrays.<ReactPackage>asList(
|
||||
new MainReactPackage(),
|
||||
new RNCameraPackage()
|
||||
);
|
||||
@SuppressWarnings("UnnecessaryLocalVariable")
|
||||
List<ReactPackage> packages = new PackageList(this).getPackages();
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
// packages.add(new MyReactNativePackage());
|
||||
return packages;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -43,5 +42,32 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
initializeFlipper(this); // Remove this line if you don't want Flipper enabled
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Flipper in React Native templates.
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
private static void initializeFlipper(Context context) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
try {
|
||||
/*
|
||||
We use reflection here to pick up the class that initializes Flipper,
|
||||
since Flipper library is not available in release mode
|
||||
*/
|
||||
Class<?> aClass = Class.forName("com.facebook.flipper.ReactNativeFlipper");
|
||||
aClass.getMethod("initializeFlipper", Context.class).invoke(null, context);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="android:textColor">#000000</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
@ -2,18 +2,17 @@
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
buildToolsVersion = "28.0.3"
|
||||
buildToolsVersion = "29.0.2"
|
||||
minSdkVersion = 16
|
||||
compileSdkVersion = 28
|
||||
targetSdkVersion = 28
|
||||
supportLibVersion = "28.0.0"
|
||||
compileSdkVersion = 29
|
||||
targetSdkVersion = 29
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.3.1'
|
||||
classpath('com.android.tools.build:gradle:4.0.1')
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
@ -23,13 +22,17 @@ buildscript {
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
google()
|
||||
jcenter()
|
||||
maven { url "https://jitpack.io" }
|
||||
maven {
|
||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||
url "$rootDir/../node_modules/react-native/android"
|
||||
url("$rootDir/../node_modules/react-native/android")
|
||||
}
|
||||
maven {
|
||||
// Android JSC is installed from npm
|
||||
url("$rootDir/../node_modules/jsc-android/dist")
|
||||
}
|
||||
google()
|
||||
jcenter()
|
||||
maven { url 'https://www.jitpack.io' }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -17,6 +17,8 @@ org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=1g -XX:+HeapDumpOnOutOfMemoryError -Df
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
android.useDeprecatedNdk=true
|
||||
android.enableJetifier=true
|
||||
android.useAndroidX=true
|
||||
|
||||
# Version of flipper SDK to use with React Native
|
||||
FLIPPER_VERSION=0.37.0
|
||||
|
||||
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-all.zip
|
||||
|
||||
126
examples/basic/android/gradlew
vendored
126
examples/basic/android/gradlew
vendored
@ -1,4 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
@ -6,47 +22,6 @@
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
fi
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
@ -61,9 +36,49 @@ while [ -h "$PRG" ] ; do
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >&-
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >&-
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
@ -90,7 +105,7 @@ location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
@ -114,6 +129,7 @@ fi
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
@ -154,11 +170,19 @@ if $cygwin ; then
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
rootProject.name = 'RNCameraExample'
|
||||
include ':react-native-camera'
|
||||
project(':react-native-camera').projectDir = new File(rootProject.projectDir, '../../../android')
|
||||
|
||||
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
|
||||
include ':app'
|
||||
|
||||
@ -22,6 +22,10 @@ module.exports = {
|
||||
blacklistRE: blacklist([
|
||||
new RegExp(`${reactNativeCameraRoot}/examples/mlkit/.*`),
|
||||
new RegExp(`${reactNativeCameraRoot}/node_modules/react-native/.*`),
|
||||
new RegExp(
|
||||
`${reactNativeCameraRoot}/examples/advanced/advanced/node_modules/react-native/.*`,
|
||||
),
|
||||
]),
|
||||
},
|
||||
maxWorkers: 2,
|
||||
};
|
||||
@ -4,22 +4,24 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.1.2",
|
||||
"babel": "^6.23.0",
|
||||
"react": "16.8.3",
|
||||
"react-native": "0.59.1"
|
||||
"react": "16.13.1",
|
||||
"react-native": "0.63.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-jest": "23.6.0",
|
||||
"jest": "23.6.0",
|
||||
"metro-react-native-babel-preset": "0.53.1",
|
||||
"react-test-renderer": "16.8.3"
|
||||
"metro-react-native-babel-preset": "0.59.0",
|
||||
"react-test-renderer": "16.13.1"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "react-native"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"clear": "node node_modules/react-native/local-cli/cli.js start --reset-cache",
|
||||
"start": "node node_modules/react-native/local-cli/cli.js start",
|
||||
"android": "react-native run-android",
|
||||
"clear": "react-native start --reset-cache",
|
||||
"ios": "react-native run-ios",
|
||||
"start": "react-native start",
|
||||
"test": "jest"
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -272,6 +272,9 @@ export default class CameraScreen extends React.Component {
|
||||
onTextRecognized={canDetectText ? this.textRecognized : null}
|
||||
onGoogleVisionBarcodesDetected={canDetectBarcode ? this.barcodeRecognized : null}
|
||||
googleVisionBarcodeType={RNCamera.Constants.GoogleVisionBarcodeDetection.BarcodeType.ALL}
|
||||
googleVisionBarcodeMode={
|
||||
RNCamera.Constants.GoogleVisionBarcodeDetection.BarcodeMode.ALTERNATE
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
|
||||
@ -152,4 +152,17 @@ task copyDownloadableDepsToLibs(type: Copy) {
|
||||
into 'libs'
|
||||
}
|
||||
|
||||
task downloadDependencies() {
|
||||
description 'Download all dependencies to the Gradle cache'
|
||||
doLast {
|
||||
configurations.findAll().each { config ->
|
||||
if (config.name.contains("minReactNative") && config.canBeResolved) {
|
||||
print config.name
|
||||
print '\n'
|
||||
config.files
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
|
||||
30
examples/mlkit/metro.config.js
Normal file
30
examples/mlkit/metro.config.js
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* This file overrides metro config so
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const blacklist = require('metro-config/src/defaults/blacklist');
|
||||
|
||||
const reactNativeCameraRoot = path.resolve(__dirname, '..', '..');
|
||||
|
||||
module.exports = {
|
||||
transformer: {
|
||||
getTransformOptions: async () => ({
|
||||
transform: {
|
||||
experimentalImportSupport: false,
|
||||
inlineRequires: false,
|
||||
},
|
||||
}),
|
||||
},
|
||||
watchFolders: [path.resolve(__dirname, 'node_modules'), reactNativeCameraRoot],
|
||||
resolver: {
|
||||
blacklistRE: blacklist([
|
||||
new RegExp(`${reactNativeCameraRoot}/examples/basic/.*`),
|
||||
new RegExp(`${reactNativeCameraRoot}/node_modules/react-native/.*`),
|
||||
new RegExp(
|
||||
`${reactNativeCameraRoot}/examples/advanced/advanced/node_modules/react-native/.*`,
|
||||
),
|
||||
]),
|
||||
},
|
||||
};
|
||||
@ -1,24 +1,27 @@
|
||||
{
|
||||
"name": "mlkit",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "node node_modules/react-native/local-cli/cli.js start",
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "16.8.3",
|
||||
"react-native": "0.59.1"
|
||||
"react": "16.13.1",
|
||||
"react-native": "0.63.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.6.2",
|
||||
"@babel/runtime": "^7.6.2",
|
||||
"babel-jest": "^24.9.0",
|
||||
"jest": "^24.9.0",
|
||||
"metro-react-native-babel-preset": "^0.56.1",
|
||||
"react-test-renderer": "16.8.3"
|
||||
"metro-react-native-babel-preset": "0.59.0",
|
||||
"react-test-renderer": "16.13.1"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "react-native"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"android": "react-native run-android",
|
||||
"clear": "react-native start --reset-cache",
|
||||
"ios": "react-native run-ios",
|
||||
"start": "react-native start",
|
||||
"test": "jest"
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
6
examples/tests/.buckconfig
Normal file
6
examples/tests/.buckconfig
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
[android]
|
||||
target = Google Inc.:Google APIs:23
|
||||
|
||||
[maven_repositories]
|
||||
central = https://repo1.maven.org/maven2
|
||||
4
examples/tests/.eslintrc.js
Normal file
4
examples/tests/.eslintrc.js
Normal file
@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: '@react-native-community',
|
||||
};
|
||||
74
examples/tests/.flowconfig
Normal file
74
examples/tests/.flowconfig
Normal file
@ -0,0 +1,74 @@
|
||||
[ignore]
|
||||
; We fork some components by platform
|
||||
.*/*[.]android.js
|
||||
|
||||
; Ignore "BUCK" generated dirs
|
||||
<PROJECT_ROOT>/\.buckd/
|
||||
|
||||
; Ignore polyfills
|
||||
node_modules/react-native/Libraries/polyfills/.*
|
||||
|
||||
; These should not be required directly
|
||||
; require from fbjs/lib instead: require('fbjs/lib/warning')
|
||||
node_modules/warning/.*
|
||||
|
||||
; Flow doesn't support platforms
|
||||
.*/Libraries/Utilities/LoadingView.js
|
||||
|
||||
[untyped]
|
||||
.*/node_modules/@react-native-community/cli/.*/.*
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
node_modules/react-native/interface.js
|
||||
node_modules/react-native/flow/
|
||||
|
||||
[options]
|
||||
emoji=true
|
||||
|
||||
esproposal.optional_chaining=enable
|
||||
esproposal.nullish_coalescing=enable
|
||||
|
||||
module.file_ext=.js
|
||||
module.file_ext=.json
|
||||
module.file_ext=.ios.js
|
||||
|
||||
munge_underscores=true
|
||||
|
||||
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/node_modules/react-native/\1'
|
||||
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/node_modules/react-native/Libraries/Image/RelativeImageStub'
|
||||
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
suppress_type=$FlowFixMeProps
|
||||
suppress_type=$FlowFixMeState
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
|
||||
|
||||
[lints]
|
||||
sketchy-null-number=warn
|
||||
sketchy-null-mixed=warn
|
||||
sketchy-number=warn
|
||||
untyped-type-import=warn
|
||||
nonstrict-import=warn
|
||||
deprecated-type=warn
|
||||
unsafe-getters-setters=warn
|
||||
inexact-spread=warn
|
||||
unnecessary-invariant=warn
|
||||
signature-verification-failure=warn
|
||||
deprecated-utility=error
|
||||
|
||||
[strict]
|
||||
deprecated-type
|
||||
nonstrict-import
|
||||
sketchy-null
|
||||
unclear-type
|
||||
unsafe-getters-setters
|
||||
untyped-import
|
||||
untyped-type-import
|
||||
|
||||
[version]
|
||||
^0.113.0
|
||||
1
examples/tests/.gitattributes
vendored
Normal file
1
examples/tests/.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.pbxproj -text
|
||||
59
examples/tests/.gitignore
vendored
Normal file
59
examples/tests/.gitignore
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
# OSX
|
||||
#
|
||||
.DS_Store
|
||||
|
||||
# Xcode
|
||||
#
|
||||
build/
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
*.hmap
|
||||
*.ipa
|
||||
*.xcuserstate
|
||||
|
||||
# Android/IntelliJ
|
||||
#
|
||||
build/
|
||||
.idea
|
||||
.gradle
|
||||
local.properties
|
||||
*.iml
|
||||
|
||||
# node.js
|
||||
#
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# BUCK
|
||||
buck-out/
|
||||
\.buckd/
|
||||
*.keystore
|
||||
!debug.keystore
|
||||
|
||||
# fastlane
|
||||
#
|
||||
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
|
||||
# screenshots whenever they are needed.
|
||||
# For more information about the recommended setup visit:
|
||||
# https://docs.fastlane.tools/best-practices/source-control/
|
||||
|
||||
*/fastlane/report.xml
|
||||
*/fastlane/Preview.html
|
||||
*/fastlane/screenshots
|
||||
|
||||
# Bundle artifact
|
||||
*.jsbundle
|
||||
|
||||
# CocoaPods
|
||||
/ios/Pods/
|
||||
6
examples/tests/.prettierrc.js
Normal file
6
examples/tests/.prettierrc.js
Normal file
@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
bracketSpacing: false,
|
||||
jsxBracketSameLine: true,
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
};
|
||||
1
examples/tests/.watchmanconfig
Normal file
1
examples/tests/.watchmanconfig
Normal file
@ -0,0 +1 @@
|
||||
{}
|
||||
21
examples/tests/App.js
Normal file
21
examples/tests/App.js
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Sample React Native App
|
||||
* https://github.com/facebook/react-native
|
||||
*
|
||||
* @format
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {SafeAreaView} from 'react-native';
|
||||
import TestSuite from 'react-native-tests';
|
||||
|
||||
import {tests} from './tests';
|
||||
|
||||
const App = () => (
|
||||
<SafeAreaView>
|
||||
<TestSuite tests={tests} />
|
||||
</SafeAreaView>
|
||||
);
|
||||
|
||||
export default App;
|
||||
55
examples/tests/android/app/_BUCK
Normal file
55
examples/tests/android/app/_BUCK
Normal file
@ -0,0 +1,55 @@
|
||||
# To learn about Buck see [Docs](https://buckbuild.com/).
|
||||
# To run your application with Buck:
|
||||
# - install Buck
|
||||
# - `npm start` - to start the packager
|
||||
# - `cd android`
|
||||
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
|
||||
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
|
||||
# - `buck install -r android/app` - compile, install and run application
|
||||
#
|
||||
|
||||
load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
|
||||
|
||||
lib_deps = []
|
||||
|
||||
create_aar_targets(glob(["libs/*.aar"]))
|
||||
|
||||
create_jar_targets(glob(["libs/*.jar"]))
|
||||
|
||||
android_library(
|
||||
name = "all-libs",
|
||||
exported_deps = lib_deps,
|
||||
)
|
||||
|
||||
android_library(
|
||||
name = "app-code",
|
||||
srcs = glob([
|
||||
"src/main/java/**/*.java",
|
||||
]),
|
||||
deps = [
|
||||
":all-libs",
|
||||
":build_config",
|
||||
":res",
|
||||
],
|
||||
)
|
||||
|
||||
android_build_config(
|
||||
name = "build_config",
|
||||
package = "com.tests",
|
||||
)
|
||||
|
||||
android_resource(
|
||||
name = "res",
|
||||
package = "com.tests",
|
||||
res = "src/main/res",
|
||||
)
|
||||
|
||||
android_binary(
|
||||
name = "app",
|
||||
keystore = "//android/keystores:debug",
|
||||
manifest = "src/main/AndroidManifest.xml",
|
||||
package_type = "debug",
|
||||
deps = [
|
||||
":app-code",
|
||||
],
|
||||
)
|
||||
239
examples/tests/android/app/build.gradle
Normal file
239
examples/tests/android/app/build.gradle
Normal file
@ -0,0 +1,239 @@
|
||||
apply plugin: "com.android.application"
|
||||
|
||||
import com.android.build.OutputFile
|
||||
|
||||
/**
|
||||
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
|
||||
* and bundleReleaseJsAndAssets).
|
||||
* These basically call `react-native bundle` with the correct arguments during the Android build
|
||||
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
|
||||
* bundle directly from the development server. Below you can see all the possible configurations
|
||||
* and their defaults. If you decide to add a configuration block, make sure to add it before the
|
||||
* `apply from: "../../node_modules/react-native/react.gradle"` line.
|
||||
*
|
||||
* project.ext.react = [
|
||||
* // the name of the generated asset file containing your JS bundle
|
||||
* bundleAssetName: "index.android.bundle",
|
||||
*
|
||||
* // the entry file for bundle generation. If none specified and
|
||||
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
|
||||
* // default. Can be overridden with ENTRY_FILE environment variable.
|
||||
* entryFile: "index.android.js",
|
||||
*
|
||||
* // https://facebook.github.io/react-native/docs/performance#enable-the-ram-format
|
||||
* bundleCommand: "ram-bundle",
|
||||
*
|
||||
* // whether to bundle JS and assets in debug mode
|
||||
* bundleInDebug: false,
|
||||
*
|
||||
* // whether to bundle JS and assets in release mode
|
||||
* bundleInRelease: true,
|
||||
*
|
||||
* // whether to bundle JS and assets in another build variant (if configured).
|
||||
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
|
||||
* // The configuration property can be in the following formats
|
||||
* // 'bundleIn${productFlavor}${buildType}'
|
||||
* // 'bundleIn${buildType}'
|
||||
* // bundleInFreeDebug: true,
|
||||
* // bundleInPaidRelease: true,
|
||||
* // bundleInBeta: true,
|
||||
*
|
||||
* // whether to disable dev mode in custom build variants (by default only disabled in release)
|
||||
* // for example: to disable dev mode in the staging build type (if configured)
|
||||
* devDisabledInStaging: true,
|
||||
* // The configuration property can be in the following formats
|
||||
* // 'devDisabledIn${productFlavor}${buildType}'
|
||||
* // 'devDisabledIn${buildType}'
|
||||
*
|
||||
* // the root of your project, i.e. where "package.json" lives
|
||||
* root: "../../",
|
||||
*
|
||||
* // where to put the JS bundle asset in debug mode
|
||||
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
|
||||
*
|
||||
* // where to put the JS bundle asset in release mode
|
||||
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
|
||||
*
|
||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||
* // require('./image.png')), in debug mode
|
||||
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
|
||||
*
|
||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||
* // require('./image.png')), in release mode
|
||||
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
|
||||
*
|
||||
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
|
||||
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
|
||||
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
|
||||
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
|
||||
* // for example, you might want to remove it from here.
|
||||
* inputExcludes: ["android/**", "ios/**"],
|
||||
*
|
||||
* // override which node gets called and with what additional arguments
|
||||
* nodeExecutableAndArgs: ["node"],
|
||||
*
|
||||
* // supply additional arguments to the packager
|
||||
* extraPackagerArgs: []
|
||||
* ]
|
||||
*/
|
||||
|
||||
project.ext.react = [
|
||||
enableHermes: false, // clean and rebuild if changing
|
||||
]
|
||||
|
||||
apply from: "../../node_modules/react-native/react.gradle"
|
||||
|
||||
/**
|
||||
* Set this to true to create two separate APKs instead of one:
|
||||
* - An APK that only works on ARM devices
|
||||
* - An APK that only works on x86 devices
|
||||
* The advantage is the size of the APK is reduced by about 4MB.
|
||||
* Upload all the APKs to the Play Store and people will download
|
||||
* the correct one based on the CPU architecture of their device.
|
||||
*/
|
||||
def enableSeparateBuildPerCPUArchitecture = false
|
||||
|
||||
/**
|
||||
* Run Proguard to shrink the Java bytecode in release builds.
|
||||
*/
|
||||
def enableProguardInReleaseBuilds = false
|
||||
|
||||
/**
|
||||
* The preferred build flavor of JavaScriptCore.
|
||||
*
|
||||
* For example, to use the international variant, you can use:
|
||||
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
|
||||
*
|
||||
* The international variant includes ICU i18n library and necessary data
|
||||
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
|
||||
* give correct results when using with locales other than en-US. Note that
|
||||
* this variant is about 6MiB larger per architecture than default.
|
||||
*/
|
||||
def jscFlavor = 'org.webkit:android-jsc:+'
|
||||
|
||||
/**
|
||||
* Whether to enable the Hermes VM.
|
||||
*
|
||||
* This should be set on project.ext.react and mirrored here. If it is not set
|
||||
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
|
||||
* and the benefits of using Hermes will therefore be sharply reduced.
|
||||
*/
|
||||
def enableHermes = project.ext.react.get("enableHermes", false);
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.tests"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
missingDimensionStrategy 'react-native-camera', 'general' // <--- insert this line
|
||||
}
|
||||
splits {
|
||||
abi {
|
||||
reset()
|
||||
enable enableSeparateBuildPerCPUArchitecture
|
||||
universalApk false // If true, also generate a universal APK
|
||||
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||
}
|
||||
}
|
||||
signingConfigs {
|
||||
debug {
|
||||
storeFile file('debug.keystore')
|
||||
storePassword 'android'
|
||||
keyAlias 'androiddebugkey'
|
||||
keyPassword 'android'
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
debug {
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
release {
|
||||
// Caution! In production, you need to generate your own keystore file.
|
||||
// see https://facebook.github.io/react-native/docs/signed-apk-android.
|
||||
signingConfig signingConfigs.debug
|
||||
minifyEnabled enableProguardInReleaseBuilds
|
||||
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||
}
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
pickFirst "lib/armeabi-v7a/libc++_shared.so"
|
||||
pickFirst "lib/arm64-v8a/libc++_shared.so"
|
||||
pickFirst "lib/x86/libc++_shared.so"
|
||||
pickFirst "lib/x86_64/libc++_shared.so"
|
||||
}
|
||||
|
||||
// applicationVariants are e.g. debug, release
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
// For each separate APK per architecture, set a unique version code as described here:
|
||||
// https://developer.android.com/studio/build/configure-apk-splits.html
|
||||
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
|
||||
def abi = output.getFilter(OutputFile.ABI)
|
||||
if (abi != null) { // null for the universal-debug, universal-release variants
|
||||
output.versionCodeOverride =
|
||||
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
//noinspection GradleDynamicVersion
|
||||
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
|
||||
|
||||
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.fbjni'
|
||||
}
|
||||
|
||||
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.flipper'
|
||||
}
|
||||
|
||||
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.flipper'
|
||||
}
|
||||
|
||||
if (enableHermes) {
|
||||
def hermesPath = "../../node_modules/hermes-engine/android/";
|
||||
debugImplementation files(hermesPath + "hermes-debug.aar")
|
||||
releaseImplementation files(hermesPath + "hermes-release.aar")
|
||||
} else {
|
||||
implementation jscFlavor
|
||||
}
|
||||
}
|
||||
|
||||
// Run this once to be able to run the application with BUCK
|
||||
// puts all compile dependencies into folder libs for BUCK to use
|
||||
task copyDownloadableDepsToLibs(type: Copy) {
|
||||
from configurations.compile
|
||||
into 'libs'
|
||||
}
|
||||
|
||||
task downloadDependencies() {
|
||||
description 'Download all dependencies to the Gradle cache'
|
||||
doLast {
|
||||
configurations.findAll().each { config ->
|
||||
if (config.name.contains("minReactNative") && config.canBeResolved) {
|
||||
print config.name
|
||||
print '\n'
|
||||
config.files
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
|
||||
19
examples/tests/android/app/build_defs.bzl
Normal file
19
examples/tests/android/app/build_defs.bzl
Normal file
@ -0,0 +1,19 @@
|
||||
"""Helper definitions to glob .aar and .jar targets"""
|
||||
|
||||
def create_aar_targets(aarfiles):
|
||||
for aarfile in aarfiles:
|
||||
name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
|
||||
lib_deps.append(":" + name)
|
||||
android_prebuilt_aar(
|
||||
name = name,
|
||||
aar = aarfile,
|
||||
)
|
||||
|
||||
def create_jar_targets(jarfiles):
|
||||
for jarfile in jarfiles:
|
||||
name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
|
||||
lib_deps.append(":" + name)
|
||||
prebuilt_jar(
|
||||
name = name,
|
||||
binary_jar = jarfile,
|
||||
)
|
||||
BIN
examples/tests/android/app/debug.keystore
Normal file
BIN
examples/tests/android/app/debug.keystore
Normal file
Binary file not shown.
10
examples/tests/android/app/proguard-rules.pro
vendored
Normal file
10
examples/tests/android/app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
8
examples/tests/android/app/src/debug/AndroidManifest.xml
Normal file
8
examples/tests/android/app/src/debug/AndroidManifest.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
|
||||
<application android:usesCleartextTraffic="true" tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" />
|
||||
</manifest>
|
||||
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
|
||||
* directory of this source tree.
|
||||
*/
|
||||
package com.tests;
|
||||
|
||||
import android.content.Context;
|
||||
import com.facebook.flipper.android.AndroidFlipperClient;
|
||||
import com.facebook.flipper.android.utils.FlipperUtils;
|
||||
import com.facebook.flipper.core.FlipperClient;
|
||||
import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
|
||||
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
|
||||
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
|
||||
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.modules.network.NetworkingModule;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
public class ReactNativeFlipper {
|
||||
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
|
||||
if (FlipperUtils.shouldEnableFlipper(context)) {
|
||||
final FlipperClient client = AndroidFlipperClient.getInstance(context);
|
||||
|
||||
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
|
||||
client.addPlugin(new ReactFlipperPlugin());
|
||||
client.addPlugin(new DatabasesFlipperPlugin(context));
|
||||
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
|
||||
client.addPlugin(CrashReporterPlugin.getInstance());
|
||||
|
||||
NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
|
||||
NetworkingModule.setCustomClientBuilder(
|
||||
new NetworkingModule.CustomClientBuilder() {
|
||||
@Override
|
||||
public void apply(OkHttpClient.Builder builder) {
|
||||
builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
|
||||
}
|
||||
});
|
||||
client.addPlugin(networkFlipperPlugin);
|
||||
client.start();
|
||||
|
||||
// Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
|
||||
// Hence we run if after all native modules have been initialized
|
||||
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
|
||||
if (reactContext == null) {
|
||||
reactInstanceManager.addReactInstanceEventListener(
|
||||
new ReactInstanceManager.ReactInstanceEventListener() {
|
||||
@Override
|
||||
public void onReactContextInitialized(ReactContext reactContext) {
|
||||
reactInstanceManager.removeReactInstanceEventListener(this);
|
||||
reactContext.runOnNativeModulesQueueThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
client.addPlugin(new FrescoFlipperPlugin());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
client.addPlugin(new FrescoFlipperPlugin());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user