Compare commits
34
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19feee9cb0 | ||
|
|
69c687e0cf | ||
|
|
b0ee9dd905 | ||
|
|
c2e0db2b86 | ||
|
|
8f07f562a2 | ||
|
|
1e8c836844 | ||
|
|
b131021303 | ||
|
|
0dabe94416 | ||
|
|
fb8ce21711 | ||
|
|
b67e4a09ed | ||
|
|
d10bf50c61 | ||
|
|
b7af490a3e | ||
|
|
c2e4d1f36d | ||
|
|
d0fe66e1d5 | ||
|
|
af36e74d05 | ||
|
|
629dbcb712 | ||
|
|
bc7c4278ef | ||
|
|
302aacb214 | ||
|
|
3927735d09 | ||
|
|
9e11ab4dfb | ||
|
|
d1dfc1e8ee | ||
|
|
ee4ac0d7b2 | ||
|
|
d52b936bd8 | ||
|
|
9fe5b430aa | ||
|
|
23dbb8889a | ||
|
|
ac4af88f55 | ||
|
|
f1ae0e9ccf | ||
|
|
563f0038fd | ||
|
|
ccbbdccc4c | ||
|
|
04ed502d92 | ||
|
|
7209d38fa2 | ||
|
|
29c9ea387d | ||
|
|
5ef04e552c | ||
|
|
b8a700215a |
+2
-2
@@ -6,8 +6,8 @@ node_js:
|
||||
install:
|
||||
- echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
|
||||
- cd ..
|
||||
- npm install -g purplecabbage/cordova-paramedic
|
||||
- npm install -g cordova-paramedic
|
||||
- npm install -g cordova
|
||||
- npm install -g ios-sim
|
||||
script:
|
||||
- cordova-paramedic --platform ios --plugin ../cordova-plugin-camera
|
||||
- cordova-paramedic --platform ios --plugin ${TRAVIS_BUILD_DIR}
|
||||
|
||||
@@ -256,7 +256,8 @@ Optional parameters to customize the camera settings.
|
||||
|
||||
- Any `cameraDirection` value results in a back-facing photo.
|
||||
|
||||
- Ignores the `allowEdit` parameter.
|
||||
- Android also uses the Crop Activity for allowEdit, even though crop should work and actually pass the cropped image back to Cordova, the only one that works consistently is the one bundled
|
||||
with the Google Plus Photos application. Other crops may not work.
|
||||
|
||||
- `Camera.PictureSourceType.PHOTOLIBRARY` and `Camera.PictureSourceType.SAVEDPHOTOALBUM` both display the same photo album.
|
||||
|
||||
|
||||
@@ -175,3 +175,34 @@
|
||||
* CB-8559 Integrate TravisCI
|
||||
* CB-8438 cordova-plugin-camera documentation translation: cordova-plugin-camera
|
||||
* CB-8538 Added package.json file
|
||||
|
||||
### 1.0.0 (Apr 15, 2015)
|
||||
* CB-8780 - Display popover using main thread. Fixes popover slowness (closes #81)
|
||||
* CB-8746 bumped version of file dependency
|
||||
* CB-8746 gave plugin major version bump
|
||||
* CB-8707 refactoring windows code to improve readability
|
||||
* CB-8706 use filePicker if saveToPhotoAlbum is true
|
||||
* CB-8706 remove unnecessary capabilities from xml
|
||||
* CB-8747 updated dependency, added peer dependency
|
||||
* CB-8683 updated blackberry specific references of org.apache.cordova.camera to cordova-plugin-camera
|
||||
* CB-8782: Updated the docs to talk about the allowEdit quirks, it's not 100% working, but better than it was
|
||||
* CB-8782: Fixed the flow so that we save the cropped image and use it, not the original non-cropped. Crop only supports G+ Photos Crop, other crops may not work, depending on the OEM
|
||||
* CB-8740: Removing FileHelper call that was failing on Samsung Galaxy S3, now that we have a real path, we only need to update the MediaStore, not pull from it in this case
|
||||
* CB-8740: Partial fix for Save Image to Gallery error found in MobileSpec
|
||||
* CB-8683 changed plugin-id to pacakge-name
|
||||
* CB-8653 properly updated translated docs to use new id
|
||||
* CB-8653 updated translated docs to use new id
|
||||
* CB-8351 Fix custom implementation of integerValueForKey (close #79)
|
||||
* Fix cordova-paramedic path change, build with TRAVIS_BUILD_DIR, use npm to install paramedic
|
||||
* docs: added 'Windows' to supported platforms
|
||||
* CB-8653 Updated Readme
|
||||
* CB-8659: ios: 4.0.x Compatibility: Remove use of deprecated headers
|
||||
|
||||
### 1.1.0 (May 06, 2015)
|
||||
* CB-8943 fix `PickAndContinue` issue on *Win10Phone*
|
||||
* CB-8253 Fix potential unreleased resources
|
||||
* CB-8909: Remove unused import from File
|
||||
* CB-8404 typo fix `cameraproxy.js`
|
||||
* CB-8404 Rotate camera feed with device orientation
|
||||
* CB-8054 Support taking pictures from file for *WP8*
|
||||
* CB-8405 Use `z-index` instead of `z-order`
|
||||
|
||||
+3
-3
@@ -17,7 +17,7 @@
|
||||
under the License.
|
||||
-->
|
||||
|
||||
# org.apache.cordova.camera
|
||||
# cordova-plugin-camera
|
||||
|
||||
Dieses Plugin definiert eine globale `navigator.camera`-Objekt, das eine API für Aufnahmen und für die Auswahl der Bilder aus dem System-Image-Library bietet.
|
||||
|
||||
@@ -31,7 +31,7 @@ Obwohl das Objekt mit der globalen Gültigkeitsbereich `navigator` verbunden ist
|
||||
|
||||
## Installation
|
||||
|
||||
cordova plugin add org.apache.cordova.camera
|
||||
cordova plugin add cordova-plugin-camera
|
||||
|
||||
|
||||
## navigator.camera.getPicture
|
||||
@@ -431,4 +431,4 @@ Fortgeschrittene Image-Dateien, die in vorübergehender Verwahrung gehalten werd
|
||||
|
||||
function onFail(message) {
|
||||
alert('Failed because: ' + message);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -17,7 +17,7 @@
|
||||
under the License.
|
||||
-->
|
||||
|
||||
# org.apache.cordova.camera
|
||||
# cordova-plugin-camera
|
||||
|
||||
Este plugin define un global `navigator.camera` objeto que proporciona una API para tomar fotografías y por elegir imágenes de biblioteca de imágenes del sistema.
|
||||
|
||||
@@ -29,7 +29,7 @@ Aunque el objeto está unido al ámbito global `navigator` , no estará disponib
|
||||
|
||||
## Instalación
|
||||
|
||||
Cordova plugin agregar org.apache.cordova.camera
|
||||
Cordova plugin agregar cordova-plugin-camera
|
||||
|
||||
|
||||
## navigator.camera.getPicture
|
||||
@@ -388,4 +388,4 @@ Elimina intermedio archivos de imagen que se mantienen en depósito temporal des
|
||||
|
||||
function onSuccess() {console.log ("cámara limpieza éxito.")}
|
||||
|
||||
function onFail(message) {alert (' falló porque: ' + mensaje);}
|
||||
function onFail(message) {alert (' falló porque: ' + mensaje);}
|
||||
|
||||
+3
-3
@@ -17,7 +17,7 @@
|
||||
under the License.
|
||||
-->
|
||||
|
||||
# org.apache.cordova.camera
|
||||
# cordova-plugin-camera
|
||||
|
||||
Ce plugin définit un global `navigator.camera` objet qui fournit une API pour la prise de photos et de choisir des images de la bibliothèque d'images du système.
|
||||
|
||||
@@ -29,7 +29,7 @@ Bien que l'objet est attaché à la portée globale `navigator` , il n'est pas d
|
||||
|
||||
## Installation
|
||||
|
||||
Cordova plugin ajouter org.apache.cordova.camera
|
||||
Cordova plugin ajouter cordova-plugin-camera
|
||||
|
||||
|
||||
## navigator.camera.getPicture
|
||||
@@ -388,4 +388,4 @@ Supprime les intermédiaires les fichiers image qui sont gardées en dépôt tem
|
||||
|
||||
fonction onSuccess() {console.log ("succès de caméra nettoyage.")}
|
||||
|
||||
function onFail(message) {alert (' a échoué car: "+ message);}
|
||||
function onFail(message) {alert (' a échoué car: "+ message);}
|
||||
|
||||
+3
-3
@@ -17,7 +17,7 @@
|
||||
under the License.
|
||||
-->
|
||||
|
||||
# org.apache.cordova.camera
|
||||
# cordova-plugin-camera
|
||||
|
||||
Questo plugin definisce un oggetto globale `navigator.camera`, che fornisce un'API per scattare foto e per aver scelto immagini dalla libreria di immagini del sistema.
|
||||
|
||||
@@ -31,7 +31,7 @@ Anche se l'oggetto è associato con ambito globale del `navigator`, non è dispo
|
||||
|
||||
## Installazione
|
||||
|
||||
cordova plugin add org.apache.cordova.camera
|
||||
cordova plugin add cordova-plugin-camera
|
||||
|
||||
|
||||
## navigator.camera.getPicture
|
||||
@@ -431,4 +431,4 @@ Rimuove i file di immagine intermedia che vengono tenuti in custodia temporanea
|
||||
|
||||
function onFail(message) {
|
||||
alert('Failed because: ' + message);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -17,7 +17,7 @@
|
||||
under the License.
|
||||
-->
|
||||
|
||||
# org.apache.cordova.camera
|
||||
# cordova-plugin-camera
|
||||
|
||||
このプラグインは、写真を撮るため、システムのイメージ ライブラリからイメージを選択するために API を提供します、グローバル `navigator.camera` オブジェクトを定義します。
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
## インストール
|
||||
|
||||
cordova plugin add org.apache.cordova.camera
|
||||
cordova plugin add cordova-plugin-camera
|
||||
|
||||
|
||||
## navigator.camera.getPicture
|
||||
@@ -431,4 +431,4 @@ iOS だけ指定パラメーターをポップ オーバーのアンカー要素
|
||||
|
||||
function onFail(message) {
|
||||
alert('Failed because: ' + message);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -17,7 +17,7 @@
|
||||
under the License.
|
||||
-->
|
||||
|
||||
# org.apache.cordova.camera
|
||||
# cordova-plugin-camera
|
||||
|
||||
이 플러그인 시스템의 이미지 라이브러리에서 이미지를 선택 및 사진 촬영을 위한 API를 제공 하는 글로벌 `navigator.camera` 개체를 정의 합니다.
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
## 설치
|
||||
|
||||
cordova plugin add org.apache.cordova.camera
|
||||
cordova plugin add cordova-plugin-camera
|
||||
|
||||
|
||||
## navigator.camera.getPicture
|
||||
@@ -431,4 +431,4 @@ iOS 전용 매개 변수 iPad의 보관 함 또는 앨범에서 이미지를 선
|
||||
|
||||
function onFail(message) {
|
||||
alert('Failed because: ' + message);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -17,7 +17,7 @@
|
||||
under the License.
|
||||
-->
|
||||
|
||||
# org.apache.cordova.camera
|
||||
# cordova-plugin-camera
|
||||
|
||||
Ten plugin definiuje obiekt globalny `navigator.camera`, który dostarcza API do robienia zdjęć i wybór zdjęć z biblioteki obrazów systemu.
|
||||
|
||||
@@ -31,7 +31,7 @@ Mimo, że obiekt jest dołączony do globalnego zakresu `navigator`, to nie dost
|
||||
|
||||
## Instalacja
|
||||
|
||||
cordova plugin add org.apache.cordova.camera
|
||||
cordova plugin add cordova-plugin-camera
|
||||
|
||||
|
||||
## navigator.camera.getPicture
|
||||
@@ -431,4 +431,4 @@ Usuwa pliki obrazów pośrednich, które są przechowywane w pamięci tymczasowe
|
||||
|
||||
function onFail(message) {
|
||||
alert('Failed because: ' + message);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -17,11 +17,11 @@
|
||||
under the License.
|
||||
-->
|
||||
|
||||
# org.apache.cordova.camera
|
||||
# cordova-plugin-camera
|
||||
|
||||
Этот плагин предоставляет API для съемки и для выбора изображения из библиотеки изображений системы.
|
||||
|
||||
cordova plugin add org.apache.cordova.camera
|
||||
cordova plugin add cordova-plugin-camera
|
||||
|
||||
|
||||
## navigator.camera.getPicture
|
||||
@@ -414,4 +414,4 @@ Tizen поддерживает только значение `destinationType`
|
||||
|
||||
function onFail(message) {
|
||||
alert('Failed because: ' + message);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -17,7 +17,7 @@
|
||||
under the License.
|
||||
-->
|
||||
|
||||
# org.apache.cordova.camera
|
||||
# cordova-plugin-camera
|
||||
|
||||
這個外掛程式定義了一個全球 `navigator.camera` 物件,它提供了 API,拍照,從系統的圖像庫中選擇圖像。
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
## 安裝
|
||||
|
||||
cordova plugin add org.apache.cordova.camera
|
||||
cordova plugin add cordova-plugin-camera
|
||||
|
||||
|
||||
## navigator.camera.getPicture
|
||||
@@ -432,4 +432,4 @@ iOS 僅指定氣泡框的錨元素的位置和箭頭方向,從 iPad 庫或專
|
||||
|
||||
function onFail(message) {
|
||||
alert('Failed because: ' + message);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "cordova-plugin-camera",
|
||||
"version": "0.3.7-dev",
|
||||
"version": "1.1.0",
|
||||
"description": "Cordova Camera Plugin",
|
||||
"cordova": {
|
||||
"id": "org.apache.cordova.camera",
|
||||
"id": "cordova-plugin-camera",
|
||||
"platforms": [
|
||||
"firefoxos",
|
||||
"android",
|
||||
@@ -38,6 +38,9 @@
|
||||
"cordova-browser",
|
||||
"cordova-windows"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"cordova-plugin-file": ">=2.0.0"
|
||||
},
|
||||
"author": "Apache Software Foundation",
|
||||
"license": "Apache 2.0"
|
||||
}
|
||||
|
||||
+3
-12
@@ -21,8 +21,8 @@
|
||||
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:rim="http://www.blackberry.com/ns/widgets"
|
||||
id="org.apache.cordova.camera"
|
||||
version="0.3.7-dev">
|
||||
id="cordova-plugin-camera"
|
||||
version="1.1.0">
|
||||
<name>Camera</name>
|
||||
<description>Cordova Camera Plugin</description>
|
||||
<license>Apache 2.0</license>
|
||||
@@ -241,16 +241,7 @@
|
||||
|
||||
<!-- windows -->
|
||||
<platform name="windows">
|
||||
<config-file target="package.windows.appxmanifest" parent="/Package/Capabilities">
|
||||
<Capability Name="picturesLibrary" />
|
||||
<DeviceCapability Name="webcam" />
|
||||
</config-file>
|
||||
<config-file target="package.windows80.appxmanifest" parent="/Package/Capabilities">
|
||||
<Capability Name="picturesLibrary" />
|
||||
<DeviceCapability Name="webcam" />
|
||||
</config-file>
|
||||
<config-file target="package.phone.appxmanifest" parent="/Package/Capabilities">
|
||||
<Capability Name="picturesLibrary" />
|
||||
<config-file target="package.appxmanifest" parent="/Package/Capabilities">
|
||||
<DeviceCapability Name="webcam" />
|
||||
</config-file>
|
||||
<js-module src="www/CameraPopoverHandle.js" name="CameraPopoverHandle">
|
||||
|
||||
+186
-67
@@ -24,7 +24,11 @@ import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
@@ -50,7 +54,7 @@ import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import android.content.pm.PackageManager;
|
||||
/**
|
||||
* This class launches the camera view, allows the user to take a picture, closes the camera view,
|
||||
* and returns the captured image. When the camera view is closed, the screen displayed before
|
||||
@@ -77,6 +81,8 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
private static final String GET_All = "Get All";
|
||||
|
||||
private static final String LOG_TAG = "CameraLauncher";
|
||||
|
||||
//Where did this come from?
|
||||
private static final int CROP_CAMERA = 100;
|
||||
|
||||
private int mQuality; // Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
|
||||
@@ -203,8 +209,8 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
// Save the number of images currently on disk for later
|
||||
this.numPics = queryImgDB(whichContentStore()).getCount();
|
||||
|
||||
// Display camera
|
||||
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
|
||||
// Let's use the intent and see what happens
|
||||
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
|
||||
// Specify file so that large image is captured and returned
|
||||
File photo = createCaptureFile(encodingType);
|
||||
@@ -212,7 +218,17 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
this.imageUri = Uri.fromFile(photo);
|
||||
|
||||
if (this.cordova != null) {
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this, intent, (CAMERA + 1) * 16 + returnType + 1);
|
||||
// Let's check to make sure the camera is actually installed. (Legacy Nexus 7 code)
|
||||
PackageManager mPm = this.cordova.getActivity().getPackageManager();
|
||||
if(intent.resolveActivity(mPm) != null)
|
||||
{
|
||||
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this, intent, (CAMERA + 1) * 16 + returnType + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.d(LOG_TAG, "Error: You don't have a default camera. Your device may not be CTS complaint.");
|
||||
}
|
||||
}
|
||||
// else
|
||||
// LOG.d(LOG_TAG, "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity");
|
||||
@@ -236,6 +252,8 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
return photo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get image from photo library.
|
||||
*
|
||||
@@ -296,13 +314,14 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
*
|
||||
* @param picUri
|
||||
*/
|
||||
private void performCrop(Uri picUri) {
|
||||
private void performCrop(Uri picUri, int destType, Intent cameraIntent) {
|
||||
try {
|
||||
Intent cropIntent = new Intent("com.android.camera.action.CROP");
|
||||
// indicate image type and Uri
|
||||
cropIntent.setDataAndType(picUri, "image/*");
|
||||
// set crop properties
|
||||
cropIntent.putExtra("crop", "true");
|
||||
|
||||
// indicate output X and Y
|
||||
if (targetWidth > 0) {
|
||||
cropIntent.putExtra("outputX", targetWidth);
|
||||
@@ -322,12 +341,18 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
|
||||
if (this.cordova != null) {
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this,
|
||||
cropIntent, CROP_CAMERA);
|
||||
cropIntent, CROP_CAMERA + destType);
|
||||
}
|
||||
} catch (ActivityNotFoundException anfe) {
|
||||
Log.e(LOG_TAG, "Crop operation not supported on this device");
|
||||
// Send Uri back to JavaScript for viewing image
|
||||
this.callbackContext.success(picUri.toString());
|
||||
try {
|
||||
processResultFromCamera(destType, cameraIntent);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
Log.e(LOG_TAG, "Unable to write to file");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,16 +367,22 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
|
||||
// Create an ExifHelper to save the exif data that is lost during compression
|
||||
ExifHelper exif = new ExifHelper();
|
||||
String sourcePath;
|
||||
try {
|
||||
if (this.encodingType == JPEG) {
|
||||
exif.createInFile(getTempDirectoryPath() + "/.Pic.jpg");
|
||||
exif.readExifData();
|
||||
rotate = exif.getOrientation();
|
||||
} else if (this.encodingType == PNG) {
|
||||
exif.createInFile(getTempDirectoryPath() + "/.Pic.png");
|
||||
exif.readExifData();
|
||||
rotate = exif.getOrientation();
|
||||
if(allowEdit && croppedUri != null)
|
||||
{
|
||||
sourcePath = FileHelper.stripFileProtocol(croppedUri.toString());
|
||||
}
|
||||
else
|
||||
{
|
||||
sourcePath = getTempDirectoryPath() + "/.Pic.jpg";
|
||||
}
|
||||
|
||||
//We don't support PNG, so let's not pretend we do
|
||||
exif.createInFile(getTempDirectoryPath() + "/.Pic.jpg");
|
||||
exif.readExifData();
|
||||
rotate = exif.getOrientation();
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -361,7 +392,13 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
|
||||
// If sending base64 image back
|
||||
if (destType == DATA_URL) {
|
||||
bitmap = getScaledBitmap(FileHelper.stripFileProtocol(imageUri.toString()));
|
||||
if(croppedUri != null) {
|
||||
bitmap = getScaledBitmap(FileHelper.stripFileProtocol(croppedUri.toString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
bitmap = getScaledBitmap(FileHelper.stripFileProtocol(imageUri.toString()));
|
||||
}
|
||||
if (bitmap == null) {
|
||||
// Try to get the bitmap from intent.
|
||||
bitmap = (Bitmap)intent.getExtras().get("data");
|
||||
@@ -384,14 +421,11 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
|
||||
// If sending filename back
|
||||
else if (destType == FILE_URI || destType == NATIVE_URI) {
|
||||
uri = Uri.fromFile(new File(getTempDirectoryPath(), System.currentTimeMillis() + ".jpg"));
|
||||
|
||||
if (this.saveToPhotoAlbum) {
|
||||
Uri inputUri = getUriFromMediaStore();
|
||||
try {
|
||||
//Just because we have a media URI doesn't mean we have a real file, we need to make it
|
||||
uri = Uri.fromFile(new File(FileHelper.getRealPath(inputUri, this.cordova)));
|
||||
} catch (NullPointerException e) {
|
||||
uri = null;
|
||||
}
|
||||
//Create a URI on the filesystem so that we can write the file.
|
||||
uri = Uri.fromFile(new File(getPicutresPath()));
|
||||
} else {
|
||||
uri = Uri.fromFile(new File(getTempDirectoryPath(), System.currentTimeMillis() + ".jpg"));
|
||||
}
|
||||
@@ -422,20 +456,20 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
// Restore exif data to file
|
||||
if (this.encodingType == JPEG) {
|
||||
String exifPath;
|
||||
if (this.saveToPhotoAlbum) {
|
||||
exifPath = FileHelper.getRealPath(uri, this.cordova);
|
||||
} else {
|
||||
exifPath = uri.getPath();
|
||||
}
|
||||
exifPath = uri.getPath();
|
||||
exif.createOutFile(exifPath);
|
||||
exif.writeExifData();
|
||||
}
|
||||
if (this.allowEdit) {
|
||||
performCrop(uri);
|
||||
} else {
|
||||
// Send Uri back to JavaScript for viewing image
|
||||
this.callbackContext.success(uri.toString());
|
||||
|
||||
//Broadcast change to File System on MediaStore
|
||||
if(this.saveToPhotoAlbum) {
|
||||
refreshGallery(uri);
|
||||
}
|
||||
|
||||
|
||||
// Send Uri back to JavaScript for viewing image
|
||||
this.callbackContext.success(uri.toString());
|
||||
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
@@ -445,6 +479,24 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
bitmap = null;
|
||||
}
|
||||
|
||||
private String getPicutresPath()
|
||||
{
|
||||
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
|
||||
String imageFileName = "IMG_" + timeStamp + ".jpg";
|
||||
File storageDir = Environment.getExternalStoragePublicDirectory(
|
||||
Environment.DIRECTORY_PICTURES);
|
||||
String galleryPath = storageDir.getAbsolutePath() + "/" + imageFileName;
|
||||
return galleryPath;
|
||||
}
|
||||
|
||||
private void refreshGallery(Uri contentUri)
|
||||
{
|
||||
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
||||
mediaScanIntent.setData(contentUri);
|
||||
this.cordova.getActivity().sendBroadcast(mediaScanIntent);
|
||||
}
|
||||
|
||||
|
||||
private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
// Create an ExifHelper to save the exif data that is lost during compression
|
||||
String modifiedPath = getTempDirectoryPath() + "/modified.jpg";
|
||||
@@ -580,34 +632,47 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
*/
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
|
||||
// Get src and dest types from request code
|
||||
// Get src and dest types from request code for a Camera Activity
|
||||
int srcType = (requestCode / 16) - 1;
|
||||
int destType = (requestCode % 16) - 1;
|
||||
// if camera crop
|
||||
if (requestCode == CROP_CAMERA) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
// // Send Uri back to JavaScript for viewing image
|
||||
this.callbackContext
|
||||
.success(croppedUri.toString());
|
||||
croppedUri = null;
|
||||
|
||||
}// If cancelled
|
||||
else if (resultCode == Activity.RESULT_CANCELED) {
|
||||
this.failPicture("Camera cancelled.");
|
||||
}
|
||||
|
||||
// If something else
|
||||
else {
|
||||
this.failPicture("Did not complete!");
|
||||
}
|
||||
// If Camera Crop
|
||||
if (requestCode >= CROP_CAMERA) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
|
||||
}
|
||||
// Because of the inability to pass through multiple intents, this hack will allow us
|
||||
// to pass arcane codes back.
|
||||
destType = requestCode - CROP_CAMERA;
|
||||
try {
|
||||
processResultFromCamera(destType, intent);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(LOG_TAG, "Unable to write to file");
|
||||
}
|
||||
|
||||
}// If cancelled
|
||||
else if (resultCode == Activity.RESULT_CANCELED) {
|
||||
this.failPicture("Camera cancelled.");
|
||||
}
|
||||
|
||||
// If something else
|
||||
else {
|
||||
this.failPicture("Did not complete!");
|
||||
}
|
||||
}
|
||||
// If CAMERA
|
||||
if (srcType == CAMERA) {
|
||||
else if (srcType == CAMERA) {
|
||||
// If image available
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
try {
|
||||
this.processResultFromCamera(destType, intent);
|
||||
if(this.allowEdit)
|
||||
{
|
||||
Uri tmpFile = Uri.fromFile(new File(getTempDirectoryPath(), ".Pic.jpg"));
|
||||
performCrop(tmpFile, destType, intent);
|
||||
}
|
||||
else {
|
||||
this.processResultFromCamera(destType, intent);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
this.failPicture("Error capturing image.");
|
||||
@@ -624,7 +689,6 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
this.failPicture("Did not complete!");
|
||||
}
|
||||
}
|
||||
|
||||
// If retrieving photo from library
|
||||
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
|
||||
if (resultCode == Activity.RESULT_OK && intent != null) {
|
||||
@@ -698,16 +762,33 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
*/
|
||||
private void writeUncompressedImage(Uri uri) throws FileNotFoundException,
|
||||
IOException {
|
||||
FileInputStream fis = new FileInputStream(FileHelper.stripFileProtocol(imageUri.toString()));
|
||||
OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
|
||||
byte[] buffer = new byte[4096];
|
||||
int len;
|
||||
while ((len = fis.read(buffer)) != -1) {
|
||||
os.write(buffer, 0, len);
|
||||
FileInputStream fis = null;
|
||||
OutputStream os = null;
|
||||
try {
|
||||
fis = new FileInputStream(FileHelper.stripFileProtocol(imageUri.toString()));
|
||||
os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
|
||||
byte[] buffer = new byte[4096];
|
||||
int len;
|
||||
while ((len = fis.read(buffer)) != -1) {
|
||||
os.write(buffer, 0, len);
|
||||
}
|
||||
os.flush();
|
||||
} finally {
|
||||
if (os != null) {
|
||||
try {
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
LOG.d(LOG_TAG,"Exception while closing output stream.");
|
||||
}
|
||||
}
|
||||
if (fis != null) {
|
||||
try {
|
||||
fis.close();
|
||||
} catch (IOException e) {
|
||||
LOG.d(LOG_TAG,"Exception while closing file input stream.");
|
||||
}
|
||||
}
|
||||
}
|
||||
os.flush();
|
||||
os.close();
|
||||
fis.close();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -743,13 +824,39 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
private Bitmap getScaledBitmap(String imageUrl) throws IOException {
|
||||
// If no new width or height were specified return the original bitmap
|
||||
if (this.targetWidth <= 0 && this.targetHeight <= 0) {
|
||||
return BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova));
|
||||
InputStream fileStream = null;
|
||||
Bitmap image = null;
|
||||
try {
|
||||
fileStream = FileHelper.getInputStreamFromUriString(imageUrl, cordova);
|
||||
image = BitmapFactory.decodeStream(fileStream);
|
||||
} finally {
|
||||
if (fileStream != null) {
|
||||
try {
|
||||
fileStream.close();
|
||||
} catch (IOException e) {
|
||||
LOG.d(LOG_TAG,"Exception while closing file input stream.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
// figure out the original width and height of the image
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova), null, options);
|
||||
InputStream fileStream = null;
|
||||
try {
|
||||
fileStream = FileHelper.getInputStreamFromUriString(imageUrl, cordova);
|
||||
BitmapFactory.decodeStream(fileStream, null, options);
|
||||
} finally {
|
||||
if (fileStream != null) {
|
||||
try {
|
||||
fileStream.close();
|
||||
} catch (IOException e) {
|
||||
LOG.d(LOG_TAG,"Exception while closing file input stream.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//CB-2292: WTF? Why is the width null?
|
||||
if(options.outWidth == 0 || options.outHeight == 0)
|
||||
@@ -763,7 +870,19 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
// Load in the smallest bitmap possible that is closest to the size we want
|
||||
options.inJustDecodeBounds = false;
|
||||
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, this.targetWidth, this.targetHeight);
|
||||
Bitmap unscaledBitmap = BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova), null, options);
|
||||
Bitmap unscaledBitmap = null;
|
||||
try {
|
||||
fileStream = FileHelper.getInputStreamFromUriString(imageUrl, cordova);
|
||||
unscaledBitmap = BitmapFactory.decodeStream(fileStream, null, options);
|
||||
} finally {
|
||||
if (fileStream != null) {
|
||||
try {
|
||||
fileStream.close();
|
||||
} catch (IOException e) {
|
||||
LOG.d(LOG_TAG,"Exception while closing file input stream.");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (unscaledBitmap == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ function showCameraDialog (done, cancel, fail) {
|
||||
});
|
||||
wv.on('JavaScriptCallback', function (evt, data) {
|
||||
var args = JSON.parse(data).args;
|
||||
if (args[0] === 'org.apache.cordova.camera') {
|
||||
if (args[0] === 'cordova-plugin-camera') {
|
||||
if (args[1] === 'cancel') {
|
||||
cancel('User canceled');
|
||||
} else if (args[1] === 'error') {
|
||||
|
||||
+19
-19
@@ -130,8 +130,8 @@ static NSString* toBase64(NSData* data) {
|
||||
[self.commandDelegate runInBackground:^{
|
||||
|
||||
CDVPictureOptions* pictureOptions = [CDVPictureOptions createFromTakePictureArguments:command];
|
||||
pictureOptions.popoverSupported = [self popoverSupported];
|
||||
pictureOptions.usesGeolocation = [self usesGeolocation];
|
||||
pictureOptions.popoverSupported = [weakSelf popoverSupported];
|
||||
pictureOptions.usesGeolocation = [weakSelf usesGeolocation];
|
||||
pictureOptions.cropToSize = NO;
|
||||
|
||||
BOOL hasCamera = [UIImagePickerController isSourceTypeAvailable:pictureOptions.sourceType];
|
||||
@@ -142,13 +142,6 @@ static NSString* toBase64(NSData* data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If a popover is already open, close it; we only want one at a time.
|
||||
if (([[weakSelf pickerController] pickerPopoverController] != nil) && [[[weakSelf pickerController] pickerPopoverController] isPopoverVisible]) {
|
||||
[[[weakSelf pickerController] pickerPopoverController] dismissPopoverAnimated:YES];
|
||||
[[[weakSelf pickerController] pickerPopoverController] setDelegate:nil];
|
||||
[[weakSelf pickerController] setPickerPopoverController:nil];
|
||||
}
|
||||
|
||||
CDVCameraPicker* cameraPicker = [CDVCameraPicker createFromPictureOptions:pictureOptions];
|
||||
weakSelf.pickerController = cameraPicker;
|
||||
|
||||
@@ -157,20 +150,27 @@ static NSString* toBase64(NSData* data) {
|
||||
// we need to capture this state for memory warnings that dealloc this object
|
||||
cameraPicker.webView = weakSelf.webView;
|
||||
|
||||
if ([weakSelf popoverSupported] && (pictureOptions.sourceType != UIImagePickerControllerSourceTypeCamera)) {
|
||||
if (cameraPicker.pickerPopoverController == nil) {
|
||||
cameraPicker.pickerPopoverController = [[NSClassFromString(@"UIPopoverController") alloc] initWithContentViewController:cameraPicker];
|
||||
// Perform UI operations on the main thread
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// If a popover is already open, close it; we only want one at a time.
|
||||
if (([[weakSelf pickerController] pickerPopoverController] != nil) && [[[weakSelf pickerController] pickerPopoverController] isPopoverVisible]) {
|
||||
[[[weakSelf pickerController] pickerPopoverController] dismissPopoverAnimated:YES];
|
||||
[[[weakSelf pickerController] pickerPopoverController] setDelegate:nil];
|
||||
[[weakSelf pickerController] setPickerPopoverController:nil];
|
||||
}
|
||||
[weakSelf displayPopover:pictureOptions.popoverOptions];
|
||||
weakSelf.hasPendingOperation = NO;
|
||||
|
||||
} else {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if ([weakSelf popoverSupported] && (pictureOptions.sourceType != UIImagePickerControllerSourceTypeCamera)) {
|
||||
if (cameraPicker.pickerPopoverController == nil) {
|
||||
cameraPicker.pickerPopoverController = [[NSClassFromString(@"UIPopoverController") alloc] initWithContentViewController:cameraPicker];
|
||||
}
|
||||
[weakSelf displayPopover:pictureOptions.popoverOptions];
|
||||
weakSelf.hasPendingOperation = NO;
|
||||
} else {
|
||||
[weakSelf.viewController presentViewController:cameraPicker animated:YES completion:^{
|
||||
weakSelf.hasPendingOperation = NO;
|
||||
}];
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ static NSString* toBase64(NSData* data) {
|
||||
{
|
||||
NSInteger value = defaultValue;
|
||||
|
||||
NSNumber* val = [self valueForKey:key]; // value is an NSNumber
|
||||
NSNumber* val = [dict valueForKey:key]; // value is an NSNumber
|
||||
|
||||
if (val != nil) {
|
||||
value = [val integerValue];
|
||||
|
||||
+534
-390
@@ -19,11 +19,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/*global Windows:true, URL:true */
|
||||
/*jshint unused:true, undef:true, browser:true */
|
||||
/*global Windows:true, URL:true, module:true, require:true */
|
||||
|
||||
|
||||
var cordova = require('cordova'),
|
||||
Camera = require('./Camera');
|
||||
var Camera = require('./Camera');
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -43,399 +43,543 @@ module.exports = {
|
||||
// 11 cameraDirection:0
|
||||
|
||||
takePicture: function (successCallback, errorCallback, args) {
|
||||
var encodingType = args[5];
|
||||
var targetWidth = args[3];
|
||||
var targetHeight = args[4];
|
||||
var sourceType = args[2];
|
||||
var destinationType = args[1];
|
||||
var mediaType = args[6];
|
||||
var allowCrop = !!args[7];
|
||||
var saveToPhotoAlbum = args[9];
|
||||
var cameraDirection = args[11];
|
||||
|
||||
// resize method :)
|
||||
var resizeImage = function (file) {
|
||||
var tempPhotoFileName = "";
|
||||
if (encodingType == Camera.EncodingType.PNG) {
|
||||
tempPhotoFileName = "camera_cordova_temp_return.png";
|
||||
} else {
|
||||
tempPhotoFileName = "camera_cordova_temp_return.jpg";
|
||||
}
|
||||
|
||||
var storageFolder = Windows.Storage.ApplicationData.current.localFolder;
|
||||
file.copyAsync(storageFolder, file.name, Windows.Storage.NameCollisionOption.replaceExisting).then(function (storageFile) {
|
||||
Windows.Storage.FileIO.readBufferAsync(storageFile).then(function(buffer) {
|
||||
var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer);
|
||||
var imageData = "data:" + file.contentType + ";base64," + strBase64;
|
||||
var image = new Image();
|
||||
image.src = imageData;
|
||||
image.onload = function() {
|
||||
var imageWidth = targetWidth,
|
||||
imageHeight = targetHeight;
|
||||
var canvas = document.createElement('canvas');
|
||||
|
||||
canvas.width = imageWidth;
|
||||
canvas.height = imageHeight;
|
||||
|
||||
canvas.getContext("2d").drawImage(this, 0, 0, imageWidth, imageHeight);
|
||||
|
||||
var fileContent = canvas.toDataURL(file.contentType).split(',')[1];
|
||||
|
||||
var storageFolder = Windows.Storage.ApplicationData.current.localFolder;
|
||||
|
||||
storageFolder.createFileAsync(tempPhotoFileName, Windows.Storage.CreationCollisionOption.generateUniqueName).done(function (storagefile) {
|
||||
var content = Windows.Security.Cryptography.CryptographicBuffer.decodeFromBase64String(fileContent);
|
||||
Windows.Storage.FileIO.writeBufferAsync(storagefile, content).then(function () {
|
||||
successCallback("ms-appdata:///local/" + storagefile.name);
|
||||
}, function () {
|
||||
errorCallback("Resize picture error.");
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
}, function () {
|
||||
errorCallback("Can't access localStorage folder");
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// because of asynchronous method, so let the successCallback be called in it.
|
||||
var resizeImageBase64 = function (file) {
|
||||
|
||||
Windows.Storage.FileIO.readBufferAsync(file).done( function(buffer) {
|
||||
var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer);
|
||||
var imageData = "data:" + file.contentType + ";base64," + strBase64;
|
||||
|
||||
var image = new Image();
|
||||
image.src = imageData;
|
||||
|
||||
image.onload = function() {
|
||||
var imageWidth = targetWidth,
|
||||
imageHeight = targetHeight;
|
||||
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);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
if (sourceType != Camera.PictureSourceType.CAMERA) {
|
||||
|
||||
// TODO: Add WP8.1 support
|
||||
// WP8.1 doesn't allow to use of pickSingleFileAsync method
|
||||
// see http://msdn.microsoft.com/en-us/library/windows/apps/br207852.aspx for details
|
||||
// replacement of pickSingleFileAsync - pickSingleFileAndContinue method
|
||||
// will take application to suspended state and this require additional logic to wake application up
|
||||
if (navigator.appVersion.indexOf('Windows Phone 8.1') >= 0 ) {
|
||||
errorCallback('Not supported');
|
||||
return;
|
||||
}
|
||||
|
||||
var fileOpenPicker = new Windows.Storage.Pickers.FileOpenPicker();
|
||||
fileOpenPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.picturesLibrary;
|
||||
if (mediaType == Camera.MediaType.PICTURE) {
|
||||
fileOpenPicker.fileTypeFilter.replaceAll([".png", ".jpg", ".jpeg"]);
|
||||
}
|
||||
else if (mediaType == Camera.MediaType.VIDEO) {
|
||||
fileOpenPicker.fileTypeFilter.replaceAll([".avi", ".flv", ".asx", ".asf", ".mov", ".mp4", ".mpg", ".rm", ".srt", ".swf", ".wmv", ".vob"]);
|
||||
}
|
||||
else {
|
||||
fileOpenPicker.fileTypeFilter.replaceAll(["*"]);
|
||||
}
|
||||
|
||||
fileOpenPicker.pickSingleFileAsync().done(function (file) {
|
||||
if (file) {
|
||||
if (destinationType == Camera.DestinationType.FILE_URI || destinationType == Camera.DestinationType.NATIVE_URI) {
|
||||
if (targetHeight > 0 && targetWidth > 0) {
|
||||
resizeImage(file);
|
||||
}
|
||||
else {
|
||||
|
||||
var storageFolder = Windows.Storage.ApplicationData.current.localFolder;
|
||||
file.copyAsync(storageFolder, file.name, Windows.Storage.NameCollisionOption.replaceExisting).then(function (storageFile) {
|
||||
successCallback(URL.createObjectURL(storageFile));
|
||||
}, function () {
|
||||
errorCallback("Can't access localStorage folder.");
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (targetHeight > 0 && targetWidth > 0) {
|
||||
resizeImageBase64(file);
|
||||
} else {
|
||||
Windows.Storage.FileIO.readBufferAsync(file).done(function (buffer) {
|
||||
var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer);
|
||||
successCallback(strBase64);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
errorCallback("User didn't choose a file.");
|
||||
}
|
||||
}, function () {
|
||||
errorCallback("User didn't choose a file.");
|
||||
});
|
||||
|
||||
takePictureFromFile(successCallback, errorCallback, args);
|
||||
} else {
|
||||
|
||||
var CaptureNS = Windows.Media.Capture;
|
||||
|
||||
// Check if necessary API available
|
||||
if (!CaptureNS.CameraCaptureUI) {
|
||||
// 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 capturePreview = null,
|
||||
captureCancelButton = null,
|
||||
capture = null,
|
||||
captureSettings = null;
|
||||
|
||||
var createCameraUI = function () {
|
||||
|
||||
// Create fullscreen preview
|
||||
capturePreview = document.createElement("video");
|
||||
|
||||
// z-order style element for capturePreview and captureCancelButton elts
|
||||
// is necessary to avoid overriding by another page elements, -1 sometimes is not enough
|
||||
capturePreview.style.cssText = "position: fixed; left: 0; top: 0; width: 100%; height: 100%; z-order: 999";
|
||||
|
||||
// Create cancel button
|
||||
captureCancelButton = document.createElement("button");
|
||||
captureCancelButton.innerText = "Cancel";
|
||||
captureCancelButton.style.cssText = "position: fixed; right: 0; bottom: 0; display: block; margin: 20px; z-order: 1000";
|
||||
|
||||
capture = new CaptureNS.MediaCapture();
|
||||
|
||||
captureSettings = new CaptureNS.MediaCaptureInitializationSettings();
|
||||
captureSettings.streamingCaptureMode = CaptureNS.StreamingCaptureMode.video;
|
||||
};
|
||||
|
||||
var startCameraPreview = function () {
|
||||
|
||||
// Search for available camera devices
|
||||
// This is necessary to detect which camera (front or back) we should use
|
||||
var expectedPanel = cameraDirection === 1 ? Windows.Devices.Enumeration.Panel.front : Windows.Devices.Enumeration.Panel.back;
|
||||
Windows.Devices.Enumeration.DeviceInformation.findAllAsync(Windows.Devices.Enumeration.DeviceClass.videoCapture)
|
||||
.done(function (devices) {
|
||||
if (devices.length > 0) {
|
||||
devices.forEach(function(currDev) {
|
||||
if (currDev.enclosureLocation.panel && currDev.enclosureLocation.panel == expectedPanel) {
|
||||
captureSettings.videoDeviceId = currDev.id;
|
||||
}
|
||||
});
|
||||
|
||||
capture.initializeAsync(captureSettings).done(function () {
|
||||
// This is necessary since WP8.1 MediaCapture outputs video stream rotated 90 degrees CCW
|
||||
// TODO: This can be not consistent across devices, need additional testing on various devices
|
||||
capture.setPreviewRotation(Windows.Media.Capture.VideoRotation.clockwise90Degrees);
|
||||
// msdn.microsoft.com/en-us/library/windows/apps/hh452807.aspx
|
||||
capturePreview.msZoom = true;
|
||||
capturePreview.src = URL.createObjectURL(capture);
|
||||
capturePreview.play();
|
||||
|
||||
// Insert preview frame and controls into page
|
||||
document.body.appendChild(capturePreview);
|
||||
document.body.appendChild(captureCancelButton);
|
||||
|
||||
// Bind events to controls
|
||||
capturePreview.addEventListener('click', captureAction);
|
||||
captureCancelButton.addEventListener('click', function () {
|
||||
destroyCameraPreview();
|
||||
errorCallback('Cancelled');
|
||||
}, false);
|
||||
}, function (err) {
|
||||
destroyCameraPreview();
|
||||
errorCallback('Camera intitialization error ' + err);
|
||||
});
|
||||
} else {
|
||||
destroyCameraPreview();
|
||||
errorCallback('Camera not found');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var destroyCameraPreview = function () {
|
||||
capturePreview.pause();
|
||||
capturePreview.src = null;
|
||||
[capturePreview, captureCancelButton].forEach(function(elem) {
|
||||
if (elem /* && elem in document.body.childNodes */) {
|
||||
document.body.removeChild(elem);
|
||||
}
|
||||
});
|
||||
if (capture) {
|
||||
capture.stopRecordAsync();
|
||||
capture = null;
|
||||
}
|
||||
};
|
||||
|
||||
var captureAction = function () {
|
||||
|
||||
var encodingProperties,
|
||||
fileName,
|
||||
generateUniqueCollisionOption = Windows.Storage.CreationCollisionOption.generateUniqueName,
|
||||
tempFolder = Windows.Storage.ApplicationData.current.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, generateUniqueCollisionOption).done(function(capturedFile) {
|
||||
capture.capturePhotoToStorageFileAsync(encodingProperties, capturedFile).done(function() {
|
||||
|
||||
destroyCameraPreview();
|
||||
|
||||
// success callback for capture operation
|
||||
var success = function(capturedfile) {
|
||||
if (destinationType == Camera.DestinationType.FILE_URI || destinationType == Camera.DestinationType.NATIVE_URI) {
|
||||
if (targetHeight > 0 && targetWidth > 0) {
|
||||
resizeImage(capturedfile);
|
||||
} else {
|
||||
capturedfile.copyAsync(Windows.Storage.ApplicationData.current.localFolder, capturedfile.name, generateUniqueCollisionOption).done(function(copiedfile) {
|
||||
successCallback("ms-appdata:///local/" + copiedfile.name);
|
||||
}, errorCallback);
|
||||
}
|
||||
} else {
|
||||
if (targetHeight > 0 && targetWidth > 0) {
|
||||
resizeImageBase64(capturedfile);
|
||||
} else {
|
||||
Windows.Storage.FileIO.readBufferAsync(capturedfile).done(function(buffer) {
|
||||
var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer);
|
||||
capturedfile.deleteAsync().done(function() {
|
||||
successCallback(strBase64);
|
||||
}, function(err) {
|
||||
console.error(err);
|
||||
successCallback(strBase64);
|
||||
});
|
||||
}, errorCallback);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (saveToPhotoAlbum) {
|
||||
capturedFile.copyAsync(Windows.Storage.KnownFolders.picturesLibrary, capturedFile.name, generateUniqueCollisionOption)
|
||||
.done(function() {
|
||||
success(capturedFile);
|
||||
}, errorCallback);
|
||||
} else {
|
||||
success(capturedFile);
|
||||
}
|
||||
|
||||
|
||||
}, function(err) {
|
||||
destroyCameraPreview();
|
||||
errorCallback(err);
|
||||
});
|
||||
}, function(err) {
|
||||
destroyCameraPreview();
|
||||
errorCallback(err);
|
||||
});
|
||||
};
|
||||
|
||||
try {
|
||||
createCameraUI();
|
||||
startCameraPreview();
|
||||
} catch (ex) {
|
||||
errorCallback(ex);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
var cameraCaptureUI = new Windows.Media.Capture.CameraCaptureUI();
|
||||
cameraCaptureUI.photoSettings.allowCropping = allowCrop;
|
||||
|
||||
if (encodingType == Camera.EncodingType.PNG) {
|
||||
cameraCaptureUI.photoSettings.format = Windows.Media.Capture.CameraCaptureUIPhotoFormat.png;
|
||||
} else {
|
||||
cameraCaptureUI.photoSettings.format = Windows.Media.Capture.CameraCaptureUIPhotoFormat.jpeg;
|
||||
}
|
||||
|
||||
// decide which max pixels should be supported by targetWidth or targetHeight.
|
||||
if (targetWidth >= 1280 || targetHeight >= 960) {
|
||||
cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.large3M;
|
||||
} else if (targetWidth >= 1024 || targetHeight >= 768) {
|
||||
cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.mediumXga;
|
||||
} else if (targetWidth >= 800 || targetHeight >= 600) {
|
||||
cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.mediumXga;
|
||||
} else if (targetWidth >= 640 || targetHeight >= 480) {
|
||||
cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.smallVga;
|
||||
} else if (targetWidth >= 320 || targetHeight >= 240) {
|
||||
cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.verySmallQvga;
|
||||
} else {
|
||||
cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.highestAvailable;
|
||||
}
|
||||
|
||||
cameraCaptureUI.captureFileAsync(Windows.Media.Capture.CameraCaptureUIMode.photo).then(function(picture) {
|
||||
if (picture) {
|
||||
// save to photo album successCallback
|
||||
var success = function() {
|
||||
if (destinationType == Camera.DestinationType.FILE_URI) {
|
||||
if (targetHeight > 0 && targetWidth > 0) {
|
||||
resizeImage(picture);
|
||||
} else {
|
||||
|
||||
var storageFolder = Windows.Storage.ApplicationData.current.localFolder;
|
||||
picture.copyAsync(storageFolder, picture.name, Windows.Storage.NameCollisionOption.replaceExisting).then(function(storageFile) {
|
||||
successCallback("ms-appdata:///local/" + storageFile.name);
|
||||
}, function() {
|
||||
errorCallback("Can't access localStorage folder.");
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (targetHeight > 0 && targetWidth > 0) {
|
||||
resizeImageBase64(picture);
|
||||
} else {
|
||||
Windows.Storage.FileIO.readBufferAsync(picture).done(function(buffer) {
|
||||
var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer);
|
||||
successCallback(strBase64);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
// save to photo album errorCallback
|
||||
var fail = function() {
|
||||
//errorCallback("FileError, code:" + fileError.code);
|
||||
errorCallback("Save fail.");
|
||||
};
|
||||
|
||||
if (saveToPhotoAlbum) {
|
||||
Windows.Storage.StorageFile.getFileFromPathAsync(picture.path).then(function(storageFile) {
|
||||
storageFile.copyAsync(Windows.Storage.KnownFolders.picturesLibrary, picture.name, Windows.Storage.NameCollisionOption.generateUniqueName).then(function(storageFile) {
|
||||
success();
|
||||
}, function() {
|
||||
fail();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
success();
|
||||
}
|
||||
} else {
|
||||
errorCallback("User didn't capture a photo.");
|
||||
}
|
||||
}, function() {
|
||||
errorCallback("Fail to capture a photo.");
|
||||
});
|
||||
}
|
||||
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"];
|
||||
|
||||
// Resize method
|
||||
function resizeImage(successCallback, errorCallback, file, targetWidth, targetHeight, encodingType) {
|
||||
var tempPhotoFileName = "";
|
||||
if (encodingType == Camera.EncodingType.PNG) {
|
||||
tempPhotoFileName = "camera_cordova_temp_return.png";
|
||||
} else {
|
||||
tempPhotoFileName = "camera_cordova_temp_return.jpg";
|
||||
}
|
||||
|
||||
var storageFolder = Windows.Storage.ApplicationData.current.localFolder;
|
||||
file.copyAsync(storageFolder, file.name, Windows.Storage.NameCollisionOption.replaceExisting)
|
||||
.then(function (storageFile) { return Windows.Storage.FileIO.readBufferAsync(storageFile); })
|
||||
.then(function(buffer) {
|
||||
var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer);
|
||||
var imageData = "data:" + file.contentType + ";base64," + strBase64;
|
||||
var image = new Image();
|
||||
image.src = imageData;
|
||||
image.onload = function() {
|
||||
var imageWidth = targetWidth,
|
||||
imageHeight = targetHeight;
|
||||
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(file.contentType).split(',')[1];
|
||||
|
||||
var storageFolder = Windows.Storage.ApplicationData.current.localFolder;
|
||||
|
||||
storageFolder.createFileAsync(tempPhotoFileName, Windows.Storage.CreationCollisionOption.generateUniqueName)
|
||||
.then(function (storagefile) {
|
||||
var content = Windows.Security.Cryptography.CryptographicBuffer.decodeFromBase64String(fileContent);
|
||||
storageFileName = storagefile.name;
|
||||
return Windows.Storage.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) {
|
||||
Windows.Storage.FileIO.readBufferAsync(file).done( function(buffer) {
|
||||
var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer);
|
||||
var imageData = "data:" + file.contentType + ";base64," + strBase64;
|
||||
|
||||
var image = new Image();
|
||||
image.src = imageData;
|
||||
|
||||
image.onload = function() {
|
||||
var imageWidth = targetWidth,
|
||||
imageHeight = targetHeight;
|
||||
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],
|
||||
destinationType = args[1],
|
||||
targetWidth = args[3],
|
||||
targetHeight = args[4],
|
||||
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.");
|
||||
Windows.UI.WebUI.WebUIApplication.removeEventListener("activated", filePickerActivationHandler);
|
||||
return;
|
||||
}
|
||||
if (destinationType == Camera.DestinationType.FILE_URI || destinationType == Camera.DestinationType.NATIVE_URI) {
|
||||
if (targetHeight > 0 && targetWidth > 0) {
|
||||
resizeImage(successCallback, errorCallback, file, targetWidth, targetHeight, encodingType);
|
||||
}
|
||||
else {
|
||||
var storageFolder = Windows.Storage.ApplicationData.current.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 {
|
||||
Windows.Storage.FileIO.readBufferAsync(file).done(function (buffer) {
|
||||
var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer);
|
||||
successCallback(strBase64);
|
||||
}, errorCallback);
|
||||
}
|
||||
}
|
||||
Windows.UI.WebUI.WebUIApplication.removeEventListener("activated", filePickerActivationHandler);
|
||||
}
|
||||
};
|
||||
|
||||
var fileOpenPicker = new Windows.Storage.Pickers.FileOpenPicker();
|
||||
if (mediaType == Camera.MediaType.PICTURE) {
|
||||
fileOpenPicker.fileTypeFilter.replaceAll([".png", ".jpg", ".jpeg"]);
|
||||
fileOpenPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.picturesLibrary;
|
||||
}
|
||||
else if (mediaType == Camera.MediaType.VIDEO) {
|
||||
fileOpenPicker.fileTypeFilter.replaceAll(windowsPhoneVideoContainers);
|
||||
fileOpenPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.videosLibrary;
|
||||
}
|
||||
else {
|
||||
fileOpenPicker.fileTypeFilter.replaceAll(["*"]);
|
||||
fileOpenPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.documentsLibrary;
|
||||
}
|
||||
|
||||
Windows.UI.WebUI.WebUIApplication.addEventListener("activated", filePickerActivationHandler);
|
||||
fileOpenPicker.pickSingleFileAndContinue();
|
||||
}
|
||||
|
||||
function takePictureFromFileWindows(successCallback, errorCallback, args) {
|
||||
var mediaType = args[6],
|
||||
destinationType = args[1],
|
||||
targetWidth = args[3],
|
||||
targetHeight = args[4],
|
||||
encodingType = args[5];
|
||||
|
||||
var fileOpenPicker = new Windows.Storage.Pickers.FileOpenPicker();
|
||||
if (mediaType == Camera.MediaType.PICTURE) {
|
||||
fileOpenPicker.fileTypeFilter.replaceAll([".png", ".jpg", ".jpeg"]);
|
||||
fileOpenPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.picturesLibrary;
|
||||
}
|
||||
else if (mediaType == Camera.MediaType.VIDEO) {
|
||||
fileOpenPicker.fileTypeFilter.replaceAll(windowsVideoContainers);
|
||||
fileOpenPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.videosLibrary;
|
||||
}
|
||||
else {
|
||||
fileOpenPicker.fileTypeFilter.replaceAll(["*"]);
|
||||
fileOpenPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.documentsLibrary;
|
||||
}
|
||||
|
||||
fileOpenPicker.pickSingleFileAsync().done(function (file) {
|
||||
if (!file) {
|
||||
errorCallback("User didn't choose a file.");
|
||||
return;
|
||||
}
|
||||
if (destinationType == Camera.DestinationType.FILE_URI || destinationType == Camera.DestinationType.NATIVE_URI) {
|
||||
if (targetHeight > 0 && targetWidth > 0) {
|
||||
resizeImage(successCallback, errorCallback, file, targetWidth, targetHeight, encodingType);
|
||||
}
|
||||
else {
|
||||
var storageFolder = Windows.Storage.ApplicationData.current.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 {
|
||||
Windows.Storage.FileIO.readBufferAsync(file).done(function (buffer) {
|
||||
var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.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],
|
||||
targetWidth = args[3],
|
||||
targetHeight = args[4],
|
||||
encodingType = args[5],
|
||||
saveToPhotoAlbum = args[9],
|
||||
cameraDirection = args[11],
|
||||
capturePreview = null,
|
||||
captureCancelButton = null,
|
||||
capture = null,
|
||||
captureSettings = null,
|
||||
CaptureNS = Windows.Media.Capture;
|
||||
|
||||
function cameraPreviewOrientation() {
|
||||
// Rotate the cam since WP8.1 MediaCapture outputs video stream rotated 90° CCW
|
||||
if (screen.msOrientation === "portrait-primary" || screen.msOrientation === "portrait-secondary") {
|
||||
capture.setPreviewRotation(Windows.Media.Capture.VideoRotation.clockwise90Degrees);
|
||||
} else if (screen.msOrientation === "landscape-secondary") {
|
||||
capture.setPreviewRotation(Windows.Media.Capture.VideoRotation.clockwise180Degrees);
|
||||
} else {
|
||||
capture.setPreviewRotation(Windows.Media.Capture.VideoRotation.none);
|
||||
}
|
||||
}
|
||||
|
||||
var createCameraUI = function () {
|
||||
// Create fullscreen preview
|
||||
capturePreview = document.createElement("video");
|
||||
|
||||
// z-order style element for capturePreview and captureCancelButton elts
|
||||
// is necessary to avoid overriding by another page elements, -1 sometimes is not enough
|
||||
capturePreview.style.cssText = "position: fixed; left: 0; top: 0; width: 100%; height: 100%; z-index: 999";
|
||||
|
||||
// Create cancel button
|
||||
captureCancelButton = document.createElement("button");
|
||||
captureCancelButton.innerText = "Cancel";
|
||||
captureCancelButton.style.cssText = "position: fixed; right: 0; bottom: 0; display: block; margin: 20px; z-index: 1000";
|
||||
|
||||
capture = new CaptureNS.MediaCapture();
|
||||
|
||||
captureSettings = new CaptureNS.MediaCaptureInitializationSettings();
|
||||
captureSettings.streamingCaptureMode = CaptureNS.StreamingCaptureMode.video;
|
||||
};
|
||||
|
||||
var startCameraPreview = function () {
|
||||
// Search for available camera devices
|
||||
// This is necessary to detect which camera (front or back) we should use
|
||||
var expectedPanel = cameraDirection === 1 ? Windows.Devices.Enumeration.Panel.front : Windows.Devices.Enumeration.Panel.back;
|
||||
Windows.Devices.Enumeration.DeviceInformation.findAllAsync(Windows.Devices.Enumeration.DeviceClass.videoCapture)
|
||||
.done(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;
|
||||
}
|
||||
});
|
||||
|
||||
capture.initializeAsync(captureSettings).done(function () {
|
||||
// msdn.microsoft.com/en-us/library/windows/apps/hh452807.aspx
|
||||
capturePreview.msZoom = true;
|
||||
capturePreview.src = URL.createObjectURL(capture);
|
||||
capturePreview.play();
|
||||
|
||||
// Insert preview frame and controls into page
|
||||
document.body.appendChild(capturePreview);
|
||||
document.body.appendChild(captureCancelButton);
|
||||
|
||||
// Bind events to controls
|
||||
window.addEventListener('deviceorientation', cameraPreviewOrientation, false);
|
||||
capturePreview.addEventListener('click', captureAction);
|
||||
captureCancelButton.addEventListener('click', function () {
|
||||
destroyCameraPreview();
|
||||
errorCallback('Cancelled');
|
||||
}, false);
|
||||
}, function (err) {
|
||||
destroyCameraPreview();
|
||||
errorCallback('Camera intitialization error ' + err);
|
||||
});
|
||||
}, errorCallback);
|
||||
};
|
||||
|
||||
var destroyCameraPreview = function () {
|
||||
window.removeEventListener('deviceorientation', cameraPreviewOrientation, false);
|
||||
capturePreview.pause();
|
||||
capturePreview.src = null;
|
||||
[capturePreview, captureCancelButton].forEach(function(elem) {
|
||||
if (elem /* && elem in document.body.childNodes */) {
|
||||
document.body.removeChild(elem);
|
||||
}
|
||||
});
|
||||
if (capture) {
|
||||
capture.stopRecordAsync();
|
||||
capture = null;
|
||||
}
|
||||
};
|
||||
|
||||
var captureAction = function () {
|
||||
|
||||
var encodingProperties,
|
||||
fileName,
|
||||
generateUniqueCollisionOption = Windows.Storage.CreationCollisionOption.generateUniqueName,
|
||||
tempFolder = Windows.Storage.ApplicationData.current.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, generateUniqueCollisionOption)
|
||||
.then(function(tempCapturedFile) {
|
||||
return new WinJS.Promise(function (complete) {
|
||||
var imgStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
|
||||
capture.capturePhotoToStreamAsync(encodingProperties, imgStream)
|
||||
.then(function() {
|
||||
return Windows.Graphics.Imaging.BitmapDecoder.createAsync(imgStream);
|
||||
})
|
||||
.then(function(dec) {
|
||||
return Windows.Graphics.Imaging.BitmapEncoder.createForTranscodingAsync(imgStream, dec);
|
||||
})
|
||||
.then(function(enc) {
|
||||
// We need to rotate the photo 90° CW because by default wp8.1 takes 90° CCW rotated photos.
|
||||
enc.bitmapTransform.rotation = Windows.Graphics.Imaging.BitmapRotation.clockwise90Degrees;
|
||||
return enc.flushAsync();
|
||||
})
|
||||
.then(function() {
|
||||
return tempCapturedFile.openAsync(Windows.Storage.FileAccessMode.readWrite);
|
||||
})
|
||||
.then(function(fileStream) {
|
||||
imgStream.seek(0); // required for win8.1 emulator
|
||||
return Windows.Storage.Streams.RandomAccessStream.copyAsync(imgStream, fileStream);
|
||||
})
|
||||
.done(function() {
|
||||
imgStream.close();
|
||||
complete(tempCapturedFile);
|
||||
}, function() {
|
||||
imgStream.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);
|
||||
});
|
||||
};
|
||||
|
||||
try {
|
||||
createCameraUI();
|
||||
startCameraPreview();
|
||||
} catch (ex) {
|
||||
errorCallback(ex);
|
||||
}
|
||||
}
|
||||
|
||||
function takePictureFromCameraWindows(successCallback, errorCallback, args) {
|
||||
var destinationType = args[1],
|
||||
targetWidth = args[3],
|
||||
targetHeight = args[4],
|
||||
encodingType = args[5],
|
||||
allowCrop = !!args[7],
|
||||
saveToPhotoAlbum = args[9],
|
||||
cameraCaptureUI = new Windows.Media.Capture.CameraCaptureUI();
|
||||
cameraCaptureUI.photoSettings.allowCropping = allowCrop;
|
||||
|
||||
if (encodingType == Camera.EncodingType.PNG) {
|
||||
cameraCaptureUI.photoSettings.format = Windows.Media.Capture.CameraCaptureUIPhotoFormat.png;
|
||||
} else {
|
||||
cameraCaptureUI.photoSettings.format = Windows.Media.Capture.CameraCaptureUIPhotoFormat.jpeg;
|
||||
}
|
||||
|
||||
// decide which max pixels should be supported by targetWidth or targetHeight.
|
||||
if (targetWidth >= 1280 || targetHeight >= 960) {
|
||||
cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.large3M;
|
||||
} else if (targetWidth >= 1024 || targetHeight >= 768) {
|
||||
cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.mediumXga;
|
||||
} else if (targetWidth >= 800 || targetHeight >= 600) {
|
||||
cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.mediumXga;
|
||||
} else if (targetWidth >= 640 || targetHeight >= 480) {
|
||||
cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.smallVga;
|
||||
} else if (targetWidth >= 320 || targetHeight >= 240) {
|
||||
cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.verySmallQvga;
|
||||
} else {
|
||||
cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.highestAvailable;
|
||||
}
|
||||
|
||||
cameraCaptureUI.captureFileAsync(Windows.Media.Capture.CameraCaptureUIMode.photo).done(function(picture) {
|
||||
if (!picture) {
|
||||
errorCallback("User didn't capture a photo.");
|
||||
return;
|
||||
}
|
||||
|
||||
savePhoto(picture, {
|
||||
destinationType: destinationType,
|
||||
targetHeight: targetHeight,
|
||||
targetWidth: targetWidth,
|
||||
encodingType: encodingType,
|
||||
saveToPhotoAlbum: saveToPhotoAlbum
|
||||
}, successCallback, errorCallback);
|
||||
}, function() {
|
||||
errorCallback("Fail to capture a photo.");
|
||||
});
|
||||
}
|
||||
|
||||
function savePhoto(picture, options, successCallback, errorCallback) {
|
||||
// success callback for capture operation
|
||||
var success = function(picture) {
|
||||
var generateUniqueCollisionOption = Windows.Storage.CreationCollisionOption.generateUniqueName;
|
||||
if (options.destinationType == Camera.DestinationType.FILE_URI || options.destinationType == Camera.DestinationType.NATIVE_URI) {
|
||||
if (options.targetHeight > 0 && options.targetWidth > 0) {
|
||||
resizeImage(successCallback, errorCallback, picture, options.targetWidth, options.targetHeight, options.encodingType);
|
||||
} else {
|
||||
picture.copyAsync(Windows.Storage.ApplicationData.current.localFolder, picture.name, generateUniqueCollisionOption).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 {
|
||||
Windows.Storage.FileIO.readBufferAsync(picture).done(function(buffer) {
|
||||
var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer);
|
||||
picture.deleteAsync().done(function() {
|
||||
successCallback(strBase64);
|
||||
}, function(err) {
|
||||
errorCallback(err);
|
||||
});
|
||||
}, errorCallback);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!options.saveToPhotoAlbum) {
|
||||
success(picture);
|
||||
return;
|
||||
} 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 = Windows.Storage.Pickers.PickerLocationId.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);
|
||||
Windows.UI.WebUI.WebUIApplication.removeEventListener("activated", fileSaveHandler);
|
||||
}
|
||||
};
|
||||
Windows.UI.WebUI.WebUIApplication.addEventListener("activated", fileSaveHandler);
|
||||
savePicker.pickSaveFileAndContinue();
|
||||
} else {
|
||||
savePicker.pickSaveFileAsync()
|
||||
.done(saveFile, errorCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
require("cordova/exec/proxy").add("Camera",module.exports);
|
||||
|
||||
+3
-3
@@ -21,12 +21,12 @@
|
||||
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:rim="http://www.blackberry.com/ns/widgets"
|
||||
id="org.apache.cordova.camera.tests"
|
||||
version="0.3.7-dev">
|
||||
id="cordova-plugin-camera-tests"
|
||||
version="1.1.0">
|
||||
<name>Cordova Camera Plugin Tests</name>
|
||||
<license>Apache 2.0</license>
|
||||
|
||||
<dependency id="org.apache.cordova.file" version=">=1.0.1" />
|
||||
<dependency id="cordova-plugin-file" version=">=2.0.0" />
|
||||
|
||||
<js-module src="tests.js" name="tests">
|
||||
</js-module>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
document.getElementById('back').onclick = function () {
|
||||
window.qnx.callExtensionMethod('org.apache.cordova.camera', 'cancel');
|
||||
window.qnx.callExtensionMethod('cordova-plugin-camera', 'cancel');
|
||||
};
|
||||
window.navigator.webkitGetUserMedia(
|
||||
{ video: true },
|
||||
@@ -36,11 +36,11 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
canvas.width = video.videoWidth;
|
||||
canvas.height = video.videoHeight;
|
||||
canvas.getContext('2d').drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
|
||||
window.qnx.callExtensionMethod('org.apache.cordova.camera', canvas.toDataURL('img/png'));
|
||||
window.qnx.callExtensionMethod('cordova-plugin-camera', canvas.toDataURL('img/png'));
|
||||
};
|
||||
},
|
||||
function () {
|
||||
window.qnx.callExtensionMethod('org.apache.cordova.camera', 'error', 'getUserMedia failed');
|
||||
window.qnx.callExtensionMethod('cordova-plugin-camera', 'error', 'getUserMedia failed');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user