You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by be...@apache.org on 2013/06/24 23:12:06 UTC

git commit: [CB-3656] first pass wp8 support

Updated Branches:
  refs/heads/master f02ae7697 -> 380630624


[CB-3656] first pass wp8 support


Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-camera/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-camera/commit/38063062
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-camera/tree/38063062
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-camera/diff/38063062

Branch: refs/heads/master
Commit: 380630624ddee5ea6155564e7414ca45a24fed48
Parents: f02ae76
Author: Benn Mapes <be...@gmail.com>
Authored: Mon Jun 24 14:11:43 2013 -0700
Committer: Benn Mapes <be...@gmail.com>
Committed: Mon Jun 24 14:11:43 2013 -0700

----------------------------------------------------------------------
 plugin.xml        |  15 ++
 src/wp8/Camera.cs | 490 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 505 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-camera/blob/38063062/plugin.xml
----------------------------------------------------------------------
diff --git a/plugin.xml b/plugin.xml
index 304bb65..80e4c34 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -73,5 +73,20 @@ id="org.apache.cordova.core.CameraLauncher"
 
         <source-file src="src/wp7/Camera.cs" />
     </platform>
+
+    <!-- wp8 -->
+    <platform name="wp8">
+        <config-file target="config.xml" parent="/*">
+            <feature name="Camera">
+                <param name="wp-package" value="Camera"/>
+            </feature>
+        </config-file>
+
+        <config-file target="Properties/WMAppManifest.xml" parent="/Deployment/App/Capabilities">
+            <Capability Name="ID_CAP_ISV_CAMERA" />
+        </config-file>
+
+        <source-file src="src/wp8/Camera.cs" />
+    </platform>
           
 </plugin>

http://git-wip-us.apache.org/repos/asf/cordova-plugin-camera/blob/38063062/src/wp8/Camera.cs
----------------------------------------------------------------------
diff --git a/src/wp8/Camera.cs b/src/wp8/Camera.cs
new file mode 100644
index 0000000..5ff8045
--- /dev/null
+++ b/src/wp8/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
+    {
+
+        /// <summary>
+        /// Return base64 encoded string
+        /// </summary>
+        private const int DATA_URL = 0;
+
+        /// <summary>
+        /// Return file uri
+        /// </summary>
+        private const int FILE_URI = 1;
+
+        /// <summary>
+        /// Choose image from picture library
+        /// </summary>
+        private const int PHOTOLIBRARY = 0;
+
+        /// <summary>
+        /// Take picture from camera
+        /// </summary>
+
+        private const int CAMERA = 1;
+
+        /// <summary>
+        /// Choose image from picture library
+        /// </summary>
+        private const int SAVEDPHOTOALBUM = 2;
+
+        /// <summary>
+        /// Take a picture of type JPEG
+        /// </summary>
+        private const int JPEG = 0;
+
+        /// <summary>
+        /// Take a picture of type PNG
+        /// </summary>
+        private const int PNG = 1;
+
+        /// <summary>
+        /// Folder to store captured images
+        /// </summary>
+        private const string isoFolder = "CapturedImagesCache";
+
+        /// <summary>
+        /// Represents captureImage action options.
+        /// </summary>
+        [DataContract]
+        public class CameraOptions
+        {
+            /// <summary>
+            /// Source to getPicture from.
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "sourceType")]
+            public int PictureSourceType { get; set; }
+
+            /// <summary>
+            /// Format of image that returned from getPicture.
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "destinationType")]
+            public int DestinationType { get; set; }
+
+            /// <summary>
+            /// Quality of saved image
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "quality")]
+            public int Quality { get; set; }
+
+            /// <summary>
+            /// Controls whether or not the image is also added to the device photo album.
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "saveToPhotoAlbum")]
+            public bool SaveToPhotoAlbum { get; set; }
+
+            /// <summary>
+            /// Ignored
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "correctOrientation")]
+            public bool CorrectOrientation { get; set; }
+
+            
+
+            /// <summary>
+            /// Ignored
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "allowEdit")]
+            public bool AllowEdit { get; set; }
+
+                        /// <summary>
+            /// Height in pixels to scale image
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "encodingType")]
+            public int EncodingType { get; set; }
+
+                        /// <summary>
+            /// Height in pixels to scale image
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "mediaType")]
+            public int MediaType { get; set; }
+
+
+            /// <summary>
+            /// Height in pixels to scale image
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "targetHeight")]
+            public int TargetHeight { get; set; }
+
+
+            /// <summary>
+            /// Width in pixels to scale image
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "targetWidth")]
+            public int TargetWidth { get; set; }
+
+            /// <summary>
+            /// Creates options object with default parameters
+            /// </summary>
+            public CameraOptions()
+            {
+                this.SetDefaultValues(new StreamingContext());
+            }
+
+            /// <summary>
+            /// Initializes default values for class fields.
+            /// Implemented in separate method because default constructor is not invoked during deserialization.
+            /// </summary>
+            /// <param name="context"></param>
+            [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;
+            }
+        }
+
+        /// <summary>
+        /// Used to open photo library
+        /// </summary>
+        PhotoChooserTask photoChooserTask;
+
+        /// <summary>
+        /// Used to open camera application
+        /// </summary>
+        CameraCaptureTask cameraTask;
+
+        /// <summary>
+        /// Camera options
+        /// </summary>
+        CameraOptions cameraOptions;
+
+        public void takePicture(string options)
+        {
+            try
+            {
+                string[] args = JSON.JsonHelper.Deserialize<string[]>(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<CameraOptions>(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;
+            }
+        }
+
+        /// <summary>
+        /// Returns image content in a form of base64 string
+        /// </summary>
+        /// <param name="stream">Image stream</param>
+        /// <returns>Base64 representation of the image</returns>
+        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);
+            }
+        }
+
+        /// <summary>
+        /// Resize image
+        /// </summary>
+        /// <param name="stream">Image stream</param>
+        /// <param name="fileData">File data</param>
+        /// <returns>resized image</returns>
+        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;
+        }
+
+        /// <summary>
+        /// Saves captured image in isolated storage
+        /// </summary>
+        /// <param name="imageFileName">image file name</param>
+        /// <returns>Image path</returns>
+        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;
+            }
+        }
+
+    }
+}