diff --git a/plugin.xml b/plugin.xml
index 81c450a..7162678 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -52,5 +52,16 @@ id="org.apache.cordova.core.CameraLauncher"
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/wp7/Camera.cs b/src/wp7/Camera.cs
new file mode 100644
index 0000000..5ff8045
--- /dev/null
+++ b/src/wp7/Camera.cs
@@ -0,0 +1,490 @@
+/*
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Collections.Generic;
+using Microsoft.Phone.Tasks;
+using System.Runtime.Serialization;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Windows.Media.Imaging;
+using Microsoft.Phone;
+using Microsoft.Xna.Framework.Media;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+ public class Camera : BaseCommand
+ {
+
+ ///
+ /// Return base64 encoded string
+ ///
+ private const int DATA_URL = 0;
+
+ ///
+ /// Return file uri
+ ///
+ private const int FILE_URI = 1;
+
+ ///
+ /// Choose image from picture library
+ ///
+ private const int PHOTOLIBRARY = 0;
+
+ ///
+ /// Take picture from camera
+ ///
+
+ private const int CAMERA = 1;
+
+ ///
+ /// Choose image from picture library
+ ///
+ private const int SAVEDPHOTOALBUM = 2;
+
+ ///
+ /// Take a picture of type JPEG
+ ///
+ private const int JPEG = 0;
+
+ ///
+ /// Take a picture of type PNG
+ ///
+ private const int PNG = 1;
+
+ ///
+ /// Folder to store captured images
+ ///
+ private const string isoFolder = "CapturedImagesCache";
+
+ ///
+ /// Represents captureImage action options.
+ ///
+ [DataContract]
+ public class CameraOptions
+ {
+ ///
+ /// Source to getPicture from.
+ ///
+ [DataMember(IsRequired = false, Name = "sourceType")]
+ public int PictureSourceType { get; set; }
+
+ ///
+ /// Format of image that returned from getPicture.
+ ///
+ [DataMember(IsRequired = false, Name = "destinationType")]
+ public int DestinationType { get; set; }
+
+ ///
+ /// Quality of saved image
+ ///
+ [DataMember(IsRequired = false, Name = "quality")]
+ public int Quality { get; set; }
+
+ ///
+ /// Controls whether or not the image is also added to the device photo album.
+ ///
+ [DataMember(IsRequired = false, Name = "saveToPhotoAlbum")]
+ public bool SaveToPhotoAlbum { get; set; }
+
+ ///
+ /// Ignored
+ ///
+ [DataMember(IsRequired = false, Name = "correctOrientation")]
+ public bool CorrectOrientation { get; set; }
+
+
+
+ ///
+ /// Ignored
+ ///
+ [DataMember(IsRequired = false, Name = "allowEdit")]
+ public bool AllowEdit { get; set; }
+
+ ///
+ /// Height in pixels to scale image
+ ///
+ [DataMember(IsRequired = false, Name = "encodingType")]
+ public int EncodingType { get; set; }
+
+ ///
+ /// Height in pixels to scale image
+ ///
+ [DataMember(IsRequired = false, Name = "mediaType")]
+ public int MediaType { get; set; }
+
+
+ ///
+ /// Height in pixels to scale image
+ ///
+ [DataMember(IsRequired = false, Name = "targetHeight")]
+ public int TargetHeight { get; set; }
+
+
+ ///
+ /// Width in pixels to scale image
+ ///
+ [DataMember(IsRequired = false, Name = "targetWidth")]
+ public int TargetWidth { get; set; }
+
+ ///
+ /// Creates options object with default parameters
+ ///
+ public CameraOptions()
+ {
+ this.SetDefaultValues(new StreamingContext());
+ }
+
+ ///
+ /// Initializes default values for class fields.
+ /// Implemented in separate method because default constructor is not invoked during deserialization.
+ ///
+ ///
+ [OnDeserializing()]
+ public void SetDefaultValues(StreamingContext context)
+ {
+ PictureSourceType = CAMERA;
+ DestinationType = FILE_URI;
+ Quality = 80;
+ TargetHeight = -1;
+ TargetWidth = -1;
+ SaveToPhotoAlbum = false;
+ CorrectOrientation = true;
+ AllowEdit = false;
+ MediaType = -1;
+ EncodingType = -1;
+ }
+ }
+
+ ///
+ /// Used to open photo library
+ ///
+ PhotoChooserTask photoChooserTask;
+
+ ///
+ /// Used to open camera application
+ ///
+ CameraCaptureTask cameraTask;
+
+ ///
+ /// Camera options
+ ///
+ CameraOptions cameraOptions;
+
+ public void takePicture(string options)
+ {
+ try
+ {
+ string[] args = JSON.JsonHelper.Deserialize(options);
+ // ["quality", "destinationType", "sourceType", "targetWidth", "targetHeight", "encodingType",
+ // "mediaType", "allowEdit", "correctOrientation", "saveToPhotoAlbum" ]
+ this.cameraOptions = new CameraOptions();
+ this.cameraOptions.Quality = int.Parse(args[0]);
+ this.cameraOptions.DestinationType = int.Parse(args[1]);
+ this.cameraOptions.PictureSourceType = int.Parse(args[2]);
+ this.cameraOptions.TargetWidth = int.Parse(args[3]);
+ this.cameraOptions.TargetHeight = int.Parse(args[4]);
+ this.cameraOptions.EncodingType = int.Parse(args[5]);
+ this.cameraOptions.MediaType = int.Parse(args[6]);
+ this.cameraOptions.AllowEdit = bool.Parse(args[7]);
+ this.cameraOptions.CorrectOrientation = bool.Parse(args[8]);
+ this.cameraOptions.SaveToPhotoAlbum = bool.Parse(args[9]);
+
+ //this.cameraOptions = String.IsNullOrEmpty(options) ?
+ // new CameraOptions() : JSON.JsonHelper.Deserialize(options);
+ }
+ catch (Exception ex)
+ {
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+ return;
+ }
+
+ //TODO Check if all the options are acceptable
+
+
+ if (cameraOptions.PictureSourceType == CAMERA)
+ {
+ cameraTask = new CameraCaptureTask();
+ cameraTask.Completed += onCameraTaskCompleted;
+ cameraTask.Show();
+ }
+ else
+ {
+ if ((cameraOptions.PictureSourceType == PHOTOLIBRARY) || (cameraOptions.PictureSourceType == SAVEDPHOTOALBUM))
+ {
+ photoChooserTask = new PhotoChooserTask();
+ photoChooserTask.Completed += onPickerTaskCompleted;
+ photoChooserTask.Show();
+ }
+ else
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.NO_RESULT));
+ }
+ }
+
+ }
+
+ public void onCameraTaskCompleted(object sender, PhotoResult e)
+ {
+ if (e.Error != null)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+ return;
+ }
+
+ switch (e.TaskResult)
+ {
+ case TaskResult.OK:
+ try
+ {
+ string imagePathOrContent = string.Empty;
+
+ if (cameraOptions.DestinationType == FILE_URI)
+ {
+ // Save image in media library
+ if (cameraOptions.SaveToPhotoAlbum)
+ {
+ MediaLibrary library = new MediaLibrary();
+ Picture pict = library.SavePicture(e.OriginalFileName, e.ChosenPhoto); // to save to photo-roll ...
+ }
+
+ int orient = ImageExifHelper.getImageOrientationFromStream(e.ChosenPhoto);
+ int newAngle = 0;
+ switch (orient)
+ {
+ case ImageExifOrientation.LandscapeLeft:
+ newAngle = 90;
+ break;
+ case ImageExifOrientation.PortraitUpsideDown:
+ newAngle = 180;
+ break;
+ case ImageExifOrientation.LandscapeRight:
+ newAngle = 270;
+ break;
+ case ImageExifOrientation.Portrait:
+ default: break; // 0 default already set
+ }
+
+ Stream rotImageStream = ImageExifHelper.RotateStream(e.ChosenPhoto, newAngle);
+
+ // we should return stream position back after saving stream to media library
+ rotImageStream.Seek(0, SeekOrigin.Begin);
+
+ WriteableBitmap image = PictureDecoder.DecodeJpeg(rotImageStream);
+
+ imagePathOrContent = this.SaveImageToLocalStorage(image, Path.GetFileName(e.OriginalFileName));
+
+
+ }
+ else if (cameraOptions.DestinationType == DATA_URL)
+ {
+ imagePathOrContent = this.GetImageContent(e.ChosenPhoto);
+ }
+ else
+ {
+ // TODO: shouldn't this happen before we launch the camera-picker?
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Incorrect option: destinationType"));
+ return;
+ }
+
+ DispatchCommandResult(new PluginResult(PluginResult.Status.OK, imagePathOrContent));
+
+ }
+ catch (Exception)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error retrieving image."));
+ }
+ break;
+
+ case TaskResult.Cancel:
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection cancelled."));
+ break;
+
+ default:
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection did not complete!"));
+ break;
+ }
+
+ }
+
+ public void onPickerTaskCompleted(object sender, PhotoResult e)
+ {
+ if (e.Error != null)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+ return;
+ }
+
+ switch (e.TaskResult)
+ {
+ case TaskResult.OK:
+ try
+ {
+ string imagePathOrContent = string.Empty;
+
+ if (cameraOptions.DestinationType == FILE_URI)
+ {
+ WriteableBitmap image = PictureDecoder.DecodeJpeg(e.ChosenPhoto);
+ imagePathOrContent = this.SaveImageToLocalStorage(image, Path.GetFileName(e.OriginalFileName));
+ }
+ else if (cameraOptions.DestinationType == DATA_URL)
+ {
+ imagePathOrContent = this.GetImageContent(e.ChosenPhoto);
+
+ }
+ else
+ {
+ // TODO: shouldn't this happen before we launch the camera-picker?
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Incorrect option: destinationType"));
+ return;
+ }
+
+ DispatchCommandResult(new PluginResult(PluginResult.Status.OK, imagePathOrContent));
+
+ }
+ catch (Exception)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error retrieving image."));
+ }
+ break;
+
+ case TaskResult.Cancel:
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection cancelled."));
+ break;
+
+ default:
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection did not complete!"));
+ break;
+ }
+ }
+
+ ///
+ /// Returns image content in a form of base64 string
+ ///
+ /// Image stream
+ /// Base64 representation of the image
+ private string GetImageContent(Stream stream)
+ {
+ int streamLength = (int)stream.Length;
+ byte[] fileData = new byte[streamLength + 1];
+ stream.Read(fileData, 0, streamLength);
+
+ //use photo's actual width & height if user doesn't provide width & height
+ if (cameraOptions.TargetWidth < 0 && cameraOptions.TargetHeight < 0)
+ {
+ stream.Close();
+ return Convert.ToBase64String(fileData);
+ }
+ else
+ {
+ // resize photo
+ byte[] resizedFile = ResizePhoto(stream, fileData);
+ stream.Close();
+ return Convert.ToBase64String(resizedFile);
+ }
+ }
+
+ ///
+ /// Resize image
+ ///
+ /// Image stream
+ /// File data
+ /// resized image
+ private byte[] ResizePhoto(Stream stream, byte[] fileData)
+ {
+ int streamLength = (int)stream.Length;
+ int intResult = 0;
+
+ byte[] resizedFile;
+
+ stream.Read(fileData, 0, streamLength);
+
+ BitmapImage objBitmap = new BitmapImage();
+ MemoryStream objBitmapStream = new MemoryStream(fileData);
+ MemoryStream objBitmapStreamResized = new MemoryStream();
+ WriteableBitmap objWB;
+ objBitmap.SetSource(stream);
+ objWB = new WriteableBitmap(objBitmap);
+
+ // resize the photo with user defined TargetWidth & TargetHeight
+ Extensions.SaveJpeg(objWB, objBitmapStreamResized, cameraOptions.TargetWidth, cameraOptions.TargetHeight, 0, cameraOptions.Quality);
+
+ //Convert the resized stream to a byte array.
+ streamLength = (int)objBitmapStreamResized.Length;
+ resizedFile = new Byte[streamLength]; //-1
+ objBitmapStreamResized.Position = 0;
+ //for some reason we have to set Position to zero, but we don't have to earlier when we get the bytes from the chosen photo...
+ intResult = objBitmapStreamResized.Read(resizedFile, 0, streamLength);
+
+ return resizedFile;
+ }
+
+ ///
+ /// Saves captured image in isolated storage
+ ///
+ /// image file name
+ /// Image path
+ private string SaveImageToLocalStorage(WriteableBitmap image, string imageFileName)
+ {
+
+ if (image == null)
+ {
+ throw new ArgumentNullException("imageBytes");
+ }
+ try
+ {
+
+
+ var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
+
+ if (!isoFile.DirectoryExists(isoFolder))
+ {
+ isoFile.CreateDirectory(isoFolder);
+ }
+
+ string filePath = System.IO.Path.Combine("///" + isoFolder + "/", imageFileName);
+
+ using (var stream = isoFile.CreateFile(filePath))
+ {
+ // resize image if Height and Width defined via options
+ if (cameraOptions.TargetHeight > 0 && cameraOptions.TargetWidth > 0)
+ {
+ image.SaveJpeg(stream, cameraOptions.TargetWidth, cameraOptions.TargetHeight, 0, cameraOptions.Quality);
+ }
+ else
+ {
+ image.SaveJpeg(stream, image.PixelWidth, image.PixelHeight, 0, cameraOptions.Quality);
+ }
+ }
+
+ return new Uri(filePath, UriKind.Relative).ToString();
+ }
+ catch (Exception)
+ {
+ //TODO: log or do something else
+ throw;
+ }
+ }
+
+ }
+}