mirror of
https://github.com/apache/cordova-plugin-camera.git
synced 2026-02-14 00:04:54 +08:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24839eb71f | ||
|
|
64bd32d641 | ||
|
|
0796f784c1 | ||
|
|
20293f3d64 | ||
|
|
8cb34e1175 | ||
|
|
505ccefb4c | ||
|
|
61a6e9bb44 | ||
|
|
c2eb21d201 | ||
|
|
23642f09b5 | ||
|
|
84166f6355 | ||
|
|
2c09ade500 | ||
|
|
827bb611ee | ||
|
|
d0545c879f | ||
|
|
d0d46c151c | ||
|
|
a18fda7ddf | ||
|
|
3e548770b7 | ||
|
|
4608f8ef80 | ||
|
|
53223c3df2 | ||
|
|
bf12b39d18 | ||
|
|
ed216ce714 | ||
|
|
879712028a | ||
|
|
204234b1b9 | ||
|
|
0fba37cac3 | ||
|
|
5b8263732a | ||
|
|
869f02da1a | ||
|
|
e9db20e381 | ||
|
|
c7971d9f63 | ||
|
|
3112e5fb15 | ||
|
|
c56a255fe8 | ||
|
|
0227cdcf14 | ||
|
|
75bf807261 | ||
|
|
abfbbd35d5 | ||
|
|
59cf76d1da | ||
|
|
4ee90a84f3 | ||
|
|
5587bec320 | ||
|
|
d523142cf2 | ||
|
|
0f6435ec73 | ||
|
|
e81e02b21f | ||
|
|
1e20a9bd07 | ||
|
|
d356030070 | ||
|
|
7bc311fba9 |
@@ -1,33 +0,0 @@
|
||||
# appveyor file
|
||||
# http://www.appveyor.com/docs/appveyor-yml
|
||||
|
||||
max_jobs: 1
|
||||
|
||||
shallow_clone: true
|
||||
|
||||
init:
|
||||
- git config --global core.autocrlf true
|
||||
|
||||
image:
|
||||
- Visual Studio 2017
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: "10"
|
||||
- nodejs_version: "12"
|
||||
- nodejs_version: "14"
|
||||
|
||||
platform:
|
||||
- x86
|
||||
- x64
|
||||
|
||||
install:
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
- node --version
|
||||
- npm install -g github:apache/cordova-paramedic
|
||||
- npm install -g cordova
|
||||
|
||||
build: off
|
||||
|
||||
test_script:
|
||||
- cordova-paramedic --config pr\windows-10-store --plugin . --justBuild
|
||||
24
.asf.yaml
24
.asf.yaml
@@ -15,6 +15,30 @@
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
github:
|
||||
description: Apache Cordova Plugin camera
|
||||
homepage: https://cordova.apache.org/
|
||||
|
||||
labels:
|
||||
- cordova
|
||||
- mobile
|
||||
- javascript
|
||||
- nodejs
|
||||
- hacktoberfest
|
||||
- java
|
||||
- objective-c
|
||||
- cordova-plugin
|
||||
|
||||
features:
|
||||
wiki: false
|
||||
issues: true
|
||||
projects: true
|
||||
|
||||
enabled_merge_buttons:
|
||||
squash: true
|
||||
merge: false
|
||||
rebase: false
|
||||
|
||||
notifications:
|
||||
commits: commits@cordova.apache.org
|
||||
issues: issues@cordova.apache.org
|
||||
|
||||
7
.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md
vendored
7
.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md
vendored
@@ -13,7 +13,8 @@ For usage and support questions, please check out the resources below. Thanks!
|
||||
|
||||
You can get answers to your usage and support questions about **Apache Cordova** on:
|
||||
|
||||
* Slack Community Chat: https://cordova.slack.com (you can sign-up at http://slack.cordova.io/)
|
||||
* GitHub Discussions: https://github.com/apache/cordova/discussions
|
||||
* Slack Community Chat: https://cordova.slack.com (you can sign-up at https://s.apache.org/cordova-slack)
|
||||
* StackOverflow: https://stackoverflow.com/questions/tagged/cordova using the tag `cordova`
|
||||
|
||||
---
|
||||
@@ -22,6 +23,4 @@ If you are using a tool that uses Cordova internally, like e.g. Ionic, check the
|
||||
|
||||
* **Ionic Framework**
|
||||
* [Ionic Community Forum](https://forum.ionicframework.com/)
|
||||
* [Ionic Worldwide Slack](https://ionicworldwide.herokuapp.com/)
|
||||
* **PhoneGap**
|
||||
* [PhoneGap Developer Community](https://forums.adobe.com/community/phonegap)
|
||||
* [Ionic Discord](https://ionic.link/discord)
|
||||
137
.github/workflows/android.yml
vendored
Normal file
137
.github/workflows/android.yml
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you 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.
|
||||
|
||||
name: Android Testsuite
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'LICENSE'
|
||||
- '.eslint*'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'LICENSE'
|
||||
- '.eslint*'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Android ${{ matrix.versions.android }} Test
|
||||
runs-on: macos-latest
|
||||
continue-on-error: true
|
||||
|
||||
# hoist configurations to top that are expected to be updated
|
||||
env:
|
||||
# Storing a copy of the repo
|
||||
repo: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
|
||||
|
||||
node-version: 16
|
||||
|
||||
# These are the default Java configurations used by most tests.
|
||||
# To customize these options, add "java-distro" or "java-version" to the strategy matrix with its overriding value.
|
||||
default_java-distro: temurin
|
||||
default_java-version: 11
|
||||
|
||||
# These are the default Android System Image configurations used by most tests.
|
||||
# To customize these options, add "system-image-arch" or "system-image-target" to the strategy matrix with its overriding value.
|
||||
default_system-image-arch: x86_64
|
||||
default_system-image-target: google_apis # Most system images have a google_api option. Set this as default.
|
||||
|
||||
# configurations for each testing strategy (test matrix)
|
||||
strategy:
|
||||
matrix:
|
||||
versions:
|
||||
# Test the lowest minimum supported APIs
|
||||
- android: 7
|
||||
android-api: 24
|
||||
|
||||
# Test the last 3-4 supported APIs
|
||||
- android: 10
|
||||
android-api: 29
|
||||
|
||||
- android: 11
|
||||
android-api: 30
|
||||
|
||||
- android: 12L
|
||||
android-api: 32
|
||||
|
||||
- android: 13
|
||||
android-api: 33
|
||||
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.node-version }}
|
||||
- uses: actions/setup-java@v3
|
||||
env:
|
||||
java-version: ${{ matrix.versions.java-version == '' && env.default_java-version || matrix.versions.java-version }}
|
||||
java-distro: ${{ matrix.versions.java-distro == '' && env.default_java-distro || matrix.versions.java-distro }}
|
||||
with:
|
||||
distribution: ${{ env.java-distro }}
|
||||
java-version: ${{ env.java-version }}
|
||||
|
||||
- name: Run Environment Information
|
||||
run: |
|
||||
node --version
|
||||
npm --version
|
||||
java -version
|
||||
|
||||
- name: Run npm install
|
||||
run: |
|
||||
export PATH="/usr/local/lib/android/sdk/platform-tools":$PATH
|
||||
export JAVA_HOME=$JAVA_HOME_11_X64
|
||||
npm i -g cordova@latest
|
||||
npm ci
|
||||
|
||||
- name: Run paramedic install
|
||||
if: ${{ endswith(env.repo, '/cordova-paramedic') != true }}
|
||||
run: npm i -g github:apache/cordova-paramedic
|
||||
|
||||
- uses: reactivecircus/android-emulator-runner@d94c3fbe4fe6a29e4a5ba47c12fb47677c73656b
|
||||
env:
|
||||
system-image-arch: ${{ matrix.versions.system-image-arch == '' && env.default_system-image-arch || matrix.versions.system-image-arch }}
|
||||
system-image-target: ${{ matrix.versions.system-image-target == '' && env.default_system-image-target || matrix.versions.system-image-target }}
|
||||
with:
|
||||
api-level: ${{ matrix.versions.android-api }}
|
||||
target: ${{ env.system-image-target }}
|
||||
arch: ${{ env.system-image-arch }}
|
||||
force-avd-creation: false
|
||||
disable-animations: false
|
||||
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim
|
||||
script: echo "Pregenerate the AVD before running Paramedic"
|
||||
|
||||
- name: Run paramedic tests
|
||||
uses: reactivecircus/android-emulator-runner@d94c3fbe4fe6a29e4a5ba47c12fb47677c73656b
|
||||
env:
|
||||
system-image-arch: ${{ matrix.versions.system-image-arch == '' && env.default_system-image-arch || matrix.versions.system-image-arch }}
|
||||
system-image-target: ${{ matrix.versions.system-image-target == '' && env.default_system-image-target || matrix.versions.system-image-target }}
|
||||
test_config: 'android-${{ matrix.versions.android }}.config.json'
|
||||
# Generally, this should automatically work for cordova-paramedic & plugins. If the path is unique, this can be manually changed.
|
||||
test_plugin_path: ${{ endswith(env.repo, '/cordova-paramedic') && './spec/testable-plugin/' || './' }}
|
||||
paramedic: ${{ endswith(env.repo, '/cordova-paramedic') && 'node main.js' || 'cordova-paramedic' }}
|
||||
with:
|
||||
api-level: ${{ matrix.versions.android-api }}
|
||||
target: ${{ env.system-image-target }}
|
||||
arch: ${{ env.system-image-arch }}
|
||||
force-avd-creation: false
|
||||
disable-animations: false
|
||||
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim
|
||||
script: ${{ env.paramedic }} --config ./pr/local/${{ env.test_config }} --plugin ${{ env.test_plugin_path }}
|
||||
73
.github/workflows/chrome.yml
vendored
Normal file
73
.github/workflows/chrome.yml
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you 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.
|
||||
|
||||
name: Chrome Testsuite
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'LICENSE'
|
||||
- '.eslint*'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'LICENSE'
|
||||
- '.eslint*'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Chrome Latest Test
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# hoist configurations to top that are expected to be updated
|
||||
env:
|
||||
# Storing a copy of the repo
|
||||
repo: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
|
||||
|
||||
node-version: 16
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.node-version }}
|
||||
|
||||
- name: Run install xvfb
|
||||
run: sudo apt-get install xvfb
|
||||
|
||||
- name: Run Environment Information
|
||||
run: |
|
||||
node --version
|
||||
npm --version
|
||||
|
||||
- name: Run npm install
|
||||
run: |
|
||||
npm i -g cordova@latest
|
||||
npm ci
|
||||
|
||||
- name: Run paramedic install
|
||||
if: ${{ endswith(env.repo, '/cordova-paramedic') != true }}
|
||||
run: npm i -g github:apache/cordova-paramedic
|
||||
|
||||
- name: Run paramedic tests
|
||||
env:
|
||||
test_config: 'browser.config.json'
|
||||
# Generally, this should automatically work for cordova-paramedic & plugins. If the path is unique, this can be manually changed.
|
||||
test_plugin_path: ${{ endswith(env.repo, '/cordova-paramedic') && './spec/testable-plugin/' || './' }}
|
||||
paramedic: ${{ endswith(env.repo, '/cordova-paramedic') && 'node main.js' || 'cordova-paramedic' }}
|
||||
run: xvfb-run --auto-servernum ${{ env.paramedic }} --config ./pr/local/${{ env.test_config }} --plugin ${{ env.test_plugin_path }}
|
||||
101
.github/workflows/ios.yml
vendored
Normal file
101
.github/workflows/ios.yml
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you 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.
|
||||
|
||||
name: iOS Testsuite
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'LICENSE'
|
||||
- '.eslint*'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'LICENSE'
|
||||
- '.eslint*'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: iOS ${{ matrix.versions.ios-version }} Test
|
||||
runs-on: ${{ matrix.versions.os-version }}
|
||||
continue-on-error: true
|
||||
|
||||
# hoist configurations to top that are expected to be updated
|
||||
env:
|
||||
# Storing a copy of the repo
|
||||
repo: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
|
||||
|
||||
node-version: 16
|
||||
|
||||
# > Starting April 26, 2021, all iOS and iPadOS apps submitted to the App Store must be built with Xcode 12 and the iOS 14 SDK.
|
||||
# Because of Apple's requirement, listed above, We will only be using the latest Xcode release for testing.
|
||||
# To customize these options, add "xcode-version" to the strategy matrix with its overriding value.
|
||||
default_xcode-version: latest-stable
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
versions:
|
||||
- os-version: macos-11
|
||||
ios-version: 13.x
|
||||
xcode-version: 11.x
|
||||
|
||||
- os-version: macos-11
|
||||
ios-version: 14.x
|
||||
xcode-version: 12.x
|
||||
|
||||
- os-version: macos-11
|
||||
ios-version: 15.x
|
||||
xcode-version: 13.x
|
||||
|
||||
- os-version: macos-12
|
||||
ios-version: 16.x
|
||||
xcode-version: 14.x
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.node-version }}
|
||||
- uses: maxim-lobanov/setup-xcode@9a697e2b393340c3cacd97468baa318e4c883d98
|
||||
env:
|
||||
xcode-version: ${{ matrix.versions.xcode-version == '' && env.default_xcode-version || matrix.versions.xcode-version }}
|
||||
with:
|
||||
xcode-version: ${{ env.xcode-version }}
|
||||
|
||||
- name: Run Environment Information
|
||||
run: |
|
||||
node --version
|
||||
npm --version
|
||||
xcodebuild -version
|
||||
|
||||
- name: Run npm install
|
||||
run: |
|
||||
npm i -g cordova@latest ios-deploy@latest
|
||||
npm ci
|
||||
|
||||
- name: Run paramedic install
|
||||
if: ${{ endswith(env.repo, '/cordova-paramedic') != true }}
|
||||
run: npm i -g github:apache/cordova-paramedic
|
||||
|
||||
- name: Run paramedic tests
|
||||
env:
|
||||
test_config: 'ios-${{ matrix.versions.ios-version }}.config.json'
|
||||
# Generally, this should automatically work for cordova-paramedic & plugins. If the path is unique, this can be manually changed.
|
||||
test_plugin_path: ${{ endswith(env.repo, '/cordova-paramedic') && './spec/testable-plugin/' || './' }}
|
||||
paramedic: ${{ endswith(env.repo, '/cordova-paramedic') && 'node main.js' || 'cordova-paramedic' }}
|
||||
run: ${{ env.paramedic }} --config ./pr/local/${{ env.test_config }} --plugin ${{ env.test_plugin_path }}
|
||||
56
.github/workflows/lint.yml
vendored
Normal file
56
.github/workflows/lint.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you 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.
|
||||
|
||||
name: Lint Test
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '**.js'
|
||||
- '.eslint*'
|
||||
- '.github/workflow/lint.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.js'
|
||||
- '.eslint*'
|
||||
- '.github/workflow/lint.yml'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Lint Test
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
node-version: 16
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.node-version }}
|
||||
|
||||
- name: Run Environment Information
|
||||
run: |
|
||||
node --version
|
||||
npm --version
|
||||
|
||||
- name: Run npm install
|
||||
run: |
|
||||
npm ci
|
||||
|
||||
- name: Run lint test
|
||||
run: |
|
||||
npm run lint
|
||||
13
.github/workflows/release-notify.yml
vendored
Normal file
13
.github/workflows/release-notify.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
name: Close issue asking for release
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
action-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: niklasmerz/release-notify@master
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,3 +1,2 @@
|
||||
.*
|
||||
appveyor.yml
|
||||
tests
|
||||
|
||||
118
.travis.yml
118
.travis.yml
@@ -1,118 +0,0 @@
|
||||
# This Travis configuration file is built after a Cordova Paramedic
|
||||
# specific template with minimal modifications and adaptations:
|
||||
# https://github.com/apache/cordova-paramedic/blob/master/.travis.yml
|
||||
|
||||
sudo: false
|
||||
|
||||
addons:
|
||||
jwt:
|
||||
# SAUCE_ACCESS_KEY
|
||||
secure: QivPLlqTVvOo3TJeHxuBOfxU6lho1I0IxQ3b68yntkEQQJko6kzleXHfgjf0a8aw8m38E3+fxaBWF1bGyucGwOLDWY8Ddt2P2xg44zdXH5EXHd9oIqAgngIdzLvUtH3Db2TbQEtIGOkrnNR2STovjqB7vHGLASQrgs4oL7r32/s=
|
||||
|
||||
env:
|
||||
global:
|
||||
- SAUCE_USERNAME=snay
|
||||
- TRAVIS_NODE_VERSION=12
|
||||
- ANDROID_API_LEVEL=29
|
||||
- ANDROID_BUILD_TOOLS_VERSION=29.0.2
|
||||
|
||||
language: node_js
|
||||
node_js: 14
|
||||
|
||||
# yaml anchor/alias: https://medium.com/@tommyvn/travis-yml-dry-with-anchors-8b6a3ac1b027
|
||||
|
||||
_ios: &_ios
|
||||
os: osx
|
||||
osx_image: xcode11.6
|
||||
|
||||
_android: &_android
|
||||
language: android
|
||||
os: linux
|
||||
jdk: oraclejdk8
|
||||
android:
|
||||
components:
|
||||
- tools
|
||||
- build-tools-$ANDROID_BUILD_TOOLS_VERSION
|
||||
- android-$ANDROID_API_LEVEL
|
||||
licenses:
|
||||
- "android-sdk-preview-license-.+"
|
||||
- "android-sdk-license-.+"
|
||||
- "google-gdk-license-.+"
|
||||
|
||||
matrix:
|
||||
include:
|
||||
# additional tests
|
||||
- env: ADDITIONAL_TESTS_DIR=./tests/ios
|
||||
os: osx
|
||||
osx_image: xcode11.5
|
||||
|
||||
# local tests, without saucelabs
|
||||
- env: PLATFORM=local/browser
|
||||
<<: *_ios
|
||||
- env: PLATFORM=local/ios-10.0
|
||||
<<: *_ios
|
||||
|
||||
# many tests with saucelabs
|
||||
- env: PLATFORM=browser-chrome
|
||||
- env: PLATFORM=browser-firefox
|
||||
- env: PLATFORM=browser-safari
|
||||
- env: PLATFORM=browser-edge
|
||||
|
||||
- env: PLATFORM=ios-11.3
|
||||
<<: *_ios
|
||||
- env: PLATFORM=ios-12.0
|
||||
<<: *_ios
|
||||
- env: PLATFORM=ios-12.2
|
||||
<<: *_ios
|
||||
|
||||
- env: PLATFORM=android-5.1
|
||||
<<: *_android
|
||||
- env: PLATFORM=android-6.0
|
||||
<<: *_android
|
||||
- env: PLATFORM=android-7.0
|
||||
<<: *_android
|
||||
- env: PLATFORM=android-7.1
|
||||
<<: *_android
|
||||
- env: PLATFORM=android-8.0
|
||||
<<: *_android
|
||||
- env: PLATFORM=android-8.1
|
||||
<<: *_android
|
||||
- env: PLATFORM=android-9.0
|
||||
<<: *_android
|
||||
|
||||
before_install:
|
||||
# manually install Node for `language: android`
|
||||
- if [[ "$PLATFORM" =~ android ]]; then nvm install $TRAVIS_NODE_VERSION; fi
|
||||
- node --version
|
||||
- if [[ "$PLATFORM" =~ android ]]; then gradle --version; fi
|
||||
- if [[ "$PLATFORM" =~ ios ]]; then npm install -g ios-deploy; fi
|
||||
- npm install -g cordova
|
||||
# install paramedic if not running on paramedic repo
|
||||
- if ! [[ "$TRAVIS_REPO_SLUG" =~ cordova-paramedic ]]; then npm install -g github:apache/cordova-paramedic; fi
|
||||
|
||||
install:
|
||||
- npm install
|
||||
|
||||
before_script:
|
||||
- |
|
||||
if [[ "$TRAVIS_REPO_SLUG" =~ cordova-paramedic ]]; then
|
||||
# when used in the cordova-paramedic repo
|
||||
TEST_COMMAND="npm run eslint"
|
||||
PARAMEDIC_PLUGIN_TO_TEST="./spec/testable-plugin/"
|
||||
PARAMEDIC_COMMAND="node main.js"
|
||||
else
|
||||
# when used in any other (plugin) repo
|
||||
TEST_COMMAND="npm test"
|
||||
PARAMEDIC_PLUGIN_TO_TEST=$(pwd)
|
||||
PARAMEDIC_COMMAND="cordova-paramedic"
|
||||
fi
|
||||
- PARAMEDIC_BUILDNAME=travis-$TRAVIS_REPO_SLUG-$TRAVIS_JOB_NUMBER
|
||||
|
||||
script:
|
||||
- $TEST_COMMAND
|
||||
- |
|
||||
if [[ "$ADDITIONAL_TESTS_DIR" != "" ]];
|
||||
then cd $ADDITIONAL_TESTS_DIR && npm install && npm test;
|
||||
else
|
||||
$PARAMEDIC_COMMAND --config ./pr/$PLATFORM --plugin $PARAMEDIC_PLUGIN_TO_TEST --buildName $PARAMEDIC_BUILDNAME;
|
||||
fi
|
||||
36
README.md
36
README.md
@@ -21,12 +21,10 @@ description: Take pictures with the device camera.
|
||||
# under the License.
|
||||
-->
|
||||
|
||||
|AppVeyor|Travis CI|
|
||||
|:-:|:-:|
|
||||
|[](https://ci.appveyor.com/project/ApacheSoftwareFoundation/cordova-plugin-camera)|[](https://travis-ci.org/apache/cordova-plugin-camera)|
|
||||
|
||||
# cordova-plugin-camera
|
||||
|
||||
[](https://github.com/apache/cordova-plugin-camera/actions/workflows/android.yml) [](https://github.com/apache/cordova-plugin-camera/actions/workflows/chrome.yml) [](https://github.com/apache/cordova-plugin-camera/actions/workflows/ios.yml) [](https://github.com/apache/cordova-plugin-camera/actions/workflows/lint.yml)
|
||||
|
||||
This plugin defines a global `navigator.camera` object, which provides an API for taking pictures and for choosing images from
|
||||
the system's image library.
|
||||
|
||||
@@ -40,16 +38,20 @@ Although the object is attached to the global scoped `navigator`, it is not avai
|
||||
|
||||
## Installation
|
||||
|
||||
This requires cordova 5.0+
|
||||
|
||||
cordova plugin add cordova-plugin-camera
|
||||
Older versions of cordova can still install via the __deprecated__ id
|
||||
|
||||
cordova plugin add org.apache.cordova.camera
|
||||
It is also possible to install via repo url directly ( unstable )
|
||||
|
||||
cordova plugin add https://github.com/apache/cordova-plugin-camera.git
|
||||
|
||||
## Plugin variables
|
||||
|
||||
The plugin uses the `ANDROIDX_CORE_VERSION` variable to configure `androidx.core:core` dependency. This allows to avoid conflicts with other plugins that have the dependency hardcoded.
|
||||
If no value is passed, it will use `1.6.+` as the default value.
|
||||
|
||||
The variable is configured on install time
|
||||
|
||||
cordova plugin add cordova-plugin-camera --variable ANDROIDX_CORE_VERSION=1.8.0
|
||||
|
||||
## How to Contribute
|
||||
|
||||
@@ -170,8 +172,6 @@ __Supported Platforms__
|
||||
- Android
|
||||
- Browser
|
||||
- iOS
|
||||
- Windows
|
||||
- OSX
|
||||
|
||||
More examples [here](#camera-getPicture-examples). Quirks [here](#camera-getPicture-quirks).
|
||||
|
||||
@@ -474,16 +474,6 @@ displays:
|
||||
// do your thing here!
|
||||
}, 0);
|
||||
|
||||
#### Windows quirks
|
||||
|
||||
On Windows Phone 8.1 using `SAVEDPHOTOALBUM` or `PHOTOLIBRARY` as a source type causes application to suspend until file picker returns the selected image and
|
||||
then restore with start page as defined in app's `config.xml`. In case when `camera.getPicture` was called from different page, this will lead to reloading
|
||||
start page from scratch and success and error callbacks will never be called.
|
||||
|
||||
To avoid this we suggest using SPA pattern or call `camera.getPicture` only from your app's start page.
|
||||
|
||||
More information about Windows Phone 8.1 picker APIs is here: [How to continue your Windows Phone app after calling a file picker](https://msdn.microsoft.com/en-us/library/windows/apps/dn720490.aspx)
|
||||
|
||||
## `CameraOptions` Errata <a name="CameraOptions-quirks"></a>
|
||||
|
||||
#### Android Quirks
|
||||
@@ -569,12 +559,6 @@ function displayImage(imgUri) {
|
||||
}
|
||||
```
|
||||
|
||||
To display the image on some platforms, you might need to include the main part of the URI in the Content-Security-Policy `<meta>` element in index.html. For example, on Windows 10, you can include `ms-appdata:` in your `<meta>` element. Here is an example.
|
||||
|
||||
```html
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: ms-appdata: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
|
||||
```
|
||||
|
||||
## Take a Picture and Return Thumbnails (Resize the Picture) <a name="getThumbnails"></a>
|
||||
|
||||
To get smaller images, you can return a resized image by passing both `targetHeight` and `targetWidth` values with your CameraOptions object. In this example, you resize the returned image to fit in a 100px by 100px box (the aspect ratio is maintained, so 100px is either the height or width, whichever is greater in the source).
|
||||
|
||||
@@ -20,6 +20,68 @@
|
||||
-->
|
||||
# Release Notes
|
||||
|
||||
### 7.0.0 (Sep 06, 2023)
|
||||
|
||||
**Breaking Changes:**
|
||||
|
||||
* [GH-848](https://github.com/apache/cordova-plugin-camera/pull/848) fix!: remove deprecated platforms
|
||||
* [GH-844](https://github.com/apache/cordova-plugin-camera/pull/844) feat(android)!: Android 13 support
|
||||
|
||||
**Fixes:**
|
||||
|
||||
* [GH-827](https://github.com/apache/cordova-plugin-camera/pull/827) fix(android): set `applicationId`
|
||||
* [GH-810](https://github.com/apache/cordova-plugin-camera/pull/810) fix(browser): use `navigator.mediaDevices.getUserMedia`
|
||||
* [GH-712](https://github.com/apache/cordova-plugin-camera/pull/712) fix(ios): preserving `EXIF` data
|
||||
* [GH-780](https://github.com/apache/cordova-plugin-camera/pull/780) fix(android): update queries in `plugin.xml`
|
||||
|
||||
**Chores, Dependencies, Docs:**
|
||||
|
||||
* [GH-850](https://github.com/apache/cordova-plugin-camera/pull/850) chore: remove windows/osx from `plugin.xml`
|
||||
* [GH-849](https://github.com/apache/cordova-plugin-camera/pull/849) chore: Update `SUPPORT_QUESTION.md` template
|
||||
* [GH-831](https://github.com/apache/cordova-plugin-camera/pull/831) chore(android): Cleanup obsolete `BuildConfig` comments
|
||||
* [GH-846](https://github.com/apache/cordova-plugin-camera/pull/846) dep(dev)!: bump `@cordova/eslint-config@5.0`
|
||||
* [GH-800](https://github.com/apache/cordova-plugin-camera/pull/800) dep(npm): bump package-lock v2 w/ rebuild
|
||||
* [GH-808](https://github.com/apache/cordova-plugin-camera/pull/808) docs(README): Document `ANDROIDX_CORE_VERSION` variable
|
||||
|
||||
**CI:**
|
||||
|
||||
* [GH-851](https://github.com/apache/cordova-plugin-camera/pull/851) ci(gh-action): sync with `paramedic` configs
|
||||
* [GH-835](https://github.com/apache/cordova-plugin-camera/pull/835) ci(android): Drop API 22 & 31. Added API 24 & 33
|
||||
* [GH-804](https://github.com/apache/cordova-plugin-camera/pull/804) ci: sync workflow with `paramedic`
|
||||
* [GH-798](https://github.com/apache/cordova-plugin-camera/pull/798) ci(android): update java requirement for `cordova-android@11`
|
||||
* [GH-770](https://github.com/apache/cordova-plugin-camera/pull/770) ci(ios): update workflow w/ iOS 15
|
||||
* [GH-766](https://github.com/apache/cordova-plugin-camera/pull/766) ci: remove old ci workflow
|
||||
* [GH-765](https://github.com/apache/cordova-plugin-camera/pull/765) ci: add action-badge
|
||||
* [GH-764](https://github.com/apache/cordova-plugin-camera/pull/764) ci: remove `travis` & `appveyor`
|
||||
* [GH-762](https://github.com/apache/cordova-plugin-camera/pull/762) ci: add `gh-actions` workflows
|
||||
|
||||
### 6.0.0 (Aug 19, 2021)
|
||||
|
||||
**Feature:**
|
||||
|
||||
* [GH-751](https://github.com/apache/cordova-plugin-camera/pull/751) feat(android)!: support **AndroidX**
|
||||
* [GH-750](https://github.com/apache/cordova-plugin-camera/pull/750) feat(android): bump `cordova-android` requirements for `10.x`
|
||||
* [GH-731](https://github.com/apache/cordova-plugin-camera/pull/731) feat(android): encode `heic` format to `EncodingType` for webview display [#711](https://github.com/apache/cordova-plugin-camera/issues/711)
|
||||
* [GH-684](https://github.com/apache/cordova-plugin-camera/pull/684) feat(android): `sdk-30` package visibility support
|
||||
|
||||
**Fix:**
|
||||
|
||||
* [GH-687](https://github.com/apache/cordova-plugin-camera/pull/687) fix(android): return exception message (where it exists)
|
||||
* [GH-585](https://github.com/apache/cordova-plugin-camera/pull/585) fix(android): file path correction if `Uri` authority is `FileProvider`
|
||||
|
||||
**Chore & CI:**
|
||||
|
||||
* [GH-749](https://github.com/apache/cordova-plugin-camera/pull/749) chore: bump plugin version for next major
|
||||
* [GH-654](https://github.com/apache/cordova-plugin-camera/pull/654) chore: add release notify action
|
||||
* [GH-745](https://github.com/apache/cordova-plugin-camera/pull/745) ci(gh-action): added workflow to run tests
|
||||
|
||||
### 5.0.3 (Aug 04, 2021)
|
||||
|
||||
* [GH-754](https://github.com/apache/cordova-plugin-camera/pull/754) chore: rebuilt `package-lock.json`
|
||||
* [GH-748](https://github.com/apache/cordova-plugin-camera/pull/748) fix: incorrect version in `package-lock`
|
||||
* [GH-747](https://github.com/apache/cordova-plugin-camera/pull/747) chore: set the 5.x versions locked to `cordova-android` `<10.0.0`
|
||||
* [GH-729](https://github.com/apache/cordova-plugin-camera/pull/729) chore(asf): Update GitHub repo metadata
|
||||
|
||||
### 5.0.2 (May 11, 2021)
|
||||
* [GH-728](https://github.com/apache/cordova-plugin-camera/pull/728) plugin release preparation - audit fix
|
||||
* [GH-700](https://github.com/apache/cordova-plugin-camera/pull/700) Bugfix [issue 665](https://github.com/apache/cordova-plugin-camera/issues/665) - app crashes after taking a picture due to a bug in the camera plugin when app is resumed
|
||||
|
||||
4607
package-lock.json
generated
4607
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
27
package.json
27
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cordova-plugin-camera",
|
||||
"version": "5.0.2",
|
||||
"version": "7.0.0",
|
||||
"description": "Cordova Camera Plugin",
|
||||
"types": "./types/index.d.ts",
|
||||
"cordova": {
|
||||
@@ -8,9 +8,7 @@
|
||||
"platforms": [
|
||||
"android",
|
||||
"ios",
|
||||
"browser",
|
||||
"windows",
|
||||
"osx"
|
||||
"browser"
|
||||
]
|
||||
},
|
||||
"repository": "github:apache/cordova-plugin-camera",
|
||||
@@ -21,9 +19,7 @@
|
||||
"ecosystem:cordova",
|
||||
"cordova-android",
|
||||
"cordova-ios",
|
||||
"cordova-browser",
|
||||
"cordova-windows",
|
||||
"cordova-osx"
|
||||
"cordova-browser"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "npm run lint",
|
||||
@@ -45,12 +41,27 @@
|
||||
"cordova-ios": ">=5.1.0",
|
||||
"cordova": ">=9.0.0"
|
||||
},
|
||||
"5.0.4-dev": {
|
||||
"cordova-android": "<10.0.0",
|
||||
"cordova-ios": ">=5.1.0",
|
||||
"cordova": ">=9.0.0"
|
||||
},
|
||||
"6.0.0": {
|
||||
"cordova-android": ">=10.0.0",
|
||||
"cordova-ios": ">=5.1.0",
|
||||
"cordova": ">=9.0.0"
|
||||
},
|
||||
"7.0.0": {
|
||||
"cordova-android": ">=12.0.0",
|
||||
"cordova-ios": ">=5.1.0",
|
||||
"cordova": ">=9.0.0"
|
||||
},
|
||||
"8.0.0": {
|
||||
"cordova": ">100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cordova/eslint-config": "^3.0.0"
|
||||
"@cordova/eslint-config": "^5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
63
plugin.xml
63
plugin.xml
@@ -21,7 +21,7 @@
|
||||
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
id="cordova-plugin-camera"
|
||||
version="5.0.2">
|
||||
version="7.0.0">
|
||||
<name>Camera</name>
|
||||
<description>Cordova Camera Plugin</description>
|
||||
<license>Apache 2.0</license>
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
<engines>
|
||||
<engine name="cordova" version=">=9.0.0"/>
|
||||
<engine name="cordova-android" version=">=9.0.0" />
|
||||
<engine name="cordova-android" version=">=12.0.0" />
|
||||
<engine name="cordova-ios" version=">=5.1.0" />
|
||||
</engines>
|
||||
|
||||
@@ -55,7 +55,9 @@
|
||||
</feature>
|
||||
</config-file>
|
||||
<config-file target="AndroidManifest.xml" parent="/*">
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
||||
</config-file>
|
||||
<config-file target="AndroidManifest.xml" parent="application">
|
||||
<provider
|
||||
@@ -69,6 +71,22 @@
|
||||
</provider>
|
||||
</config-file>
|
||||
|
||||
<config-file target="AndroidManifest.xml" parent="queries">
|
||||
<intent>
|
||||
<action android:name="android.media.action.IMAGE_CAPTURE" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.GET_CONTENT" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.PICK" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="com.android.camera.action.CROP" />
|
||||
<data android:scheme="content" android:mimeType="image/*"/>
|
||||
</intent>
|
||||
</config-file>
|
||||
|
||||
<source-file src="src/android/CameraLauncher.java" target-dir="src/org/apache/cordova/camera" />
|
||||
<source-file src="src/android/FileHelper.java" target-dir="src/org/apache/cordova/camera" />
|
||||
<source-file src="src/android/ExifHelper.java" target-dir="src/org/apache/cordova/camera" />
|
||||
@@ -76,13 +94,12 @@
|
||||
<source-file src="src/android/GalleryPathVO.java" target-dir="src/org/apache/cordova/camera" />
|
||||
<source-file src="src/android/xml/camera_provider_paths.xml" target-dir="res/xml" />
|
||||
|
||||
<preference name="ANDROIDX_CORE_VERSION" default="1.6.+"/>
|
||||
<framework src="androidx.core:core:$ANDROIDX_CORE_VERSION" />
|
||||
|
||||
<js-module src="www/CameraPopoverHandle.js" name="CameraPopoverHandle">
|
||||
<clobbers target="CameraPopoverHandle" />
|
||||
</js-module>
|
||||
|
||||
<preference name="ANDROID_SUPPORT_V4_VERSION" default="27.+"/>
|
||||
<framework src="com.android.support:support-v4:$ANDROID_SUPPORT_V4_VERSION"/>
|
||||
|
||||
</platform>
|
||||
|
||||
<!-- ios -->
|
||||
@@ -128,36 +145,4 @@
|
||||
</js-module>
|
||||
</platform>
|
||||
|
||||
<!-- windows -->
|
||||
<platform name="windows">
|
||||
<config-file target="package.appxmanifest" parent="/Package/Capabilities">
|
||||
<DeviceCapability Name="webcam" />
|
||||
</config-file>
|
||||
<js-module src="www/CameraPopoverHandle.js" name="CameraPopoverHandle">
|
||||
<clobbers target="CameraPopoverHandle" />
|
||||
</js-module>
|
||||
<js-module src="src/windows/CameraProxy.js" name="CameraProxy">
|
||||
<runs />
|
||||
</js-module>
|
||||
</platform>
|
||||
|
||||
<!-- osx -->
|
||||
<platform name="osx">
|
||||
<config-file target="config.xml" parent="/*">
|
||||
<feature name="Camera">
|
||||
<param name="osx-package" value="CDVCamera"/>
|
||||
</feature>
|
||||
</config-file>
|
||||
|
||||
<js-module src="www/CameraPopoverHandle.js" name="CameraPopoverHandle">
|
||||
<clobbers target="CameraPopoverHandle" />
|
||||
</js-module>
|
||||
|
||||
<header-file src="src/osx/CDVCamera.h" />
|
||||
<source-file src="src/osx/CDVCamera.m" />
|
||||
|
||||
<framework src="Quartz.framework" />
|
||||
<framework src="AppKit.framework" />
|
||||
</platform>
|
||||
|
||||
</plugin>
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package org.apache.cordova.camera;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ContentResolver;
|
||||
@@ -39,7 +40,7 @@ import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.support.v4.content.FileProvider;
|
||||
import androidx.core.content.FileProvider;
|
||||
import android.util.Base64;
|
||||
|
||||
import org.apache.cordova.BuildHelper;
|
||||
@@ -59,6 +60,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
@@ -87,6 +89,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
private static final String PNG_EXTENSION = "." + PNG_TYPE;
|
||||
private static final String PNG_MIME_TYPE = "image/png";
|
||||
private static final String JPEG_MIME_TYPE = "image/jpeg";
|
||||
private static final String HEIC_MIME_TYPE = "image/heic";
|
||||
private static final String GET_PICTURE = "Get Picture";
|
||||
private static final String GET_VIDEO = "Get Video";
|
||||
private static final String GET_All = "Get All";
|
||||
@@ -121,8 +124,6 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
private boolean orientationCorrected; // Has the picture's orientation been corrected
|
||||
private boolean allowEdit; // Should we allow the user to crop the image.
|
||||
|
||||
protected final static String[] permissions = { Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE };
|
||||
|
||||
public CallbackContext callbackContext;
|
||||
private int numPics;
|
||||
|
||||
@@ -144,11 +145,9 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
*/
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
this.callbackContext = callbackContext;
|
||||
//Adding an API to CoreAndroid to get the BuildConfigValue
|
||||
//This allows us to not make this a breaking change to embedding
|
||||
this.applicationId = (String) BuildHelper.getBuildConfigValue(cordova.getActivity(), "APPLICATION_ID");
|
||||
this.applicationId = preferences.getString("applicationId", this.applicationId);
|
||||
|
||||
this.applicationId = cordova.getContext().getPackageName();
|
||||
this.applicationId = preferences.getString("applicationId", this.applicationId);
|
||||
|
||||
if (action.equals(TAKE_PICTURE_ACTION)) {
|
||||
this.srcType = CAMERA;
|
||||
@@ -194,10 +193,11 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
}
|
||||
else if ((this.srcType == PHOTOLIBRARY) || (this.srcType == SAVEDPHOTOALBUM)) {
|
||||
// FIXME: Stop always requesting the permission
|
||||
if(!PermissionHelper.hasPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
|
||||
PermissionHelper.requestPermission(this, SAVE_TO_ALBUM_SEC, Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
String[] permissions = getPermissions(true, mediaType);
|
||||
if(!hasPermissions(permissions)) {
|
||||
PermissionHelper.requestPermissions(this, SAVE_TO_ALBUM_SEC, permissions);
|
||||
} else {
|
||||
this.getImage(this.srcType, destType, encodingType);
|
||||
this.getImage(this.srcType, destType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,6 +222,37 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
private String[] getPermissions(boolean storageOnly, int mediaType) {
|
||||
ArrayList<String> permissions = new ArrayList<>();
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
// Android API 33 and higher
|
||||
switch (mediaType) {
|
||||
case PICTURE:
|
||||
permissions.add(Manifest.permission.READ_MEDIA_IMAGES);
|
||||
break;
|
||||
case VIDEO:
|
||||
permissions.add(Manifest.permission.READ_MEDIA_VIDEO);
|
||||
break;
|
||||
default:
|
||||
permissions.add(Manifest.permission.READ_MEDIA_IMAGES);
|
||||
permissions.add(Manifest.permission.READ_MEDIA_VIDEO);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Android API 32 or lower
|
||||
permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
}
|
||||
|
||||
if (!storageOnly) {
|
||||
// Add camera permission when not storage.
|
||||
permissions.add(Manifest.permission.CAMERA);
|
||||
}
|
||||
|
||||
return permissions.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private String getTempDirectoryPath() {
|
||||
File cache = cordova.getActivity().getCacheDir();
|
||||
// Create the cache directory if it doesn't exist
|
||||
@@ -244,8 +275,13 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
* @param encodingType Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
|
||||
*/
|
||||
public void callTakePicture(int returnType, int encodingType) {
|
||||
boolean saveAlbumPermission = PermissionHelper.hasPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
&& PermissionHelper.hasPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
String[] storagePermissions = getPermissions(true, mediaType);
|
||||
boolean saveAlbumPermission;
|
||||
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
saveAlbumPermission = this.saveToPhotoAlbum ? hasPermissions(storagePermissions) : true;
|
||||
} else {
|
||||
saveAlbumPermission = hasPermissions(storagePermissions);
|
||||
}
|
||||
boolean takePicturePermission = PermissionHelper.hasPermission(this, Manifest.permission.CAMERA);
|
||||
|
||||
// CB-10120: The CAMERA permission does not need to be requested unless it is declared
|
||||
@@ -273,13 +309,12 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
|
||||
if (takePicturePermission && saveAlbumPermission) {
|
||||
takePicture(returnType, encodingType);
|
||||
} else if (saveAlbumPermission && !takePicturePermission) {
|
||||
} else if (saveAlbumPermission) {
|
||||
PermissionHelper.requestPermission(this, TAKE_PIC_SEC, Manifest.permission.CAMERA);
|
||||
} else if (!saveAlbumPermission && takePicturePermission) {
|
||||
PermissionHelper.requestPermissions(this, TAKE_PIC_SEC,
|
||||
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE});
|
||||
} else if (takePicturePermission) {
|
||||
PermissionHelper.requestPermissions(this, TAKE_PIC_SEC, storagePermissions);
|
||||
} else {
|
||||
PermissionHelper.requestPermissions(this, TAKE_PIC_SEC, permissions);
|
||||
PermissionHelper.requestPermissions(this, TAKE_PIC_SEC, getPermissions(false, mediaType));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,11 +391,10 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
*
|
||||
* @param srcType The album to get image from.
|
||||
* @param returnType Set the type of image to return.
|
||||
* @param encodingType
|
||||
*/
|
||||
// TODO: Images selected from SDCARD don't display correctly, but from CAMERA ALBUM do!
|
||||
// TODO: Images from kitkat filechooser not going into crop function
|
||||
public void getImage(int srcType, int returnType, int encodingType) {
|
||||
public void getImage(int srcType, int returnType) {
|
||||
Intent intent = new Intent();
|
||||
String title = GET_PICTURE;
|
||||
croppedUri = null;
|
||||
@@ -420,7 +454,6 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
// set crop properties
|
||||
cropIntent.putExtra("crop", "true");
|
||||
|
||||
|
||||
// indicate output X and Y
|
||||
if (targetWidth > 0) {
|
||||
cropIntent.putExtra("outputX", targetWidth);
|
||||
@@ -439,7 +472,6 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
cropIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
cropIntent.putExtra("output", croppedUri);
|
||||
|
||||
|
||||
// start the activity - we handle returning in onActivityResult
|
||||
|
||||
if (this.cordova != null) {
|
||||
@@ -450,9 +482,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
LOG.e(LOG_TAG, "Crop operation not supported on this device");
|
||||
try {
|
||||
processResultFromCamera(destType, cameraIntent);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
LOG.e(LOG_TAG, "Unable to write to file");
|
||||
}
|
||||
@@ -475,7 +505,6 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
this.croppedFilePath :
|
||||
this.imageFilePath;
|
||||
|
||||
|
||||
if (this.encodingType == JPEG) {
|
||||
try {
|
||||
//We don't support PNG, so let's not pretend we do
|
||||
@@ -610,7 +639,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
ContentResolver resolver = this.cordova.getActivity().getContentResolver();
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, galleryPathVO.getGalleryFileName());
|
||||
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, getMimetypeForFormat(encodingType));
|
||||
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, getMimetypeForEncodingType());
|
||||
Uri galleryOutputUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
|
||||
|
||||
InputStream fileStream = org.apache.cordova.camera.FileHelper.getInputStreamFromUriString(imageUri.toString(), cordova);
|
||||
@@ -623,7 +652,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
|
||||
private GalleryPathVO getPicturesPath() {
|
||||
String timeStamp = new SimpleDateFormat(TIME_FORMAT).format(new Date());
|
||||
String imageFileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? JPEG_EXTENSION : PNG_EXTENSION);
|
||||
String imageFileName = "IMG_" + timeStamp + getExtensionForEncodingType();
|
||||
File storageDir = Environment.getExternalStoragePublicDirectory(
|
||||
Environment.DIRECTORY_PICTURES);
|
||||
storageDir.mkdirs();
|
||||
@@ -639,28 +668,20 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
|
||||
/**
|
||||
* Converts output image format int value to string value of mime type.
|
||||
* @param outputFormat int Output format of camera API.
|
||||
* Must be value of either JPEG or PNG constant
|
||||
* @return String String value of mime type or empty string if mime type is not supported
|
||||
*/
|
||||
private String getMimetypeForFormat(int outputFormat) {
|
||||
if (outputFormat == PNG) return PNG_MIME_TYPE;
|
||||
if (outputFormat == JPEG) return JPEG_MIME_TYPE;
|
||||
private String getMimetypeForEncodingType() {
|
||||
if (encodingType == PNG) return PNG_MIME_TYPE;
|
||||
if (encodingType == JPEG) return JPEG_MIME_TYPE;
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
private String outputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
private String outputModifiedBitmap(Bitmap bitmap, Uri uri, String mimeTypeOfOriginalFile) throws IOException {
|
||||
// Some content: URIs do not map to file paths (e.g. picasa).
|
||||
String realPath = FileHelper.getRealPath(uri, this.cordova);
|
||||
String fileName = calculateModifiedBitmapOutputFileName(mimeTypeOfOriginalFile, realPath);
|
||||
|
||||
// Get filename from uri
|
||||
String fileName = realPath != null ?
|
||||
realPath.substring(realPath.lastIndexOf('/') + 1) :
|
||||
"modified." + (this.encodingType == JPEG ? JPEG_TYPE : PNG_TYPE);
|
||||
|
||||
String timeStamp = new SimpleDateFormat(TIME_FORMAT).format(new Date());
|
||||
//String fileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? ".jpg" : ".png");
|
||||
String modifiedPath = getTempDirectoryPath() + "/" + fileName;
|
||||
|
||||
OutputStream os = new FileOutputStream(modifiedPath);
|
||||
@@ -684,6 +705,23 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
return modifiedPath;
|
||||
}
|
||||
|
||||
private String calculateModifiedBitmapOutputFileName(String mimeTypeOfOriginalFile, String realPath) {
|
||||
if (realPath == null) {
|
||||
return "modified" + getExtensionForEncodingType();
|
||||
}
|
||||
String fileName = realPath.substring(realPath.lastIndexOf('/') + 1);
|
||||
if (getMimetypeForEncodingType().equals(mimeTypeOfOriginalFile)) {
|
||||
return fileName;
|
||||
}
|
||||
// if the picture is not a jpeg or png, (a .heic for example) when processed to a bitmap
|
||||
// the file extension is changed to the output format, f.e. an input file my_photo.heic could become my_photo.jpg
|
||||
return fileName.substring(fileName.lastIndexOf(".") + 1) + getExtensionForEncodingType();
|
||||
}
|
||||
|
||||
private String getExtensionForEncodingType() {
|
||||
return this.encodingType == JPEG ? JPEG_EXTENSION : PNG_EXTENSION;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Applies all needed transformation to the image received from the gallery.
|
||||
@@ -707,24 +745,22 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
|
||||
String uriString = uri.toString();
|
||||
String finalLocation = fileLocation != null ? fileLocation : uriString;
|
||||
String mimeType = FileHelper.getMimeType(uriString, this.cordova);
|
||||
String mimeTypeOfGalleryFile = FileHelper.getMimeType(uriString, this.cordova);
|
||||
|
||||
if (finalLocation == null) {
|
||||
this.failPicture("Error retrieving result.");
|
||||
} else {
|
||||
|
||||
// If you ask for video or the selected file doesn't have JPEG or PNG mime type
|
||||
// there will be no attempt to resize any returned data
|
||||
if (this.mediaType == VIDEO || !(JPEG_MIME_TYPE.equalsIgnoreCase(mimeType) || PNG_MIME_TYPE.equalsIgnoreCase(mimeType))) {
|
||||
// If you ask for video or the selected file cannot be processed
|
||||
// there will be no attempt to resize any returned data.
|
||||
if (this.mediaType == VIDEO || !isImageMimeTypeProcessable(mimeTypeOfGalleryFile)) {
|
||||
this.callbackContext.success(finalLocation);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
|
||||
// This is a special case to just return the path as no scaling,
|
||||
// rotating, nor compressing needs to be done
|
||||
if (this.targetHeight == -1 && this.targetWidth == -1 &&
|
||||
destType == FILE_URI && !this.correctOrientation &&
|
||||
mimeType != null && mimeType.equalsIgnoreCase(getMimetypeForFormat(encodingType)))
|
||||
getMimetypeForEncodingType().equalsIgnoreCase(mimeTypeOfGalleryFile))
|
||||
{
|
||||
this.callbackContext.success(finalLocation);
|
||||
} else {
|
||||
@@ -750,17 +786,17 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
// Did we modify the image?
|
||||
if ( (this.targetHeight > 0 && this.targetWidth > 0) ||
|
||||
(this.correctOrientation && this.orientationCorrected) ||
|
||||
!mimeType.equalsIgnoreCase(getMimetypeForFormat(encodingType)))
|
||||
!mimeTypeOfGalleryFile.equalsIgnoreCase(getMimetypeForEncodingType()))
|
||||
{
|
||||
try {
|
||||
String modifiedPath = this.outputModifiedBitmap(bitmap, uri);
|
||||
String modifiedPath = this.outputModifiedBitmap(bitmap, uri, mimeTypeOfGalleryFile);
|
||||
// The modified image is cached by the app in order to get around this and not have to delete you
|
||||
// application cache I'm adding the current system time to the end of the file url.
|
||||
this.callbackContext.success("file://" + modifiedPath + "?" + System.currentTimeMillis());
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
this.failPicture("Error retrieving image.");
|
||||
this.failPicture("Error retrieving image: "+e.getLocalizedMessage());
|
||||
}
|
||||
} else {
|
||||
this.callbackContext.success(finalLocation);
|
||||
@@ -777,6 +813,18 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* JPEG, PNG and HEIC mime types (images) can be scaled, decreased in quantity, corrected by orientation.
|
||||
* But f.e. an image/gif cannot be scaled, but is can be selected through the PHOTOLIBRARY.
|
||||
*
|
||||
* @param mimeType The mimeType to check
|
||||
* @return if the mimeType is a processable image mime type
|
||||
*/
|
||||
private boolean isImageMimeTypeProcessable(String mimeType) {
|
||||
return JPEG_MIME_TYPE.equalsIgnoreCase(mimeType) || PNG_MIME_TYPE.equalsIgnoreCase(mimeType)
|
||||
|| HEIC_MIME_TYPE.equalsIgnoreCase(mimeType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the camera view exits.
|
||||
*
|
||||
@@ -830,7 +878,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
this.failPicture("Error capturing image.");
|
||||
this.failPicture("Error capturing image: "+e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -974,7 +1022,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
if (fileStream != null) {
|
||||
// Generate a temporary file
|
||||
String timeStamp = new SimpleDateFormat(TIME_FORMAT).format(new Date());
|
||||
String fileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? JPEG_EXTENSION : PNG_EXTENSION);
|
||||
String fileName = "IMG_" + timeStamp + (getExtensionForEncodingType());
|
||||
localFile = new File(getTempDirectoryPath() + fileName);
|
||||
galleryUri = Uri.fromFile(localFile);
|
||||
writeUncompressedImage(fileStream, galleryUri);
|
||||
@@ -998,15 +1046,11 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
rotate = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
} catch (Exception e) {
|
||||
LOG.e(LOG_TAG,"Exception while getting input stream: "+ e.toString());
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
try {
|
||||
// figure out the original width and height of the image
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
@@ -1052,7 +1096,6 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
// determine the correct aspect ratio
|
||||
int[] widthHeight = calculateAspectRatio(rotatedWidth, rotatedHeight);
|
||||
|
||||
|
||||
// Load in the smallest bitmap possible that is closest to the size we want
|
||||
options.inJustDecodeBounds = false;
|
||||
options.inSampleSize = calculateSampleSize(rotatedWidth, rotatedHeight, widthHeight[0], widthHeight[1]);
|
||||
@@ -1092,8 +1135,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
}
|
||||
}
|
||||
return scaledBitmap;
|
||||
}
|
||||
finally {
|
||||
} finally {
|
||||
// delete the temporary copy
|
||||
if (localFile != null) {
|
||||
localFile.delete();
|
||||
@@ -1226,6 +1268,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
// delete the duplicate file if the difference is 2 for file URI or 1 for Data URL
|
||||
if ((currentNumOfImages - numPics) == diff) {
|
||||
cursor.moveToLast();
|
||||
@SuppressLint("Range")
|
||||
int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID)));
|
||||
if (diff == 2) {
|
||||
id--;
|
||||
@@ -1269,7 +1312,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
code = null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
this.failPicture("Error compressing image.");
|
||||
this.failPicture("Error compressing image: "+e.getLocalizedMessage());
|
||||
}
|
||||
jpeg_data = null;
|
||||
}
|
||||
@@ -1305,9 +1348,8 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
this.conn.disconnect();
|
||||
}
|
||||
|
||||
|
||||
public void onRequestPermissionResult(int requestCode, String[] permissions,
|
||||
int[] grantResults) throws JSONException {
|
||||
int[] grantResults) {
|
||||
for (int r : grantResults) {
|
||||
if (r == PackageManager.PERMISSION_DENIED) {
|
||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, PERMISSION_DENIED_ERROR));
|
||||
@@ -1319,7 +1361,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
takePicture(this.destType, this.encodingType);
|
||||
break;
|
||||
case SAVE_TO_ALBUM_SEC:
|
||||
this.getImage(this.srcType, this.destType, this.encodingType);
|
||||
this.getImage(this.srcType, this.destType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1381,9 +1423,18 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
}
|
||||
|
||||
if (state.containsKey(IMAGE_FILE_PATH_KEY)) {
|
||||
this.imageFilePath = state.getString(IMAGE_FILE_PATH_KEY);
|
||||
this.imageFilePath = state.getString(IMAGE_FILE_PATH_KEY);
|
||||
}
|
||||
|
||||
this.callbackContext = callbackContext;
|
||||
}
|
||||
|
||||
private boolean hasPermissions(String[] permissions) {
|
||||
for (String permission: permissions) {
|
||||
if (!PermissionHelper.hasPermission(this, permission)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.apache.cordova.camera;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.CursorLoader;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
@@ -29,8 +28,8 @@ import android.provider.MediaStore;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.LOG;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -44,7 +43,7 @@ public class FileHelper {
|
||||
* Returns the real path of the given URI string.
|
||||
* If the given URI string represents a content:// URI, the real path is retrieved from the media store.
|
||||
*
|
||||
* @param uriString the URI string of the audio/image/video
|
||||
* @param uri the URI of the audio/image/video
|
||||
* @param cordova the current application context
|
||||
* @return the full path to the file
|
||||
*/
|
||||
@@ -57,7 +56,7 @@ public class FileHelper {
|
||||
* Returns the real path of the given URI.
|
||||
* If the given URI is a content:// URI, the real path is retrieved from the media store.
|
||||
*
|
||||
* @param uri the URI of the audio/image/video
|
||||
* @param uriString the URI string from which to obtain the input stream
|
||||
* @param cordova the current application context
|
||||
* @return the full path to the file
|
||||
*/
|
||||
@@ -132,6 +131,9 @@ public class FileHelper {
|
||||
if (isGooglePhotosUri(uri))
|
||||
return uri.getLastPathSegment();
|
||||
|
||||
if (isFileProviderUri(context, uri))
|
||||
return getFileProviderPath(context, uri);
|
||||
|
||||
return getDataColumn(context, uri, null, null);
|
||||
}
|
||||
// File
|
||||
@@ -142,22 +144,6 @@ public class FileHelper {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getRealPathFromURI_BelowAPI11(Context context, Uri contentUri) {
|
||||
String[] proj = { MediaStore.Images.Media.DATA };
|
||||
String result = null;
|
||||
|
||||
try {
|
||||
Cursor cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
|
||||
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
|
||||
cursor.moveToFirst();
|
||||
result = cursor.getString(column_index);
|
||||
|
||||
} catch (Exception e) {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an input stream based on given URI string.
|
||||
*
|
||||
@@ -177,6 +163,7 @@ public class FileHelper {
|
||||
if (question > -1) {
|
||||
uriString = uriString.substring(0, question);
|
||||
}
|
||||
|
||||
if (uriString.startsWith("file:///android_asset/")) {
|
||||
Uri uri = Uri.parse(uriString);
|
||||
String relativePath = uri.getPath().substring(15);
|
||||
@@ -206,6 +193,7 @@ public class FileHelper {
|
||||
* @return a path without the "file://" prefix
|
||||
*/
|
||||
public static String stripFileProtocol(String uriString) {
|
||||
|
||||
if (uriString.startsWith("file://")) {
|
||||
uriString = uriString.substring(7);
|
||||
}
|
||||
@@ -225,7 +213,7 @@ public class FileHelper {
|
||||
}
|
||||
return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the mime type of the data specified by the given URI string.
|
||||
*
|
||||
@@ -233,7 +221,7 @@ public class FileHelper {
|
||||
* @return the mime type of the specified data
|
||||
*/
|
||||
public static String getMimeType(String uriString, CordovaInterface cordova) {
|
||||
String mimeType = null;
|
||||
String mimeType;
|
||||
|
||||
Uri uri = Uri.parse(uriString);
|
||||
if (uriString.startsWith("content://")) {
|
||||
@@ -316,4 +304,28 @@ public class FileHelper {
|
||||
public static boolean isGooglePhotosUri(Uri uri) {
|
||||
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context The Application context
|
||||
* @param uri The Uri is checked by functions
|
||||
* @return Whether the Uri authority is FileProvider
|
||||
*/
|
||||
public static boolean isFileProviderUri(final Context context, final Uri uri) {
|
||||
final String packageName = context.getPackageName();
|
||||
final String authority = new StringBuilder(packageName).append(".provider").toString();
|
||||
return authority.equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context The Application context
|
||||
* @param uri The Uri is checked by functions
|
||||
* @return File path or null if file is missing
|
||||
*/
|
||||
public static String getFileProviderPath(final Context context, final Uri uri)
|
||||
{
|
||||
final File appDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
|
||||
final File file = new File(appDir, uri.getLastPathSegment());
|
||||
return file.exists() ? file.toString(): null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,4 +18,4 @@
|
||||
*/
|
||||
package org.apache.cordova.camera;
|
||||
|
||||
public class FileProvider extends android.support.v4.content.FileProvider {}
|
||||
public class FileProvider extends androidx.core.content.FileProvider {}
|
||||
|
||||
@@ -19,13 +19,13 @@
|
||||
*
|
||||
*/
|
||||
|
||||
var HIGHEST_POSSIBLE_Z_INDEX = 2147483647;
|
||||
const HIGHEST_POSSIBLE_Z_INDEX = 2147483647;
|
||||
|
||||
function takePicture (success, error, opts) {
|
||||
if (opts && opts[2] === 1) {
|
||||
capture(success, error, opts);
|
||||
} else {
|
||||
var input = document.createElement('input');
|
||||
const input = document.createElement('input');
|
||||
input.style.position = 'relative';
|
||||
input.style.zIndex = HIGHEST_POSSIBLE_Z_INDEX;
|
||||
input.className = 'cordova-camera-select';
|
||||
@@ -33,11 +33,11 @@ function takePicture (success, error, opts) {
|
||||
input.name = 'files[]';
|
||||
|
||||
input.onchange = function (inputEvent) {
|
||||
var reader = new FileReader(); /* eslint no-undef : 0 */
|
||||
const reader = new FileReader(); /* eslint no-undef : 0 */
|
||||
reader.onload = function (readerEvent) {
|
||||
input.parentNode.removeChild(input);
|
||||
|
||||
var imageData = readerEvent.target.result;
|
||||
const imageData = readerEvent.target.result;
|
||||
|
||||
return success(imageData.substr(imageData.indexOf(',') + 1));
|
||||
};
|
||||
@@ -50,16 +50,16 @@ function takePicture (success, error, opts) {
|
||||
}
|
||||
|
||||
function capture (success, errorCallback, opts) {
|
||||
var localMediaStream;
|
||||
var targetWidth = opts[3];
|
||||
var targetHeight = opts[4];
|
||||
let localMediaStream;
|
||||
let targetWidth = opts[3];
|
||||
let targetHeight = opts[4];
|
||||
|
||||
targetWidth = targetWidth === -1 ? 320 : targetWidth;
|
||||
targetHeight = targetHeight === -1 ? 240 : targetHeight;
|
||||
|
||||
var video = document.createElement('video');
|
||||
var button = document.createElement('button');
|
||||
var parent = document.createElement('div');
|
||||
const video = document.createElement('video');
|
||||
const button = document.createElement('button');
|
||||
const parent = document.createElement('div');
|
||||
parent.style.position = 'relative';
|
||||
parent.style.zIndex = HIGHEST_POSSIBLE_Z_INDEX;
|
||||
parent.className = 'cordova-camera-capture';
|
||||
@@ -72,13 +72,13 @@ function capture (success, errorCallback, opts) {
|
||||
|
||||
button.onclick = function () {
|
||||
// create a canvas and capture a frame from video stream
|
||||
var canvas = document.createElement('canvas');
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = targetWidth;
|
||||
canvas.height = targetHeight;
|
||||
canvas.getContext('2d').drawImage(video, 0, 0, targetWidth, targetHeight);
|
||||
|
||||
// convert image stored in canvas to base64 encoded image
|
||||
var imageData = canvas.toDataURL('image/png');
|
||||
let imageData = canvas.toDataURL('image/png');
|
||||
imageData = imageData.replace('data:image/png;base64,', '');
|
||||
|
||||
// stop video stream, remove video and button.
|
||||
@@ -100,7 +100,7 @@ function capture (success, errorCallback, opts) {
|
||||
navigator.mozGetUserMedia ||
|
||||
navigator.msGetUserMedia;
|
||||
|
||||
var successCallback = function (stream) {
|
||||
const successCallback = function (stream) {
|
||||
localMediaStream = stream;
|
||||
if ('srcObject' in video) {
|
||||
video.srcObject = localMediaStream;
|
||||
@@ -111,7 +111,11 @@ function capture (success, errorCallback, opts) {
|
||||
document.body.appendChild(parent);
|
||||
};
|
||||
|
||||
if (navigator.getUserMedia) {
|
||||
if (navigator.mediaDevices.getUserMedia) {
|
||||
navigator.mediaDevices.getUserMedia({ video: true, audio: false })
|
||||
.then(successCallback)
|
||||
.catch(errorCallback);
|
||||
} else if (navigator.getUserMedia) {
|
||||
navigator.getUserMedia({ video: true, audio: false }, successCallback, errorCallback);
|
||||
} else {
|
||||
alert('Browser does not support camera :(');
|
||||
@@ -119,7 +123,7 @@ function capture (success, errorCallback, opts) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
takePicture: takePicture,
|
||||
takePicture,
|
||||
cleanup: function () {}
|
||||
};
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#import <ImageIO/CGImageDestination.h>
|
||||
#import <MobileCoreServices/UTCoreTypes.h>
|
||||
#import <objc/message.h>
|
||||
#import <Photos/Photos.h>
|
||||
|
||||
#ifndef __CORDOVA_4_0_0
|
||||
#import <Cordova/NSData+Base64.h>
|
||||
@@ -159,7 +160,7 @@ static NSString* toBase64(NSData* data) {
|
||||
if (pictureOptions.sourceType == UIImagePickerControllerSourceTypeCamera) {
|
||||
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted)
|
||||
{
|
||||
if(!granted)
|
||||
if (!granted)
|
||||
{
|
||||
// Denied; show an alert
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
@@ -174,11 +175,32 @@ static NSString* toBase64(NSData* data) {
|
||||
[weakSelf.viewController presentViewController:alertController animated:YES completion:nil];
|
||||
});
|
||||
} else {
|
||||
[weakSelf showCameraPicker:command.callbackId withOptions:pictureOptions];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[weakSelf showCameraPicker:command.callbackId withOptions:pictureOptions];
|
||||
});
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
[weakSelf showCameraPicker:command.callbackId withOptions:pictureOptions];
|
||||
[weakSelf options:pictureOptions requestPhotoPermissions:^(BOOL granted) {
|
||||
if (!granted) {
|
||||
// Denied; show an alert
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"] message:NSLocalizedString(@"Access to the camera roll has been prohibited; please enable it in the Settings to continue.", nil) preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
[weakSelf sendNoPermissionResult:command.callbackId];
|
||||
}]];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Settings", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
|
||||
[weakSelf sendNoPermissionResult:command.callbackId];
|
||||
}]];
|
||||
[weakSelf.viewController presentViewController:alertController animated:YES completion:nil];
|
||||
});
|
||||
} else {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[weakSelf showCameraPicker:command.callbackId withOptions:pictureOptions];
|
||||
});
|
||||
}
|
||||
}];
|
||||
}
|
||||
}];
|
||||
}
|
||||
@@ -367,24 +389,51 @@ static NSString* toBase64(NSData* data) {
|
||||
data = UIImageJPEGRepresentation(image, [options.quality floatValue] / 100.0f);
|
||||
}
|
||||
|
||||
if (options.usesGeolocation) {
|
||||
NSDictionary* controllerMetadata = [info objectForKey:@"UIImagePickerControllerMediaMetadata"];
|
||||
if (pickerController.sourceType == UIImagePickerControllerSourceTypeCamera) {
|
||||
if (options.usesGeolocation) {
|
||||
NSDictionary* controllerMetadata = [info objectForKey:@"UIImagePickerControllerMediaMetadata"];
|
||||
if (controllerMetadata) {
|
||||
self.data = data;
|
||||
self.metadata = [[NSMutableDictionary alloc] init];
|
||||
|
||||
NSMutableDictionary* EXIFDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyExifDictionary]mutableCopy];
|
||||
if (EXIFDictionary) {
|
||||
[self.metadata setObject:EXIFDictionary forKey:(NSString*)kCGImagePropertyExifDictionary];
|
||||
}
|
||||
|
||||
if (IsAtLeastiOSVersion(@"8.0")) {
|
||||
[[self locationManager] performSelector:NSSelectorFromString(@"requestWhenInUseAuthorization") withObject:nil afterDelay:0];
|
||||
}
|
||||
[[self locationManager] startUpdatingLocation];
|
||||
}
|
||||
data = nil;
|
||||
}
|
||||
} else if (pickerController.sourceType == UIImagePickerControllerSourceTypePhotoLibrary) {
|
||||
PHAsset* asset = [info objectForKey:@"UIImagePickerControllerPHAsset"];
|
||||
NSDictionary* controllerMetadata = [self getImageMetadataFromAsset:asset];
|
||||
|
||||
self.data = data;
|
||||
if (controllerMetadata) {
|
||||
self.data = data;
|
||||
self.metadata = [[NSMutableDictionary alloc] init];
|
||||
|
||||
NSMutableDictionary* EXIFDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyExifDictionary]mutableCopy];
|
||||
if (EXIFDictionary) {
|
||||
[self.metadata setObject:EXIFDictionary forKey:(NSString*)kCGImagePropertyExifDictionary];
|
||||
}
|
||||
|
||||
if (IsAtLeastiOSVersion(@"8.0")) {
|
||||
[[self locationManager] performSelector:NSSelectorFromString(@"requestWhenInUseAuthorization") withObject:nil afterDelay:0];
|
||||
NSMutableDictionary* TIFFDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyTIFFDictionary
|
||||
]mutableCopy];
|
||||
if (TIFFDictionary) {
|
||||
[self.metadata setObject:TIFFDictionary forKey:(NSString*)kCGImagePropertyTIFFDictionary];
|
||||
}
|
||||
NSMutableDictionary* GPSDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyGPSDictionary
|
||||
]mutableCopy];
|
||||
if (GPSDictionary) {
|
||||
[self.metadata setObject:GPSDictionary forKey:(NSString*)kCGImagePropertyGPSDictionary
|
||||
];
|
||||
}
|
||||
[[self locationManager] startUpdatingLocation];
|
||||
}
|
||||
data = nil;
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -394,6 +443,78 @@ static NSString* toBase64(NSData* data) {
|
||||
return data;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------
|
||||
-- get the metadata of the image from a PHAsset
|
||||
-------------------------------------------------------------- */
|
||||
- (NSDictionary*)getImageMetadataFromAsset:(PHAsset*)asset {
|
||||
|
||||
if(asset == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// get photo info from this asset
|
||||
__block NSDictionary *dict = nil;
|
||||
PHImageRequestOptions *imageRequestOptions = [[PHImageRequestOptions alloc] init];
|
||||
imageRequestOptions.synchronous = YES;
|
||||
[[PHImageManager defaultManager]
|
||||
requestImageDataForAsset:asset
|
||||
options:imageRequestOptions
|
||||
resultHandler: ^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
|
||||
dict = [self convertImageMetadata:imageData]; // as this imageData is in NSData format so we need a method to convert this NSData into NSDictionary
|
||||
}];
|
||||
return dict;
|
||||
}
|
||||
|
||||
-(NSDictionary*)convertImageMetadata:(NSData*)imageData {
|
||||
CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)(imageData), NULL);
|
||||
if (imageSource) {
|
||||
NSDictionary *options = @{(NSString *)kCGImageSourceShouldCache : [NSNumber numberWithBool:NO]};
|
||||
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, (__bridge CFDictionaryRef)options);
|
||||
if (imageProperties) {
|
||||
NSDictionary *metadata = (__bridge NSDictionary *)imageProperties;
|
||||
CFRelease(imageProperties);
|
||||
CFRelease(imageSource);
|
||||
NSLog(@"Metadata of selected image%@", metadata);// image metadata after converting NSData into NSDictionary
|
||||
return metadata;
|
||||
}
|
||||
CFRelease(imageSource);
|
||||
}
|
||||
|
||||
NSLog(@"Can't read image metadata");
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)options:(CDVPictureOptions*)options requestPhotoPermissions:(void (^)(BOOL auth))completion
|
||||
{
|
||||
if((unsigned long)options.sourceType == 1){
|
||||
completion(YES);
|
||||
}
|
||||
else{
|
||||
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
|
||||
|
||||
switch (status) {
|
||||
case PHAuthorizationStatusAuthorized:
|
||||
completion(YES);
|
||||
break;
|
||||
case PHAuthorizationStatusNotDetermined: {
|
||||
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus authorizationStatus) {
|
||||
if (authorizationStatus == PHAuthorizationStatusAuthorized) {
|
||||
completion(YES);
|
||||
} else {
|
||||
completion(NO);
|
||||
}
|
||||
}];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
completion(NO);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (NSString*)tempFilePath:(NSString*)extension
|
||||
{
|
||||
NSString* docsPath = [NSTemporaryDirectory()stringByStandardizingPath];
|
||||
@@ -454,17 +575,48 @@ static NSString* toBase64(NSData* data) {
|
||||
image = [self retrieveImage:info options:options];
|
||||
NSData* data = [self processImage:image info:info options:options];
|
||||
if (data) {
|
||||
if (pickerController.sourceType == UIImagePickerControllerSourceTypePhotoLibrary) {
|
||||
NSMutableData *imageDataWithExif = [NSMutableData data];
|
||||
if (self.metadata) {
|
||||
CGImageSourceRef sourceImage = CGImageSourceCreateWithData((__bridge CFDataRef)self.data, NULL);
|
||||
CFStringRef sourceType = CGImageSourceGetType(sourceImage);
|
||||
|
||||
NSString* extension = options.encodingType == EncodingTypePNG? @"png" : @"jpg";
|
||||
NSString* filePath = [self tempFilePath:extension];
|
||||
NSError* err = nil;
|
||||
CGImageDestinationRef destinationImage = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageDataWithExif, sourceType, 1, NULL);
|
||||
CGImageDestinationAddImageFromSource(destinationImage, sourceImage, 0, (__bridge CFDictionaryRef)self.metadata);
|
||||
CGImageDestinationFinalize(destinationImage);
|
||||
|
||||
// save file
|
||||
if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) {
|
||||
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
|
||||
} else {
|
||||
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[self urlTransformer:[NSURL fileURLWithPath:filePath]] absoluteString]];
|
||||
CFRelease(sourceImage);
|
||||
CFRelease(destinationImage);
|
||||
} else {
|
||||
imageDataWithExif = [self.data mutableCopy];
|
||||
}
|
||||
|
||||
NSError* err = nil;
|
||||
NSString* extension = self.pickerController.pictureOptions.encodingType == EncodingTypePNG ? @"png":@"jpg";
|
||||
NSString* filePath = [self tempFilePath:extension];
|
||||
|
||||
// save file
|
||||
if (![imageDataWithExif writeToFile:filePath options:NSAtomicWrite error:&err]) {
|
||||
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
|
||||
}
|
||||
else {
|
||||
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[self urlTransformer:[NSURL fileURLWithPath:filePath]] absoluteString]];
|
||||
}
|
||||
|
||||
} else if (pickerController.sourceType != UIImagePickerControllerSourceTypeCamera || !options.usesGeolocation) {
|
||||
// No need to save file if usesGeolocation is true since it will be saved after the location is tracked
|
||||
NSString* extension = options.encodingType == EncodingTypePNG? @"png" : @"jpg";
|
||||
NSString* filePath = [self tempFilePath:extension];
|
||||
NSError* err = nil;
|
||||
|
||||
// save file
|
||||
if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) {
|
||||
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
|
||||
} else {
|
||||
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[self urlTransformer:[NSURL fileURLWithPath:filePath]] absoluteString]];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -649,19 +801,23 @@ static NSString* toBase64(NSData* data) {
|
||||
{
|
||||
CDVPictureOptions* options = self.pickerController.pictureOptions;
|
||||
CDVPluginResult* result = nil;
|
||||
|
||||
NSMutableData *imageDataWithExif = [NSMutableData data];
|
||||
|
||||
if (self.metadata) {
|
||||
NSData* dataCopy = [self.data mutableCopy];
|
||||
CGImageSourceRef sourceImage = CGImageSourceCreateWithData((__bridge CFDataRef)dataCopy, NULL);
|
||||
CFStringRef sourceType = CGImageSourceGetType(sourceImage);
|
||||
|
||||
CGImageDestinationRef destinationImage = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)self.data, sourceType, 1, NULL);
|
||||
CGImageDestinationRef destinationImage = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageDataWithExif, sourceType, 1, NULL);
|
||||
CGImageDestinationAddImageFromSource(destinationImage, sourceImage, 0, (__bridge CFDictionaryRef)self.metadata);
|
||||
CGImageDestinationFinalize(destinationImage);
|
||||
|
||||
dataCopy = nil;
|
||||
CFRelease(sourceImage);
|
||||
CFRelease(destinationImage);
|
||||
} else {
|
||||
imageDataWithExif = [self.data mutableCopy];
|
||||
}
|
||||
|
||||
switch (options.destinationType) {
|
||||
@@ -695,7 +851,7 @@ static NSString* toBase64(NSData* data) {
|
||||
self.pickerController = nil;
|
||||
self.data = nil;
|
||||
self.metadata = nil;
|
||||
|
||||
imageDataWithExif = nil;
|
||||
if (options.saveToPhotoAlbum) {
|
||||
UIImageWriteToSavedPhotosAlbum([[UIImage alloc] initWithData:self.data], nil, nil, nil);
|
||||
}
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you 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.
|
||||
*/
|
||||
#import <Quartz/Quartz.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <Cordova/CDVPlugin.h>
|
||||
|
||||
|
||||
|
||||
enum CDVDestinationType {
|
||||
DestinationTypeDataUrl = 0,
|
||||
DestinationTypeFileUri
|
||||
};
|
||||
typedef NSUInteger CDVDestinationType;
|
||||
|
||||
enum CDVSourceType {
|
||||
SourceTypePhotoLibrary = 0,
|
||||
SourceTypeCamera,
|
||||
SourceTypePhotoAlbum
|
||||
};
|
||||
typedef NSUInteger CDVSourceType;
|
||||
|
||||
enum CDVEncodingType {
|
||||
EncodingTypeJPEG = 0,
|
||||
EncodingTypePNG
|
||||
};
|
||||
typedef NSUInteger CDVEncodingType;
|
||||
|
||||
enum CDVMediaType {
|
||||
MediaTypePicture = 0,
|
||||
MediaTypeVideo,
|
||||
MediaTypeAll
|
||||
};
|
||||
typedef NSUInteger CDVMediaType;
|
||||
|
||||
|
||||
// ======================================================================= //
|
||||
|
||||
|
||||
@interface CDVPictureOptions : NSObject
|
||||
|
||||
@property (strong) NSNumber *quality;
|
||||
@property (assign) CDVDestinationType destinationType;
|
||||
@property (assign) CDVSourceType sourceType;
|
||||
@property (assign) CGSize targetSize;
|
||||
@property (assign) CDVEncodingType encodingType;
|
||||
@property (assign) CDVMediaType mediaType;
|
||||
@property (assign) BOOL allowsEditing;
|
||||
@property (assign) BOOL correctOrientation;
|
||||
@property (assign) BOOL saveToPhotoAlbum;
|
||||
|
||||
+ (instancetype) createFromTakePictureArguments:(CDVInvokedUrlCommand *)command;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
// ======================================================================= //
|
||||
|
||||
|
||||
@interface CDVCamera : CDVPlugin
|
||||
|
||||
- (void)takePicture:(CDVInvokedUrlCommand *)command;
|
||||
- (void)cleanup:(CDVInvokedUrlCommand *)command;
|
||||
|
||||
@end
|
||||
@@ -1,258 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you 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.
|
||||
*/
|
||||
#import "CDVCamera.h"
|
||||
|
||||
|
||||
@implementation CDVPictureOptions
|
||||
|
||||
+ (instancetype) createFromTakePictureArguments:(CDVInvokedUrlCommand*)command {
|
||||
CDVPictureOptions *pictureOptions = [[CDVPictureOptions alloc] init];
|
||||
|
||||
pictureOptions.quality = [command argumentAtIndex:0 withDefault:@(50)];
|
||||
pictureOptions.destinationType = [[command argumentAtIndex:1 withDefault:@(DestinationTypeFileUri)] unsignedIntegerValue];
|
||||
pictureOptions.sourceType = [[command argumentAtIndex:2 withDefault:@(SourceTypeCamera)] unsignedIntegerValue];
|
||||
|
||||
NSNumber *targetWidth = [command argumentAtIndex:3 withDefault:nil];
|
||||
NSNumber *targetHeight = [command argumentAtIndex:4 withDefault:nil];
|
||||
pictureOptions.targetSize = CGSizeMake(0, 0);
|
||||
if ((targetWidth != nil) && (targetHeight != nil)) {
|
||||
pictureOptions.targetSize = CGSizeMake([targetWidth floatValue], [targetHeight floatValue]);
|
||||
}
|
||||
|
||||
pictureOptions.encodingType = [[command argumentAtIndex:5 withDefault:@(EncodingTypeJPEG)] unsignedIntegerValue];
|
||||
pictureOptions.mediaType = [[command argumentAtIndex:6 withDefault:@(MediaTypePicture)] unsignedIntegerValue];
|
||||
pictureOptions.allowsEditing = [[command argumentAtIndex:7 withDefault:@(NO)] boolValue];
|
||||
pictureOptions.correctOrientation = [[command argumentAtIndex:8 withDefault:@(NO)] boolValue];
|
||||
pictureOptions.saveToPhotoAlbum = [[command argumentAtIndex:9 withDefault:@(NO)] boolValue];
|
||||
|
||||
return pictureOptions;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
// ======================================================================= //
|
||||
|
||||
|
||||
@implementation CDVCamera
|
||||
|
||||
/*!
|
||||
Static array that stores the temporary created files allowing to delete them when calling navigator.camera.cleanup(...)
|
||||
*/
|
||||
static NSMutableArray *cleanUpFiles;
|
||||
|
||||
+ (void)initialize {
|
||||
cleanUpFiles = [NSMutableArray array];
|
||||
}
|
||||
|
||||
- (void)takePicture:(CDVInvokedUrlCommand *)command {
|
||||
CDVPictureOptions *pictureOptions = [CDVPictureOptions createFromTakePictureArguments:command];
|
||||
if (pictureOptions.sourceType == SourceTypeCamera) {
|
||||
[self takePictureFromCamera:command withOptions:pictureOptions];
|
||||
} else {
|
||||
[self takePictureFromFile:command withOptions:pictureOptions];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cleanup:(CDVInvokedUrlCommand*)command {
|
||||
[self.commandDelegate runInBackground:^{
|
||||
if (cleanUpFiles.count > 0) {
|
||||
for (int i=0; i<cleanUpFiles.count; i++) {
|
||||
NSString *path = [cleanUpFiles objectAtIndex:i];
|
||||
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
|
||||
}
|
||||
|
||||
[cleanUpFiles removeAllObjects];
|
||||
|
||||
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Camera
|
||||
|
||||
/*!
|
||||
Takes a picture from the iSight camera using the default OS dialog.
|
||||
@see https://developer.apple.com/documentation/quartz/ikpicturetaker
|
||||
*/
|
||||
- (void)takePictureFromCamera:(CDVInvokedUrlCommand *)command withOptions:(CDVPictureOptions *)pictureOptions {
|
||||
IKPictureTaker *pictureTaker = [IKPictureTaker pictureTaker];
|
||||
[pictureTaker setValue:[NSNumber numberWithBool:YES] forKey:IKPictureTakerAllowsVideoCaptureKey];
|
||||
[pictureTaker setValue:[NSNumber numberWithBool:NO] forKey:IKPictureTakerAllowsFileChoosingKey];
|
||||
[pictureTaker setValue:[NSNumber numberWithBool:pictureOptions.allowsEditing] forKey:IKPictureTakerShowEffectsKey];
|
||||
[pictureTaker setValue:[NSNumber numberWithBool:pictureOptions.allowsEditing] forKey:IKPictureTakerAllowsEditingKey];
|
||||
|
||||
NSDictionary *contextInfo = @{ @"command": command, @"pictureOptions" : pictureOptions};
|
||||
[pictureTaker beginPictureTakerSheetForWindow:self.viewController.contentView.window withDelegate:self didEndSelector:@selector(pictureTakerDidEnd:returnCode:contextInfo:) contextInfo:(void *)CFBridgingRetain(contextInfo)];
|
||||
|
||||
}
|
||||
|
||||
- (void)pictureTakerDidEnd:(IKPictureTaker *)pictureTaker returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
|
||||
if (returnCode == NSOKButton) {
|
||||
NSDictionary *contextInfoDictionary = (NSDictionary *)CFBridgingRelease(contextInfo);
|
||||
CDVInvokedUrlCommand *command = [contextInfoDictionary valueForKey:@"command"];
|
||||
CDVPictureOptions *pictureOptions = [contextInfoDictionary valueForKey:@"pictureOptions"];
|
||||
|
||||
[self returnImage:pictureTaker.outputImage command:command options:pictureOptions];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - File
|
||||
|
||||
/*!
|
||||
Allows to select an image or video using the system native dialog.
|
||||
*/
|
||||
- (void)takePictureFromFile:(CDVInvokedUrlCommand *)command withOptions:(CDVPictureOptions *)pictureOptions {
|
||||
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
|
||||
openPanel.canChooseFiles = YES;
|
||||
openPanel.canChooseDirectories = NO;
|
||||
openPanel.canCreateDirectories = YES;
|
||||
openPanel.allowsMultipleSelection = NO;
|
||||
|
||||
NSMutableArray *allowedTypes = [NSMutableArray array];
|
||||
if (pictureOptions.mediaType == MediaTypePicture || pictureOptions.mediaType == MediaTypeAll) {
|
||||
[allowedTypes addObjectsFromArray:[NSImage imageTypes]];
|
||||
}
|
||||
if (pictureOptions.mediaType == MediaTypeVideo || pictureOptions.mediaType == MediaTypeAll) {
|
||||
[allowedTypes addObjectsFromArray:@[(NSString *)kUTTypeMovie]];
|
||||
}
|
||||
[openPanel setAllowedFileTypes:allowedTypes];
|
||||
|
||||
[openPanel beginSheetModalForWindow:self.viewController.contentView.window completionHandler:^(NSInteger result) {
|
||||
if (result == NSOKButton) {
|
||||
NSURL *fileURL = [openPanel.URLs objectAtIndex:0];
|
||||
|
||||
if ([self fileIsImage:fileURL]) {
|
||||
NSImage *image = [[NSImage alloc] initWithContentsOfFile:fileURL.path];
|
||||
[self returnImage:image command:command options:pictureOptions];
|
||||
} else {
|
||||
if (pictureOptions.destinationType == DestinationTypeDataUrl) {
|
||||
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Camera.DestinationType.DATA_URL is only available with image files"];
|
||||
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
||||
} else {
|
||||
[self returnUri:fileURL.path command:command options:pictureOptions];
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Common
|
||||
|
||||
/*!
|
||||
Returns to JavaScript a URI.
|
||||
Called when Camera.DestinationType.FILE_URI.
|
||||
*/
|
||||
- (void)returnUri:(NSString *)path command:(CDVInvokedUrlCommand *)command options:(CDVPictureOptions *)pictureOptions {
|
||||
NSString *protocol = (pictureOptions.destinationType == DestinationTypeFileUri) ? @"file://" : @"";
|
||||
NSString *uri = [NSString stringWithFormat:@"%@%@", protocol, path];
|
||||
|
||||
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:uri];
|
||||
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns to JavaScript a base64 encoded image.
|
||||
Called when Camera.DestinationType.DATA_URL.
|
||||
*/
|
||||
- (void)returnImage:(NSImage *)image command:(CDVInvokedUrlCommand *)command options:(CDVPictureOptions *)pictureOptions {
|
||||
[self.commandDelegate runInBackground:^{
|
||||
NSData *processedImageData = [self processImage:image options:pictureOptions];
|
||||
|
||||
if (pictureOptions.destinationType == DestinationTypeDataUrl) {
|
||||
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[processedImageData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]];
|
||||
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
||||
} else {
|
||||
NSString *tempFilePath = [self uniqueImageName:pictureOptions];
|
||||
[processedImageData writeToFile:tempFilePath atomically:YES];
|
||||
[cleanUpFiles addObject:tempFilePath];
|
||||
[self returnUri:tempFilePath command:command options:pictureOptions];
|
||||
}
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
Top level method to apply the size and quality required changes to an image.
|
||||
*/
|
||||
- (NSData *)processImage:(NSImage *)image options:(CDVPictureOptions *)pictureOptions {
|
||||
NSImage *sourceImage = image;
|
||||
if (pictureOptions.targetSize.width > 0 && pictureOptions.targetSize.height > 0) {
|
||||
sourceImage = [self resizeImage:sourceImage toSize:pictureOptions.targetSize];
|
||||
}
|
||||
|
||||
CGImageRef cgRef = [sourceImage CGImageForProposedRect:NULL context:nil hints:nil];
|
||||
NSBitmapImageRep *imageRepresentation = [[NSBitmapImageRep alloc] initWithCGImage:cgRef];
|
||||
|
||||
NSData *data = (pictureOptions.encodingType == EncodingTypeJPEG)
|
||||
? [imageRepresentation representationUsingType:NSJPEGFileType properties:@{NSImageCompressionFactor: [NSNumber numberWithFloat:pictureOptions.quality.floatValue/100.f]}]
|
||||
: [imageRepresentation representationUsingType:NSPNGFileType properties:@{NSImageCompressionFactor: @1.0}];
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/*!
|
||||
Auxiliar method to resize an image.
|
||||
*/
|
||||
- (NSImage *)resizeImage:(NSImage *)image toSize:(CGSize)newSize {
|
||||
CGFloat aspectWidth = newSize.width / image.size.width;
|
||||
CGFloat aspectHeight = newSize.height / image.size.height;
|
||||
CGFloat aspectRatio = MIN(aspectWidth, aspectHeight);
|
||||
|
||||
CGSize scaledSize = NSMakeSize(image.size.width*aspectRatio, image.size.height*aspectRatio);
|
||||
|
||||
NSImage *smallImage = [[NSImage alloc] initWithSize: scaledSize];
|
||||
[smallImage lockFocus];
|
||||
[image setSize: scaledSize];
|
||||
[[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
|
||||
[image drawAtPoint:NSZeroPoint fromRect:CGRectMake(0, 0, scaledSize.width, scaledSize.height) operation:NSCompositeCopy fraction:1.0];
|
||||
[smallImage unlockFocus];
|
||||
return smallImage;
|
||||
}
|
||||
|
||||
/*!
|
||||
Auxiliar method to know if a given file is an image or not.
|
||||
*/
|
||||
- (BOOL)fileIsImage:(NSURL *)fileURL {
|
||||
NSString *type;
|
||||
BOOL isImage = NO;
|
||||
|
||||
if ([fileURL getResourceValue:&type forKey:NSURLTypeIdentifierKey error:nil]) {
|
||||
isImage = [[NSImage imageTypes] containsObject:type];
|
||||
}
|
||||
|
||||
return isImage;
|
||||
}
|
||||
|
||||
/*!
|
||||
Auxiliar method that generates an unique filename for an image in the temporary directory.
|
||||
*/
|
||||
- (NSString *)uniqueImageName:(CDVPictureOptions *)pictureOptions {
|
||||
NSString *tempDir = NSTemporaryDirectory();
|
||||
NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString] ;
|
||||
NSString *extension = (pictureOptions.encodingType == EncodingTypeJPEG) ? @"jpeg" : @"png";
|
||||
NSString *uniqueFileName = [NSString stringWithFormat:@"%@%@.%@", tempDir, guid, extension];
|
||||
return uniqueFileName;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,861 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/* global Windows:true, URL:true, module:true, require:true, WinJS:true */
|
||||
|
||||
var Camera = require('./Camera');
|
||||
|
||||
var getAppData = function () {
|
||||
return Windows.Storage.ApplicationData.current;
|
||||
};
|
||||
var encodeToBase64String = function (buffer) {
|
||||
return Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer);
|
||||
};
|
||||
var OptUnique = Windows.Storage.CreationCollisionOption.generateUniqueName;
|
||||
var CapMSType = Windows.Media.Capture.MediaStreamType;
|
||||
var webUIApp = Windows.UI.WebUI.WebUIApplication;
|
||||
var fileIO = Windows.Storage.FileIO;
|
||||
var pickerLocId = Windows.Storage.Pickers.PickerLocationId;
|
||||
|
||||
module.exports = {
|
||||
|
||||
// args will contain :
|
||||
// ... it is an array, so be careful
|
||||
// 0 quality:50,
|
||||
// 1 destinationType:Camera.DestinationType.FILE_URI,
|
||||
// 2 sourceType:Camera.PictureSourceType.CAMERA,
|
||||
// 3 targetWidth:-1,
|
||||
// 4 targetHeight:-1,
|
||||
// 5 encodingType:Camera.EncodingType.JPEG,
|
||||
// 6 mediaType:Camera.MediaType.PICTURE,
|
||||
// 7 allowEdit:false,
|
||||
// 8 correctOrientation:false,
|
||||
// 9 saveToPhotoAlbum:false,
|
||||
// 10 popoverOptions:null
|
||||
// 11 cameraDirection:0
|
||||
|
||||
takePicture: function (successCallback, errorCallback, args) {
|
||||
var sourceType = args[2];
|
||||
|
||||
if (sourceType !== Camera.PictureSourceType.CAMERA) {
|
||||
takePictureFromFile(successCallback, errorCallback, args);
|
||||
} else {
|
||||
takePictureFromCamera(successCallback, errorCallback, args);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// https://msdn.microsoft.com/en-us/library/windows/apps/ff462087(v=vs.105).aspx
|
||||
var windowsVideoContainers = ['.avi', '.flv', '.asx', '.asf', '.mov', '.mp4', '.mpg', '.rm', '.srt', '.swf', '.wmv', '.vob'];
|
||||
var windowsPhoneVideoContainers = ['.avi', '.3gp', '.3g2', '.wmv', '.3gp', '.3g2', '.mp4', '.m4v'];
|
||||
|
||||
// Default aspect ratio 1.78 (16:9 hd video standard)
|
||||
var DEFAULT_ASPECT_RATIO = '1.8';
|
||||
|
||||
// Highest possible z-index supported across browsers. Anything used above is converted to this value.
|
||||
var HIGHEST_POSSIBLE_Z_INDEX = 2147483647;
|
||||
|
||||
// Resize method
|
||||
function resizeImage (successCallback, errorCallback, file, targetWidth, targetHeight, encodingType) {
|
||||
var tempPhotoFileName = '';
|
||||
var targetContentType = '';
|
||||
|
||||
if (encodingType === Camera.EncodingType.PNG) {
|
||||
tempPhotoFileName = 'camera_cordova_temp_return.png';
|
||||
targetContentType = 'image/png';
|
||||
} else {
|
||||
tempPhotoFileName = 'camera_cordova_temp_return.jpg';
|
||||
targetContentType = 'image/jpeg';
|
||||
}
|
||||
|
||||
var storageFolder = getAppData().localFolder;
|
||||
file.copyAsync(storageFolder, file.name, Windows.Storage.NameCollisionOption.replaceExisting)
|
||||
.then(function (storageFile) {
|
||||
return fileIO.readBufferAsync(storageFile);
|
||||
})
|
||||
.then(function (buffer) {
|
||||
var strBase64 = encodeToBase64String(buffer);
|
||||
var imageData = 'data:' + file.contentType + ';base64,' + strBase64;
|
||||
var image = new Image(); /* eslint no-undef : 0 */
|
||||
image.src = imageData;
|
||||
image.onload = function () {
|
||||
var ratio = Math.min(targetWidth / this.width, targetHeight / this.height);
|
||||
var imageWidth = ratio * this.width;
|
||||
var imageHeight = ratio * this.height;
|
||||
|
||||
var canvas = document.createElement('canvas');
|
||||
var storageFileName;
|
||||
|
||||
canvas.width = imageWidth;
|
||||
canvas.height = imageHeight;
|
||||
|
||||
canvas.getContext('2d').drawImage(this, 0, 0, imageWidth, imageHeight);
|
||||
|
||||
var fileContent = canvas.toDataURL(targetContentType).split(',')[1];
|
||||
|
||||
var storageFolder = getAppData().localFolder;
|
||||
|
||||
storageFolder.createFileAsync(tempPhotoFileName, OptUnique)
|
||||
.then(function (storagefile) {
|
||||
var content = Windows.Security.Cryptography.CryptographicBuffer.decodeFromBase64String(fileContent);
|
||||
storageFileName = storagefile.name;
|
||||
return fileIO.writeBufferAsync(storagefile, content);
|
||||
})
|
||||
.done(function () {
|
||||
successCallback('ms-appdata:///local/' + storageFileName);
|
||||
}, errorCallback);
|
||||
};
|
||||
})
|
||||
.done(null, function (err) {
|
||||
errorCallback(err);
|
||||
});
|
||||
}
|
||||
|
||||
// Because of asynchronous method, so let the successCallback be called in it.
|
||||
function resizeImageBase64 (successCallback, errorCallback, file, targetWidth, targetHeight) {
|
||||
fileIO.readBufferAsync(file).done(function (buffer) {
|
||||
var strBase64 = encodeToBase64String(buffer);
|
||||
var imageData = 'data:' + file.contentType + ';base64,' + strBase64;
|
||||
|
||||
var image = new Image(); /* eslint no-undef : 0 */
|
||||
image.src = imageData;
|
||||
|
||||
image.onload = function () {
|
||||
var ratio = Math.min(targetWidth / this.width, targetHeight / this.height);
|
||||
var imageWidth = ratio * this.width;
|
||||
var imageHeight = ratio * this.height;
|
||||
var canvas = document.createElement('canvas');
|
||||
|
||||
canvas.width = imageWidth;
|
||||
canvas.height = imageHeight;
|
||||
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(this, 0, 0, imageWidth, imageHeight);
|
||||
|
||||
// The resized file ready for upload
|
||||
var finalFile = canvas.toDataURL(file.contentType);
|
||||
|
||||
// Remove the prefix such as "data:" + contentType + ";base64," , in order to meet the Cordova API.
|
||||
var arr = finalFile.split(',');
|
||||
var newStr = finalFile.substr(arr[0].length + 1);
|
||||
successCallback(newStr);
|
||||
};
|
||||
}, function (err) { errorCallback(err); });
|
||||
}
|
||||
|
||||
function takePictureFromFile (successCallback, errorCallback, args) {
|
||||
// Detect Windows Phone
|
||||
if (navigator.appVersion.indexOf('Windows Phone 8.1') >= 0) {
|
||||
takePictureFromFileWP(successCallback, errorCallback, args);
|
||||
} else {
|
||||
takePictureFromFileWindows(successCallback, errorCallback, args);
|
||||
}
|
||||
}
|
||||
|
||||
function takePictureFromFileWP (successCallback, errorCallback, args) {
|
||||
var mediaType = args[6];
|
||||
var destinationType = args[1];
|
||||
var targetWidth = args[3];
|
||||
var targetHeight = args[4];
|
||||
var encodingType = args[5];
|
||||
|
||||
/*
|
||||
Need to add and remove an event listener to catch activation state
|
||||
Using FileOpenPicker will suspend the app and it's required to catch the PickSingleFileAndContinue
|
||||
https://msdn.microsoft.com/en-us/library/windows/apps/xaml/dn631755.aspx
|
||||
*/
|
||||
var filePickerActivationHandler = function (eventArgs) {
|
||||
if (eventArgs.kind === Windows.ApplicationModel.Activation.ActivationKind.pickFileContinuation) {
|
||||
var file = eventArgs.files[0];
|
||||
if (!file) {
|
||||
errorCallback("User didn't choose a file.");
|
||||
webUIApp.removeEventListener('activated', filePickerActivationHandler);
|
||||
return;
|
||||
}
|
||||
if (destinationType === Camera.DestinationType.FILE_URI) {
|
||||
if (targetHeight > 0 && targetWidth > 0) {
|
||||
resizeImage(successCallback, errorCallback, file, targetWidth, targetHeight, encodingType);
|
||||
} else {
|
||||
var storageFolder = getAppData().localFolder;
|
||||
file.copyAsync(storageFolder, file.name, Windows.Storage.NameCollisionOption.replaceExisting).done(function (storageFile) {
|
||||
successCallback(URL.createObjectURL(storageFile));
|
||||
}, function () {
|
||||
errorCallback("Can't access localStorage folder.");
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (targetHeight > 0 && targetWidth > 0) {
|
||||
resizeImageBase64(successCallback, errorCallback, file, targetWidth, targetHeight);
|
||||
} else {
|
||||
fileIO.readBufferAsync(file).done(function (buffer) {
|
||||
var strBase64 = encodeToBase64String(buffer);
|
||||
successCallback(strBase64);
|
||||
}, errorCallback);
|
||||
}
|
||||
}
|
||||
webUIApp.removeEventListener('activated', filePickerActivationHandler);
|
||||
}
|
||||
};
|
||||
|
||||
var fileOpenPicker = new Windows.Storage.Pickers.FileOpenPicker();
|
||||
if (mediaType === Camera.MediaType.PICTURE) {
|
||||
fileOpenPicker.fileTypeFilter.replaceAll(['.png', '.jpg', '.jpeg']);
|
||||
fileOpenPicker.suggestedStartLocation = pickerLocId.picturesLibrary;
|
||||
} else if (mediaType === Camera.MediaType.VIDEO) {
|
||||
fileOpenPicker.fileTypeFilter.replaceAll(windowsPhoneVideoContainers);
|
||||
fileOpenPicker.suggestedStartLocation = pickerLocId.videosLibrary;
|
||||
} else {
|
||||
fileOpenPicker.fileTypeFilter.replaceAll(['*']);
|
||||
fileOpenPicker.suggestedStartLocation = pickerLocId.documentsLibrary;
|
||||
}
|
||||
|
||||
webUIApp.addEventListener('activated', filePickerActivationHandler);
|
||||
fileOpenPicker.pickSingleFileAndContinue();
|
||||
}
|
||||
|
||||
function takePictureFromFileWindows (successCallback, errorCallback, args) {
|
||||
var mediaType = args[6];
|
||||
var destinationType = args[1];
|
||||
var targetWidth = args[3];
|
||||
var targetHeight = args[4];
|
||||
var encodingType = args[5];
|
||||
|
||||
var fileOpenPicker = new Windows.Storage.Pickers.FileOpenPicker();
|
||||
if (mediaType === Camera.MediaType.PICTURE) {
|
||||
fileOpenPicker.fileTypeFilter.replaceAll(['.png', '.jpg', '.jpeg']);
|
||||
fileOpenPicker.suggestedStartLocation = pickerLocId.picturesLibrary;
|
||||
} else if (mediaType === Camera.MediaType.VIDEO) {
|
||||
fileOpenPicker.fileTypeFilter.replaceAll(windowsVideoContainers);
|
||||
fileOpenPicker.suggestedStartLocation = pickerLocId.videosLibrary;
|
||||
} else {
|
||||
fileOpenPicker.fileTypeFilter.replaceAll(['*']);
|
||||
fileOpenPicker.suggestedStartLocation = pickerLocId.documentsLibrary;
|
||||
}
|
||||
|
||||
fileOpenPicker.pickSingleFileAsync().done(function (file) {
|
||||
if (!file) {
|
||||
errorCallback("User didn't choose a file.");
|
||||
return;
|
||||
}
|
||||
if (destinationType === Camera.DestinationType.FILE_URI) {
|
||||
if (targetHeight > 0 && targetWidth > 0) {
|
||||
resizeImage(successCallback, errorCallback, file, targetWidth, targetHeight, encodingType);
|
||||
} else {
|
||||
var storageFolder = getAppData().localFolder;
|
||||
file.copyAsync(storageFolder, file.name, Windows.Storage.NameCollisionOption.replaceExisting).done(function (storageFile) {
|
||||
successCallback(URL.createObjectURL(storageFile));
|
||||
}, function () {
|
||||
errorCallback("Can't access localStorage folder.");
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (targetHeight > 0 && targetWidth > 0) {
|
||||
resizeImageBase64(successCallback, errorCallback, file, targetWidth, targetHeight);
|
||||
} else {
|
||||
fileIO.readBufferAsync(file).done(function (buffer) {
|
||||
var strBase64 = encodeToBase64String(buffer);
|
||||
successCallback(strBase64);
|
||||
}, errorCallback);
|
||||
}
|
||||
}
|
||||
}, function () {
|
||||
errorCallback("User didn't choose a file.");
|
||||
});
|
||||
}
|
||||
|
||||
function takePictureFromCamera (successCallback, errorCallback, args) {
|
||||
// Check if necessary API available
|
||||
if (!Windows.Media.Capture.CameraCaptureUI) {
|
||||
takePictureFromCameraWP(successCallback, errorCallback, args);
|
||||
} else {
|
||||
takePictureFromCameraWindows(successCallback, errorCallback, args);
|
||||
}
|
||||
}
|
||||
|
||||
function takePictureFromCameraWP (successCallback, errorCallback, args) {
|
||||
// We are running on WP8.1 which lacks CameraCaptureUI class
|
||||
// so we need to use MediaCapture class instead and implement custom UI for camera
|
||||
var destinationType = args[1];
|
||||
var targetWidth = args[3];
|
||||
var targetHeight = args[4];
|
||||
var encodingType = args[5];
|
||||
var saveToPhotoAlbum = args[9];
|
||||
var cameraDirection = args[11];
|
||||
var capturePreview = null;
|
||||
var cameraCaptureButton = null;
|
||||
var cameraCancelButton = null;
|
||||
var capture = null;
|
||||
var captureSettings = null;
|
||||
var CaptureNS = Windows.Media.Capture;
|
||||
var sensor = null;
|
||||
|
||||
function createCameraUI () {
|
||||
// create style for take and cancel buttons
|
||||
var buttonStyle = 'width:45%;padding: 10px 16px;font-size: 18px;line-height: 1.3333333;color: #333;background-color: #fff;border-color: #ccc; border: 1px solid transparent;border-radius: 6px; display: block; margin: 20px; z-index: 1000;border-color: #adadad;';
|
||||
|
||||
// Create fullscreen preview
|
||||
// z-order style element for capturePreview and cameraCancelButton elts
|
||||
// is necessary to avoid overriding by another page elements, -1 sometimes is not enough
|
||||
capturePreview = document.createElement('video');
|
||||
capturePreview.style.cssText = 'position: fixed; left: 0; top: 0; width: 100%; height: 100%; z-index: ' + (HIGHEST_POSSIBLE_Z_INDEX - 1) + ';';
|
||||
|
||||
// Create capture button
|
||||
cameraCaptureButton = document.createElement('button');
|
||||
cameraCaptureButton.innerText = 'Take';
|
||||
cameraCaptureButton.style.cssText = buttonStyle + 'position: fixed; left: 0; bottom: 0; margin: 20px; z-index: ' + HIGHEST_POSSIBLE_Z_INDEX + ';';
|
||||
|
||||
// Create cancel button
|
||||
cameraCancelButton = document.createElement('button');
|
||||
cameraCancelButton.innerText = 'Cancel';
|
||||
cameraCancelButton.style.cssText = buttonStyle + 'position: fixed; right: 0; bottom: 0; margin: 20px; z-index: ' + HIGHEST_POSSIBLE_Z_INDEX + ';';
|
||||
|
||||
capture = new CaptureNS.MediaCapture();
|
||||
|
||||
captureSettings = new CaptureNS.MediaCaptureInitializationSettings();
|
||||
captureSettings.streamingCaptureMode = CaptureNS.StreamingCaptureMode.video;
|
||||
}
|
||||
|
||||
function continueVideoOnFocus () {
|
||||
// if preview is defined it would be stuck, play it
|
||||
if (capturePreview) {
|
||||
capturePreview.play();
|
||||
}
|
||||
}
|
||||
|
||||
function startCameraPreview () {
|
||||
// Search for available camera devices
|
||||
// This is necessary to detect which camera (front or back) we should use
|
||||
var DeviceEnum = Windows.Devices.Enumeration;
|
||||
var expectedPanel = cameraDirection === 1 ? DeviceEnum.Panel.front : DeviceEnum.Panel.back;
|
||||
|
||||
// Add focus event handler to capture the event when user suspends the app and comes back while the preview is on
|
||||
window.addEventListener('focus', continueVideoOnFocus);
|
||||
|
||||
DeviceEnum.DeviceInformation.findAllAsync(DeviceEnum.DeviceClass.videoCapture).then(function (devices) {
|
||||
if (devices.length <= 0) {
|
||||
destroyCameraPreview();
|
||||
errorCallback('Camera not found');
|
||||
return;
|
||||
}
|
||||
|
||||
devices.forEach(function (currDev) {
|
||||
if (currDev.enclosureLocation.panel && currDev.enclosureLocation.panel === expectedPanel) {
|
||||
captureSettings.videoDeviceId = currDev.id;
|
||||
}
|
||||
});
|
||||
|
||||
captureSettings.photoCaptureSource = Windows.Media.Capture.PhotoCaptureSource.photo;
|
||||
|
||||
return capture.initializeAsync(captureSettings);
|
||||
}).then(function () {
|
||||
// create focus control if available
|
||||
var VideoDeviceController = capture.videoDeviceController;
|
||||
var FocusControl = VideoDeviceController.focusControl;
|
||||
|
||||
if (FocusControl.supported === true) {
|
||||
capturePreview.addEventListener('click', function () {
|
||||
// Make sure function isn't called again before previous focus is completed
|
||||
if (this.getAttribute('clicked') === '1') {
|
||||
return false;
|
||||
} else {
|
||||
this.setAttribute('clicked', '1');
|
||||
}
|
||||
var preset = Windows.Media.Devices.FocusPreset.autoNormal;
|
||||
var parent = this;
|
||||
FocusControl.setPresetAsync(preset).done(function () {
|
||||
// set the clicked attribute back to '0' to allow focus again
|
||||
parent.setAttribute('clicked', '0');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// msdn.microsoft.com/en-us/library/windows/apps/hh452807.aspx
|
||||
capturePreview.msZoom = true;
|
||||
capturePreview.src = URL.createObjectURL(capture);
|
||||
capturePreview.play();
|
||||
|
||||
// Bind events to controls
|
||||
sensor = Windows.Devices.Sensors.SimpleOrientationSensor.getDefault();
|
||||
if (sensor !== null) {
|
||||
sensor.addEventListener('orientationchanged', onOrientationChange);
|
||||
}
|
||||
|
||||
// add click events to capture and cancel buttons
|
||||
cameraCaptureButton.addEventListener('click', onCameraCaptureButtonClick);
|
||||
cameraCancelButton.addEventListener('click', onCameraCancelButtonClick);
|
||||
|
||||
// Change default orientation
|
||||
if (sensor) {
|
||||
setPreviewRotation(sensor.getCurrentOrientation());
|
||||
} else {
|
||||
setPreviewRotation(Windows.Graphics.Display.DisplayInformation.getForCurrentView().currentOrientation);
|
||||
}
|
||||
|
||||
// Get available aspect ratios
|
||||
var aspectRatios = getAspectRatios(capture);
|
||||
|
||||
// Couldn't find a good ratio
|
||||
if (aspectRatios.length === 0) {
|
||||
destroyCameraPreview();
|
||||
errorCallback('There\'s not a good aspect ratio available');
|
||||
return;
|
||||
}
|
||||
|
||||
// add elements to body
|
||||
document.body.appendChild(capturePreview);
|
||||
document.body.appendChild(cameraCaptureButton);
|
||||
document.body.appendChild(cameraCancelButton);
|
||||
|
||||
if (aspectRatios.indexOf(DEFAULT_ASPECT_RATIO) > -1) {
|
||||
return setAspectRatio(capture, DEFAULT_ASPECT_RATIO);
|
||||
} else {
|
||||
// Doesn't support 16:9 - pick next best
|
||||
return setAspectRatio(capture, aspectRatios[0]);
|
||||
}
|
||||
}).done(null, function (err) {
|
||||
destroyCameraPreview();
|
||||
errorCallback('Camera intitialization error ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
function destroyCameraPreview () {
|
||||
// If sensor is available, remove event listener
|
||||
if (sensor !== null) {
|
||||
sensor.removeEventListener('orientationchanged', onOrientationChange);
|
||||
}
|
||||
|
||||
// Pause and dispose preview element
|
||||
capturePreview.pause();
|
||||
capturePreview.src = null;
|
||||
|
||||
// Remove event listeners from buttons
|
||||
cameraCaptureButton.removeEventListener('click', onCameraCaptureButtonClick);
|
||||
cameraCancelButton.removeEventListener('click', onCameraCancelButtonClick);
|
||||
|
||||
// Remove the focus event handler
|
||||
window.removeEventListener('focus', continueVideoOnFocus);
|
||||
|
||||
// Remove elements
|
||||
[capturePreview, cameraCaptureButton, cameraCancelButton].forEach(function (elem) {
|
||||
if (elem /* && elem in document.body.childNodes */) {
|
||||
document.body.removeChild(elem);
|
||||
}
|
||||
});
|
||||
|
||||
// Stop and dispose media capture manager
|
||||
if (capture) {
|
||||
capture.stopRecordAsync();
|
||||
capture = null;
|
||||
}
|
||||
}
|
||||
|
||||
function captureAction () {
|
||||
var encodingProperties;
|
||||
var fileName;
|
||||
var tempFolder = getAppData().temporaryFolder;
|
||||
|
||||
if (encodingType === Camera.EncodingType.PNG) {
|
||||
fileName = 'photo.png';
|
||||
encodingProperties = Windows.Media.MediaProperties.ImageEncodingProperties.createPng();
|
||||
} else {
|
||||
fileName = 'photo.jpg';
|
||||
encodingProperties = Windows.Media.MediaProperties.ImageEncodingProperties.createJpeg();
|
||||
}
|
||||
|
||||
tempFolder.createFileAsync(fileName, OptUnique)
|
||||
.then(function (tempCapturedFile) {
|
||||
return new WinJS.Promise(function (complete) {
|
||||
var photoStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
|
||||
var finalStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
|
||||
capture.capturePhotoToStreamAsync(encodingProperties, photoStream)
|
||||
.then(function () {
|
||||
return Windows.Graphics.Imaging.BitmapDecoder.createAsync(photoStream);
|
||||
})
|
||||
.then(function (dec) {
|
||||
finalStream.size = 0; // BitmapEncoder requires the output stream to be empty
|
||||
return Windows.Graphics.Imaging.BitmapEncoder.createForTranscodingAsync(finalStream, dec);
|
||||
})
|
||||
.then(function (enc) {
|
||||
// We need to rotate the photo wrt sensor orientation
|
||||
enc.bitmapTransform.rotation = orientationToRotation(sensor.getCurrentOrientation());
|
||||
return enc.flushAsync();
|
||||
})
|
||||
.then(function () {
|
||||
return tempCapturedFile.openAsync(Windows.Storage.FileAccessMode.readWrite);
|
||||
})
|
||||
.then(function (fileStream) {
|
||||
return Windows.Storage.Streams.RandomAccessStream.copyAndCloseAsync(finalStream, fileStream);
|
||||
})
|
||||
.done(function () {
|
||||
photoStream.close();
|
||||
finalStream.close();
|
||||
complete(tempCapturedFile);
|
||||
}, function () {
|
||||
photoStream.close();
|
||||
finalStream.close();
|
||||
throw new Error('An error has occured while capturing the photo.');
|
||||
});
|
||||
});
|
||||
})
|
||||
.done(function (capturedFile) {
|
||||
destroyCameraPreview();
|
||||
savePhoto(capturedFile, {
|
||||
destinationType: destinationType,
|
||||
targetHeight: targetHeight,
|
||||
targetWidth: targetWidth,
|
||||
encodingType: encodingType,
|
||||
saveToPhotoAlbum: saveToPhotoAlbum
|
||||
}, successCallback, errorCallback);
|
||||
}, function (err) {
|
||||
destroyCameraPreview();
|
||||
errorCallback(err);
|
||||
});
|
||||
}
|
||||
|
||||
function getAspectRatios (capture) {
|
||||
var videoDeviceController = capture.videoDeviceController;
|
||||
var photoAspectRatios = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.photo).map(function (element) {
|
||||
return (element.width / element.height).toFixed(1);
|
||||
}).filter(function (element, index, array) { return (index === array.indexOf(element)); });
|
||||
|
||||
var videoAspectRatios = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.videoRecord).map(function (element) {
|
||||
return (element.width / element.height).toFixed(1);
|
||||
}).filter(function (element, index, array) { return (index === array.indexOf(element)); });
|
||||
|
||||
var videoPreviewAspectRatios = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.videoPreview).map(function (element) {
|
||||
return (element.width / element.height).toFixed(1);
|
||||
}).filter(function (element, index, array) { return (index === array.indexOf(element)); });
|
||||
|
||||
var allAspectRatios = [].concat(photoAspectRatios, videoAspectRatios, videoPreviewAspectRatios);
|
||||
|
||||
var aspectObj = allAspectRatios.reduce(function (map, item) {
|
||||
if (!map[item]) {
|
||||
map[item] = 0;
|
||||
}
|
||||
map[item]++;
|
||||
return map;
|
||||
}, {});
|
||||
|
||||
return Object.keys(aspectObj).filter(function (k) {
|
||||
return aspectObj[k] === 3;
|
||||
});
|
||||
}
|
||||
|
||||
function setAspectRatio (capture, aspect) {
|
||||
// Max photo resolution with desired aspect ratio
|
||||
var videoDeviceController = capture.videoDeviceController;
|
||||
var photoResolution = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.photo)
|
||||
.filter(function (elem) {
|
||||
return ((elem.width / elem.height).toFixed(1) === aspect);
|
||||
})
|
||||
.reduce(function (prop1, prop2) {
|
||||
return (prop1.width * prop1.height) > (prop2.width * prop2.height) ? prop1 : prop2;
|
||||
});
|
||||
|
||||
// Max video resolution with desired aspect ratio
|
||||
var videoRecordResolution = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.videoRecord)
|
||||
.filter(function (elem) {
|
||||
return ((elem.width / elem.height).toFixed(1) === aspect);
|
||||
})
|
||||
.reduce(function (prop1, prop2) {
|
||||
return (prop1.width * prop1.height) > (prop2.width * prop2.height) ? prop1 : prop2;
|
||||
});
|
||||
|
||||
// Max video preview resolution with desired aspect ratio
|
||||
var videoPreviewResolution = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.videoPreview)
|
||||
.filter(function (elem) {
|
||||
return ((elem.width / elem.height).toFixed(1) === aspect);
|
||||
})
|
||||
.reduce(function (prop1, prop2) {
|
||||
return (prop1.width * prop1.height) > (prop2.width * prop2.height) ? prop1 : prop2;
|
||||
});
|
||||
|
||||
return videoDeviceController.setMediaStreamPropertiesAsync(CapMSType.photo, photoResolution)
|
||||
.then(function () {
|
||||
return videoDeviceController.setMediaStreamPropertiesAsync(CapMSType.videoPreview, videoPreviewResolution);
|
||||
})
|
||||
.then(function () {
|
||||
return videoDeviceController.setMediaStreamPropertiesAsync(CapMSType.videoRecord, videoRecordResolution);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* When Capture button is clicked, try to capture a picture and return
|
||||
*/
|
||||
function onCameraCaptureButtonClick () {
|
||||
// Make sure user can't click more than once
|
||||
if (this.getAttribute('clicked') === '1') {
|
||||
return false;
|
||||
} else {
|
||||
this.setAttribute('clicked', '1');
|
||||
}
|
||||
captureAction();
|
||||
}
|
||||
|
||||
/**
|
||||
* When Cancel button is clicked, destroy camera preview and return with error callback
|
||||
*/
|
||||
function onCameraCancelButtonClick () {
|
||||
// Make sure user can't click more than once
|
||||
if (this.getAttribute('clicked') === '1') {
|
||||
return false;
|
||||
} else {
|
||||
this.setAttribute('clicked', '1');
|
||||
}
|
||||
destroyCameraPreview();
|
||||
errorCallback('no image selected');
|
||||
}
|
||||
|
||||
/**
|
||||
* When the phone orientation change, get the event and change camera preview rotation
|
||||
* @param {Object} e - SimpleOrientationSensorOrientationChangedEventArgs
|
||||
*/
|
||||
function onOrientationChange (e) {
|
||||
setPreviewRotation(e.orientation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts SimpleOrientation to a VideoRotation to remove difference between camera sensor orientation
|
||||
* and video orientation
|
||||
* @param {number} orientation - Windows.Devices.Sensors.SimpleOrientation
|
||||
* @return {number} - Windows.Media.Capture.VideoRotation
|
||||
*/
|
||||
function orientationToRotation (orientation) {
|
||||
// VideoRotation enumerable and BitmapRotation enumerable have the same values
|
||||
// https://msdn.microsoft.com/en-us/library/windows/apps/windows.media.capture.videorotation.aspx
|
||||
// https://msdn.microsoft.com/en-us/library/windows/apps/windows.graphics.imaging.bitmaprotation.aspx
|
||||
|
||||
switch (orientation) {
|
||||
// portrait
|
||||
case Windows.Devices.Sensors.SimpleOrientation.notRotated:
|
||||
return Windows.Media.Capture.VideoRotation.clockwise90Degrees;
|
||||
// landscape
|
||||
case Windows.Devices.Sensors.SimpleOrientation.rotated90DegreesCounterclockwise:
|
||||
return Windows.Media.Capture.VideoRotation.none;
|
||||
// portrait-flipped (not supported by WinPhone Apps)
|
||||
case Windows.Devices.Sensors.SimpleOrientation.rotated180DegreesCounterclockwise:
|
||||
// Falling back to portrait default
|
||||
return Windows.Media.Capture.VideoRotation.clockwise90Degrees;
|
||||
// landscape-flipped
|
||||
case Windows.Devices.Sensors.SimpleOrientation.rotated270DegreesCounterclockwise:
|
||||
return Windows.Media.Capture.VideoRotation.clockwise180Degrees;
|
||||
// faceup & facedown
|
||||
default:
|
||||
// Falling back to portrait default
|
||||
return Windows.Media.Capture.VideoRotation.clockwise90Degrees;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the current MediaCapture's video
|
||||
* @param {number} orientation - Windows.Devices.Sensors.SimpleOrientation
|
||||
*/
|
||||
function setPreviewRotation (orientation) {
|
||||
capture.setPreviewRotation(orientationToRotation(orientation));
|
||||
}
|
||||
|
||||
try {
|
||||
createCameraUI();
|
||||
startCameraPreview();
|
||||
} catch (ex) {
|
||||
errorCallback(ex);
|
||||
}
|
||||
}
|
||||
|
||||
function takePictureFromCameraWindows (successCallback, errorCallback, args) {
|
||||
var destinationType = args[1];
|
||||
var targetWidth = args[3];
|
||||
var targetHeight = args[4];
|
||||
var encodingType = args[5];
|
||||
var allowCrop = !!args[7];
|
||||
var saveToPhotoAlbum = args[9];
|
||||
var WMCapture = Windows.Media.Capture;
|
||||
var cameraCaptureUI = new WMCapture.CameraCaptureUI();
|
||||
|
||||
cameraCaptureUI.photoSettings.allowCropping = allowCrop;
|
||||
|
||||
if (encodingType === Camera.EncodingType.PNG) {
|
||||
cameraCaptureUI.photoSettings.format = WMCapture.CameraCaptureUIPhotoFormat.png;
|
||||
} else {
|
||||
cameraCaptureUI.photoSettings.format = WMCapture.CameraCaptureUIPhotoFormat.jpeg;
|
||||
}
|
||||
|
||||
// decide which max pixels should be supported by targetWidth or targetHeight.
|
||||
var maxRes = null;
|
||||
var UIMaxRes = WMCapture.CameraCaptureUIMaxPhotoResolution;
|
||||
var totalPixels = targetWidth * targetHeight;
|
||||
|
||||
if (targetWidth === -1 && targetHeight === -1) {
|
||||
maxRes = UIMaxRes.highestAvailable;
|
||||
// Temp fix for CB-10539
|
||||
/* else if (totalPixels <= 320 * 240) {
|
||||
maxRes = UIMaxRes.verySmallQvga;
|
||||
} */
|
||||
} else if (totalPixels <= 640 * 480) {
|
||||
maxRes = UIMaxRes.smallVga;
|
||||
} else if (totalPixels <= 1024 * 768) {
|
||||
maxRes = UIMaxRes.mediumXga;
|
||||
} else if (totalPixels <= 3 * 1000 * 1000) {
|
||||
maxRes = UIMaxRes.large3M;
|
||||
} else if (totalPixels <= 5 * 1000 * 1000) {
|
||||
maxRes = UIMaxRes.veryLarge5M;
|
||||
} else {
|
||||
maxRes = UIMaxRes.highestAvailable;
|
||||
}
|
||||
|
||||
cameraCaptureUI.photoSettings.maxResolution = maxRes;
|
||||
|
||||
var cameraPicture;
|
||||
|
||||
// define focus handler for windows phone 10.0
|
||||
var savePhotoOnFocus = function () {
|
||||
window.removeEventListener('focus', savePhotoOnFocus);
|
||||
// call only when the app is in focus again
|
||||
savePhoto(cameraPicture, {
|
||||
destinationType: destinationType,
|
||||
targetHeight: targetHeight,
|
||||
targetWidth: targetWidth,
|
||||
encodingType: encodingType,
|
||||
saveToPhotoAlbum: saveToPhotoAlbum
|
||||
}, successCallback, errorCallback);
|
||||
};
|
||||
|
||||
// if windows phone 10, add and delete focus eventHandler to capture the focus back from cameraUI to app
|
||||
if (navigator.appVersion.indexOf('Windows Phone 10.0') >= 0) {
|
||||
window.addEventListener('focus', savePhotoOnFocus);
|
||||
}
|
||||
|
||||
cameraCaptureUI.captureFileAsync(WMCapture.CameraCaptureUIMode.photo).done(function (picture) {
|
||||
if (!picture) {
|
||||
errorCallback("User didn't capture a photo.");
|
||||
// Remove the focus handler if present
|
||||
window.removeEventListener('focus', savePhotoOnFocus);
|
||||
return;
|
||||
}
|
||||
cameraPicture = picture;
|
||||
|
||||
// If not windows 10, call savePhoto() now. If windows 10, wait for the app to be in focus again
|
||||
if (navigator.appVersion.indexOf('Windows Phone 10.0') < 0) {
|
||||
savePhoto(cameraPicture, {
|
||||
destinationType: destinationType,
|
||||
targetHeight: targetHeight,
|
||||
targetWidth: targetWidth,
|
||||
encodingType: encodingType,
|
||||
saveToPhotoAlbum: saveToPhotoAlbum
|
||||
}, successCallback, errorCallback);
|
||||
}
|
||||
}, function () {
|
||||
errorCallback('Fail to capture a photo.');
|
||||
window.removeEventListener('focus', savePhotoOnFocus);
|
||||
});
|
||||
}
|
||||
|
||||
function savePhoto (picture, options, successCallback, errorCallback) {
|
||||
// success callback for capture operation
|
||||
var success = function (picture) {
|
||||
if (options.destinationType === Camera.DestinationType.FILE_URI) {
|
||||
if (options.targetHeight > 0 && options.targetWidth > 0) {
|
||||
resizeImage(successCallback, errorCallback, picture, options.targetWidth, options.targetHeight, options.encodingType);
|
||||
} else {
|
||||
// CB-11714: check if target content-type is PNG to just rename as *.jpg since camera is captured as JPEG
|
||||
if (options.encodingType === Camera.EncodingType.PNG) {
|
||||
picture.name = picture.name.replace(/\.png$/, '.jpg');
|
||||
}
|
||||
|
||||
picture.copyAsync(getAppData().localFolder, picture.name, OptUnique).done(function (copiedFile) {
|
||||
successCallback('ms-appdata:///local/' + copiedFile.name);
|
||||
}, errorCallback);
|
||||
}
|
||||
} else {
|
||||
if (options.targetHeight > 0 && options.targetWidth > 0) {
|
||||
resizeImageBase64(successCallback, errorCallback, picture, options.targetWidth, options.targetHeight);
|
||||
} else {
|
||||
fileIO.readBufferAsync(picture).done(function (buffer) {
|
||||
var strBase64 = encodeToBase64String(buffer);
|
||||
picture.deleteAsync().done(function () {
|
||||
successCallback(strBase64);
|
||||
}, function (err) {
|
||||
errorCallback(err);
|
||||
});
|
||||
}, errorCallback);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!options.saveToPhotoAlbum) {
|
||||
success(picture);
|
||||
} else {
|
||||
var savePicker = new Windows.Storage.Pickers.FileSavePicker();
|
||||
var saveFile = function (file) {
|
||||
if (file) {
|
||||
// Prevent updates to the remote version of the file until we're done
|
||||
Windows.Storage.CachedFileManager.deferUpdates(file);
|
||||
picture.moveAndReplaceAsync(file)
|
||||
.then(function () {
|
||||
// Let Windows know that we're finished changing the file so
|
||||
// the other app can update the remote version of the file.
|
||||
return Windows.Storage.CachedFileManager.completeUpdatesAsync(file);
|
||||
})
|
||||
.done(function (updateStatus) {
|
||||
if (updateStatus === Windows.Storage.Provider.FileUpdateStatus.complete) {
|
||||
success(picture);
|
||||
} else {
|
||||
errorCallback('File update status is not complete.');
|
||||
}
|
||||
}, errorCallback);
|
||||
} else {
|
||||
errorCallback('Failed to select a file.');
|
||||
}
|
||||
};
|
||||
savePicker.suggestedStartLocation = pickerLocId.picturesLibrary;
|
||||
|
||||
if (options.encodingType === Camera.EncodingType.PNG) {
|
||||
savePicker.fileTypeChoices.insert('PNG', ['.png']);
|
||||
savePicker.suggestedFileName = 'photo.png';
|
||||
} else {
|
||||
savePicker.fileTypeChoices.insert('JPEG', ['.jpg']);
|
||||
savePicker.suggestedFileName = 'photo.jpg';
|
||||
}
|
||||
|
||||
// If Windows Phone 8.1 use pickSaveFileAndContinue()
|
||||
if (navigator.appVersion.indexOf('Windows Phone 8.1') >= 0) {
|
||||
/*
|
||||
Need to add and remove an event listener to catch activation state
|
||||
Using FileSavePicker will suspend the app and it's required to catch the pickSaveFileContinuation
|
||||
https://msdn.microsoft.com/en-us/library/windows/apps/xaml/dn631755.aspx
|
||||
*/
|
||||
var fileSaveHandler = function (eventArgs) {
|
||||
if (eventArgs.kind === Windows.ApplicationModel.Activation.ActivationKind.pickSaveFileContinuation) {
|
||||
var file = eventArgs.file;
|
||||
saveFile(file);
|
||||
webUIApp.removeEventListener('activated', fileSaveHandler);
|
||||
}
|
||||
};
|
||||
webUIApp.addEventListener('activated', fileSaveHandler);
|
||||
savePicker.pickSaveFileAndContinue();
|
||||
} else {
|
||||
savePicker.pickSaveFileAsync()
|
||||
.done(saveFile, errorCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
require('cordova/exec/proxy').add('Camera', module.exports);
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cordova-plugin-camera-tests",
|
||||
"version": "5.0.2",
|
||||
"version": "7.0.0",
|
||||
"description": "",
|
||||
"cordova": {
|
||||
"id": "cordova-plugin-camera-tests",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:rim="http://www.blackberry.com/ns/widgets"
|
||||
id="cordova-plugin-camera-tests"
|
||||
version="5.0.2">
|
||||
version="7.0.0">
|
||||
<name>Cordova Camera Plugin Tests</name>
|
||||
<license>Apache 2.0</license>
|
||||
|
||||
|
||||
120
tests/tests.js
120
tests/tests.js
@@ -19,7 +19,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/* globals Camera, resolveLocalFileSystemURL, FileEntry, CameraPopoverOptions, LocalFileSystem, MSApp */
|
||||
/* globals Camera, resolveLocalFileSystemURL, FileEntry, CameraPopoverOptions, LocalFileSystem */
|
||||
/* eslint-env jasmine */
|
||||
|
||||
exports.defineAutoTests = function () {
|
||||
@@ -78,20 +78,20 @@ exports.defineAutoTests = function () {
|
||||
/******************************************************************************/
|
||||
|
||||
exports.defineManualTests = function (contentEl, createActionButton) {
|
||||
var pictureUrl = null;
|
||||
var fileObj = null;
|
||||
var fileEntry = null;
|
||||
var pageStartTime = +new Date();
|
||||
let pictureUrl = null;
|
||||
let fileObj = null;
|
||||
let fileEntry = null;
|
||||
const pageStartTime = +new Date();
|
||||
|
||||
// default camera options
|
||||
var camQualityDefault = ['50', 50];
|
||||
var camDestinationTypeDefault = ['FILE_URI', 1];
|
||||
var camPictureSourceTypeDefault = ['CAMERA', 1];
|
||||
var camAllowEditDefault = ['allowEdit', false];
|
||||
var camEncodingTypeDefault = ['JPEG', 0];
|
||||
var camMediaTypeDefault = ['mediaType', 0];
|
||||
var camCorrectOrientationDefault = ['correctOrientation', false];
|
||||
var camSaveToPhotoAlbumDefault = ['saveToPhotoAlbum', true];
|
||||
const camQualityDefault = ['50', 50];
|
||||
const camDestinationTypeDefault = ['FILE_URI', 1];
|
||||
const camPictureSourceTypeDefault = ['CAMERA', 1];
|
||||
const camAllowEditDefault = ['allowEdit', false];
|
||||
const camEncodingTypeDefault = ['JPEG', 0];
|
||||
const camMediaTypeDefault = ['mediaType', 0];
|
||||
const camCorrectOrientationDefault = ['correctOrientation', false];
|
||||
const camSaveToPhotoAlbumDefault = ['saveToPhotoAlbum', true];
|
||||
|
||||
function log (value) {
|
||||
console.log(value);
|
||||
@@ -101,7 +101,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
||||
function clearStatus () {
|
||||
document.getElementById('camera_status').innerHTML = '';
|
||||
document.getElementById('camera_image').src = 'about:blank';
|
||||
var canvas = document.getElementById('canvas');
|
||||
const canvas = document.getElementById('canvas');
|
||||
canvas.width = canvas.height = 1;
|
||||
pictureUrl = null;
|
||||
fileObj = null;
|
||||
@@ -119,8 +119,8 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
||||
log('URL: "' + url.slice(0, 90) + '"');
|
||||
|
||||
pictureUrl = url;
|
||||
var img = document.getElementById('camera_image');
|
||||
var startTime = new Date();
|
||||
const img = document.getElementById('camera_image');
|
||||
const startTime = new Date();
|
||||
img.src = url;
|
||||
img.onload = function () {
|
||||
log('Img size: ' + img.naturalWidth + 'x' + img.naturalHeight);
|
||||
@@ -147,20 +147,20 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
||||
} else if (pictureUrl.indexOf('data:image/jpeg;base64') === 0) {
|
||||
// do nothing
|
||||
} else {
|
||||
var path = pictureUrl.replace(/^file:\/\/(localhost)?/, '').replace(/%20/g, ' ');
|
||||
const path = pictureUrl.replace(/^file:\/\/(localhost)?/, '').replace(/%20/g, ' ');
|
||||
fileEntry = new FileEntry('image_name.png', path);
|
||||
}
|
||||
}
|
||||
|
||||
function getPicture () {
|
||||
clearStatus();
|
||||
var options = extractOptions();
|
||||
const options = extractOptions();
|
||||
log('Getting picture with options: ' + JSON.stringify(options));
|
||||
var popoverHandle = navigator.camera.getPicture(getPictureWin, onGetPictureError, options);
|
||||
const popoverHandle = navigator.camera.getPicture(getPictureWin, onGetPictureError, options);
|
||||
|
||||
// Reposition the popover if the orientation changes.
|
||||
window.onorientationchange = function () {
|
||||
var newPopoverOptions = new CameraPopoverOptions(0, 0, 100, 100, 0, 300, 400);
|
||||
const newPopoverOptions = new CameraPopoverOptions(0, 0, 100, 100, 0, 300, 400);
|
||||
popoverHandle.setPosition(newPopoverOptions);
|
||||
};
|
||||
}
|
||||
@@ -177,7 +177,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
||||
*/
|
||||
function readFile () {
|
||||
function onFileReadAsDataURL (evt) {
|
||||
var img = document.getElementById('camera_image');
|
||||
const img = document.getElementById('camera_image');
|
||||
img.style.visibility = 'visible';
|
||||
img.style.display = 'block';
|
||||
img.src = evt.target.result;
|
||||
@@ -188,7 +188,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
||||
log('Got file: ' + JSON.stringify(file));
|
||||
fileObj = file;
|
||||
/* eslint-disable no-undef */
|
||||
var reader = new FileReader();
|
||||
const reader = new FileReader();
|
||||
/* eslint-enable no-undef */
|
||||
reader.onload = function () {
|
||||
log('FileReader.readAsDataURL() - length = ' + reader.result.length);
|
||||
@@ -219,9 +219,9 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
||||
* This calls FileEntry.copyTo and FileEntry.moveTo.
|
||||
*/
|
||||
function copyImage () {
|
||||
var onFileSystemReceived = function (fileSystem) {
|
||||
var destDirEntry = fileSystem.root;
|
||||
var origName = fileEntry.name;
|
||||
const onFileSystemReceived = function (fileSystem) {
|
||||
const destDirEntry = fileSystem.root;
|
||||
const origName = fileEntry.name;
|
||||
|
||||
// Test FileEntry API here.
|
||||
fileEntry.copyTo(destDirEntry, 'copied_file.png', logCallback('FileEntry.copyTo', true), logCallback('FileEntry.copyTo', false));
|
||||
@@ -253,13 +253,13 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
||||
* This calls FileEntry.createWriter, FileWriter.write, and FileWriter.truncate.
|
||||
*/
|
||||
function writeImage () {
|
||||
var onFileWriterReceived = function (fileWriter) {
|
||||
const onFileWriterReceived = function (fileWriter) {
|
||||
fileWriter.onwrite = logCallback('FileWriter.write', true);
|
||||
fileWriter.onerror = logCallback('FileWriter.write', false);
|
||||
fileWriter.write('some text!');
|
||||
};
|
||||
|
||||
var onFileTruncateWriterReceived = function (fileWriter) {
|
||||
const onFileTruncateWriterReceived = function (fileWriter) {
|
||||
fileWriter.onwrite = logCallback('FileWriter.truncate', true);
|
||||
fileWriter.onerror = logCallback('FileWriter.truncate', false);
|
||||
fileWriter.truncate(10);
|
||||
@@ -270,15 +270,15 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
||||
}
|
||||
|
||||
function displayImageUsingCanvas () {
|
||||
var canvas = document.getElementById('canvas');
|
||||
var img = document.getElementById('camera_image');
|
||||
var w = img.width;
|
||||
var h = img.height;
|
||||
const canvas = document.getElementById('canvas');
|
||||
const img = document.getElementById('camera_image');
|
||||
let w = img.width;
|
||||
let h = img.height;
|
||||
h = 100 / w * h;
|
||||
w = 100;
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
var context = canvas.getContext('2d');
|
||||
const context = canvas.getContext('2d');
|
||||
context.drawImage(img, 0, 0, w, h);
|
||||
}
|
||||
|
||||
@@ -311,9 +311,9 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
||||
return;
|
||||
}
|
||||
/* eslint-enable no-undef */
|
||||
var URLApi = window.URL || window.webkitURL;
|
||||
const URLApi = window.URL || window.webkitURL;
|
||||
if (URLApi) {
|
||||
var blobURL = URLApi.createObjectURL(fileObj);
|
||||
const blobURL = URLApi.createObjectURL(fileObj);
|
||||
if (blobURL) {
|
||||
setPicture(blobURL, function () {
|
||||
URLApi.revokeObjectURL(blobURL);
|
||||
@@ -327,11 +327,11 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
||||
}
|
||||
|
||||
function extractOptions () {
|
||||
var els = document.querySelectorAll('#image-options select');
|
||||
var ret = {};
|
||||
const els = document.querySelectorAll('#image-options select');
|
||||
const ret = {};
|
||||
/* eslint-disable no-cond-assign */
|
||||
for (var i = 0, el; el = els[i]; ++i) {
|
||||
var value = el.value;
|
||||
for (let i = 0, el; el = els[i]; ++i) {
|
||||
let value = el.value;
|
||||
if (value === '') continue;
|
||||
value = +value;
|
||||
|
||||
@@ -346,20 +346,20 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
||||
}
|
||||
|
||||
function createOptionsEl (name, values, selectionDefault) {
|
||||
var openDiv = '<div style="display: inline-block">' + name + ': ';
|
||||
var select = '<select name=' + name + ' id="' + name + '">';
|
||||
const openDiv = '<div style="display: inline-block">' + name + ': ';
|
||||
const select = '<select name=' + name + ' id="' + name + '">';
|
||||
|
||||
var defaultOption = '';
|
||||
let defaultOption = '';
|
||||
if (selectionDefault === undefined) {
|
||||
defaultOption = '<option value="">default</option>';
|
||||
}
|
||||
|
||||
var options = '';
|
||||
let options = '';
|
||||
if (typeof values === 'boolean') {
|
||||
values = { true: 1, false: 0 };
|
||||
}
|
||||
for (var k in values) {
|
||||
var isSelected = '';
|
||||
for (const k in values) {
|
||||
let isSelected = '';
|
||||
if (selectionDefault) {
|
||||
if (selectionDefault[0] === k) {
|
||||
isSelected = 'selected';
|
||||
@@ -368,20 +368,20 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
||||
options += '<option value="' + values[k] + '" ' + isSelected + '>' + k + '</option>';
|
||||
}
|
||||
|
||||
var closeDiv = '</select></div>';
|
||||
const closeDiv = '</select></div>';
|
||||
|
||||
return openDiv + select + defaultOption + options + closeDiv;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var info_div = '<h1>Camera</h1>' +
|
||||
const info_div = '<h1>Camera</h1>' +
|
||||
'<div id="info">' +
|
||||
'<b>Status:</b> <div id="camera_status"></div>' +
|
||||
'img: <img width="100" id="camera_image">' +
|
||||
'canvas: <canvas id="canvas" width="1" height="1"></canvas>' +
|
||||
'</div>';
|
||||
var options_div = '<h2>Cordova Camera API Options</h2>' +
|
||||
const options_div = '<h2>Cordova Camera API Options</h2>' +
|
||||
'<div id="image-options">' +
|
||||
createOptionsEl('sourceType', Camera.PictureSourceType, camPictureSourceTypeDefault) +
|
||||
createOptionsEl('destinationType', Camera.DestinationType, camDestinationTypeDefault) +
|
||||
@@ -395,8 +395,8 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
||||
createOptionsEl('saveToPhotoAlbum', true, camSaveToPhotoAlbumDefault) +
|
||||
createOptionsEl('cameraDirection', Camera.Direction) +
|
||||
'</div>';
|
||||
var getpicture_div = '<div id="getpicture"></div>';
|
||||
var test_procedure = '<h4>Recommended Test Procedure</h4>' +
|
||||
const getpicture_div = '<div id="getpicture"></div>';
|
||||
const test_procedure = '<h4>Recommended Test Procedure</h4>' +
|
||||
'Options not specified should be the default value' +
|
||||
'<br>Status box should update with image and info whenever an image is taken or selected from library' +
|
||||
'</p><div style="background:#B0C4DE;border:1px solid #FFA07A;margin:15px 6px 0px;min-width:295px;max-width:97%;padding:4px 0px 2px 10px;min-height:160px;max-height:200px;overflow:auto">' +
|
||||
@@ -410,13 +410,13 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
||||
'</p><li>sourceType=CAMERA<br>targetWidth & targetHeight=50<br>allowEdit=false<br>Do Get File Metadata test below and take note of size<br>Repeat test but with width and height=800. Size should be significantly larger.</li>' +
|
||||
'</p><li>quality=0<br>targetWidth & targetHeight=default<br>allowEdit=false<br>Do Get File Metadata test below and take note of size<br>Repeat test but with quality=80. Size should be significantly larger.</li>' +
|
||||
'</ol></div>';
|
||||
var inputs_div = '<h2>Native File Inputs</h2>' +
|
||||
const inputs_div = '<h2>Native File Inputs</h2>' +
|
||||
'For the following tests, status box should update with file selected' +
|
||||
'</p><div>input type=file <input type="file" class="testInputTag"></div>' +
|
||||
'<div>capture=camera <input type="file" accept="image/*;capture=camera" class="testInputTag"></div>' +
|
||||
'<div>capture=camcorder <input type="file" accept="video/*;capture=camcorder" class="testInputTag"></div>' +
|
||||
'<div>capture=microphone <input type="file" accept="audio/*;capture=microphone" class="testInputTag"></div>';
|
||||
var actions_div = '<h2>Actions</h2>' +
|
||||
const actions_div = '<h2>Actions</h2>' +
|
||||
'For the following tests, ensure that an image is set in status box' +
|
||||
'</p><div id="metadata"></div>' +
|
||||
'Expected result: Get metadata about file selected.<br>Status box will show, along with the metadata, "Call to FileEntry.getMetadata success, Call to FileEntry.setMetadata success, Call to FileEntry.getParent success"' +
|
||||
@@ -433,22 +433,14 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
||||
'</p><div id="remove"></div>' +
|
||||
'Expected result: Remove image from library.<br>Status box will show "FileEntry.remove success:["OK"]';
|
||||
|
||||
// We need to wrap this code due to Windows security restrictions
|
||||
// see http://msdn.microsoft.com/en-us/library/windows/apps/hh465380.aspx#differences for details
|
||||
if (window.MSApp && window.MSApp.execUnsafeLocalFunction) {
|
||||
MSApp.execUnsafeLocalFunction(function () {
|
||||
contentEl.innerHTML = info_div + options_div + getpicture_div + test_procedure + inputs_div + actions_div;
|
||||
});
|
||||
} else {
|
||||
contentEl.innerHTML = info_div + options_div + getpicture_div + test_procedure + inputs_div + actions_div;
|
||||
}
|
||||
contentEl.innerHTML = info_div + options_div + getpicture_div + test_procedure + inputs_div + actions_div;
|
||||
|
||||
var elements = document.getElementsByClassName('testInputTag');
|
||||
var listener = function (e) {
|
||||
const elements = document.getElementsByClassName('testInputTag');
|
||||
const listener = function (e) {
|
||||
testInputTag(e.target);
|
||||
};
|
||||
for (var i = 0; i < elements.length; ++i) {
|
||||
var item = elements[i];
|
||||
for (let i = 0; i < elements.length; ++i) {
|
||||
const item = elements[i];
|
||||
item.addEventListener('change', listener, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
var argscheck = require('cordova/argscheck');
|
||||
var exec = require('cordova/exec');
|
||||
var Camera = require('./Camera');
|
||||
const argscheck = require('cordova/argscheck');
|
||||
const exec = require('cordova/exec');
|
||||
const Camera = require('./Camera');
|
||||
// XXX: commented out
|
||||
// CameraPopoverHandle = require('./CameraPopoverHandle');
|
||||
|
||||
@@ -32,10 +32,10 @@ var Camera = require('./Camera');
|
||||
/**
|
||||
* @exports camera
|
||||
*/
|
||||
var cameraExport = {};
|
||||
const cameraExport = {};
|
||||
|
||||
// Tack on the Camera Constants to the base camera plugin.
|
||||
for (var key in Camera) {
|
||||
for (const key in Camera) {
|
||||
cameraExport[key] = Camera[key];
|
||||
}
|
||||
|
||||
@@ -114,14 +114,8 @@ for (var key in Camera) {
|
||||
* __Supported Platforms__
|
||||
*
|
||||
* - Android
|
||||
* - BlackBerry
|
||||
* - Browser
|
||||
* - Firefox
|
||||
* - FireOS
|
||||
* - iOS
|
||||
* - Windows
|
||||
* - WP8
|
||||
* - Ubuntu
|
||||
*
|
||||
* More examples [here](#camera-getPicture-examples). Quirks [here](#camera-getPicture-quirks).
|
||||
*
|
||||
@@ -134,22 +128,22 @@ for (var key in Camera) {
|
||||
cameraExport.getPicture = function (successCallback, errorCallback, options) {
|
||||
argscheck.checkArgs('fFO', 'Camera.getPicture', arguments);
|
||||
options = options || {};
|
||||
var getValue = argscheck.getValue;
|
||||
const getValue = argscheck.getValue;
|
||||
|
||||
var quality = getValue(options.quality, 50);
|
||||
var destinationType = getValue(options.destinationType, Camera.DestinationType.FILE_URI);
|
||||
var sourceType = getValue(options.sourceType, Camera.PictureSourceType.CAMERA);
|
||||
var targetWidth = getValue(options.targetWidth, -1);
|
||||
var targetHeight = getValue(options.targetHeight, -1);
|
||||
var encodingType = getValue(options.encodingType, Camera.EncodingType.JPEG);
|
||||
var mediaType = getValue(options.mediaType, Camera.MediaType.PICTURE);
|
||||
var allowEdit = !!options.allowEdit;
|
||||
var correctOrientation = !!options.correctOrientation;
|
||||
var saveToPhotoAlbum = !!options.saveToPhotoAlbum;
|
||||
var popoverOptions = getValue(options.popoverOptions, null);
|
||||
var cameraDirection = getValue(options.cameraDirection, Camera.Direction.BACK);
|
||||
const quality = getValue(options.quality, 50);
|
||||
const destinationType = getValue(options.destinationType, Camera.DestinationType.FILE_URI);
|
||||
const sourceType = getValue(options.sourceType, Camera.PictureSourceType.CAMERA);
|
||||
const targetWidth = getValue(options.targetWidth, -1);
|
||||
const targetHeight = getValue(options.targetHeight, -1);
|
||||
const encodingType = getValue(options.encodingType, Camera.EncodingType.JPEG);
|
||||
const mediaType = getValue(options.mediaType, Camera.MediaType.PICTURE);
|
||||
const allowEdit = !!options.allowEdit;
|
||||
const correctOrientation = !!options.correctOrientation;
|
||||
const saveToPhotoAlbum = !!options.saveToPhotoAlbum;
|
||||
const popoverOptions = getValue(options.popoverOptions, null);
|
||||
const cameraDirection = getValue(options.cameraDirection, Camera.Direction.BACK);
|
||||
|
||||
var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType,
|
||||
const args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType,
|
||||
mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection];
|
||||
|
||||
exec(successCallback, errorCallback, 'Camera', 'takePicture', args);
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
* @ignore in favour of iOS' one
|
||||
* A handle to an image picker popover.
|
||||
*/
|
||||
var CameraPopoverHandle = function () {
|
||||
const CameraPopoverHandle = function () {
|
||||
this.setPosition = function (popoverOptions) {
|
||||
console.log('CameraPopoverHandle.setPosition is only supported on iOS.');
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
var Camera = require('./Camera');
|
||||
const Camera = require('./Camera');
|
||||
|
||||
/**
|
||||
* @namespace navigator
|
||||
@@ -42,7 +42,7 @@ var Camera = require('./Camera');
|
||||
* @param {Number} [popoverWidth=0] - width of the popover (0 or not specified will use apple's default width).
|
||||
* @param {Number} [popoverHeight=0] - height of the popover (0 or not specified will use apple's default height).
|
||||
*/
|
||||
var CameraPopoverOptions = function (x, y, width, height, arrowDir, popoverWidth, popoverHeight) {
|
||||
const CameraPopoverOptions = function (x, y, width, height, arrowDir, popoverWidth, popoverHeight) {
|
||||
// information of rectangle that popover should be anchored to
|
||||
this.x = x || 0;
|
||||
this.y = y || 32;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
var exec = require('cordova/exec');
|
||||
const exec = require('cordova/exec');
|
||||
|
||||
/**
|
||||
* @namespace navigator
|
||||
@@ -48,7 +48,7 @@ var exec = require('cordova/exec');
|
||||
* }
|
||||
* @module CameraPopoverHandle
|
||||
*/
|
||||
var CameraPopoverHandle = function () {
|
||||
const CameraPopoverHandle = function () {
|
||||
/**
|
||||
* Can be used to reposition the image selection dialog,
|
||||
* for example, when the device orientation changes.
|
||||
@@ -58,7 +58,7 @@ var CameraPopoverHandle = function () {
|
||||
* @param {module:CameraPopoverOptions} popoverOptions
|
||||
*/
|
||||
this.setPosition = function (popoverOptions) {
|
||||
var args = [popoverOptions];
|
||||
const args = [popoverOptions];
|
||||
exec(null, null, 'Camera', 'repositionPopover', args);
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user