cordova-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From purplecabb...@apache.org
Subject [3/3] git commit: [CB-4122] remove dupe code
Date Mon, 08 Jul 2013 23:00:57 GMT
[CB-4122] remove dupe code


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

Branch: refs/heads/master
Commit: aef150793ce860b07c013592f92ea6a8e7d8af87
Parents: baf4893
Author: Jesse MacFadyen <purplecabbage@gmail.com>
Authored: Mon Jul 8 16:00:37 2013 -0700
Committer: Jesse MacFadyen <purplecabbage@gmail.com>
Committed: Mon Jul 8 16:00:37 2013 -0700

----------------------------------------------------------------------
 plugin.xml                       |  28 +-
 src/wp/Capture.cs                | 736 ++++++++++++++++++++++++++++++++++
 src/wp/UI/AudioCaptureTask.cs    | 107 +++++
 src/wp/UI/AudioRecorder.xaml     |  66 +++
 src/wp/UI/AudioRecorder.xaml.cs  | 330 +++++++++++++++
 src/wp/UI/VideoCaptureTask.cs    | 105 +++++
 src/wp/UI/VideoRecorder.xaml     |  52 +++
 src/wp/UI/VideoRecorder.xaml.cs  | 405 +++++++++++++++++++
 src/wp7/Capture.cs               | 736 ----------------------------------
 src/wp7/UI/AudioCaptureTask.cs   | 107 -----
 src/wp7/UI/AudioRecorder.xaml    |  66 ---
 src/wp7/UI/AudioRecorder.xaml.cs | 330 ---------------
 src/wp7/UI/VideoCaptureTask.cs   | 105 -----
 src/wp7/UI/VideoRecorder.xaml    |  52 ---
 src/wp7/UI/VideoRecorder.xaml.cs | 405 -------------------
 src/wp8/Capture.cs               | 736 ----------------------------------
 src/wp8/UI/AudioCaptureTask.cs   | 107 -----
 src/wp8/UI/AudioRecorder.xaml    |  66 ---
 src/wp8/UI/AudioRecorder.xaml.cs | 330 ---------------
 src/wp8/UI/VideoCaptureTask.cs   | 105 -----
 src/wp8/UI/VideoRecorder.xaml    |  52 ---
 src/wp8/UI/VideoRecorder.xaml.cs | 405 -------------------
 22 files changed, 1815 insertions(+), 3616 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/blob/aef15079/plugin.xml
----------------------------------------------------------------------
diff --git a/plugin.xml b/plugin.xml
index 68dfd2c..ad7e00c 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -77,13 +77,13 @@
             <Capability Name="ID_CAP_ISV_CAMERA" />
         </config-file>
 
-        <source-file src="src/wp7/Capture.cs" />
-        <source-file src="src/wp7/UI/AudioCaptureTask.cs" />
-        <source-file src="src/wp7/UI/AudioRecorder.xaml" />
-        <source-file src="src/wp7/UI/AudioRecorder.xaml.cs" />
-        <source-file src="src/wp7/UI/VideoCaptureTask.cs" />
-        <source-file src="src/wp7/UI/VideoRecorder.xaml" />
-        <source-file src="src/wp7/UI/VideoRecorder.xaml.cs" />
+        <source-file src="src/wp/Capture.cs" />
+        <source-file src="src/wp/UI/AudioCaptureTask.cs" />
+        <source-file src="src/wp/UI/AudioRecorder.xaml" />
+        <source-file src="src/wp/UI/AudioRecorder.xaml.cs" />
+        <source-file src="src/wp/UI/VideoCaptureTask.cs" />
+        <source-file src="src/wp/UI/VideoRecorder.xaml" />
+        <source-file src="src/wp/UI/VideoRecorder.xaml.cs" />
     </platform>
 
     <!-- wp8 -->
@@ -102,13 +102,13 @@
             <Capability Name="ID_CAP_ISV_CAMERA" />
         </config-file>
 
-        <source-file src="src/wp8/Capture.cs" />
-        <source-file src="src/wp8/UI/AudioCaptureTask.cs" />
-        <source-file src="src/wp8/UI/AudioRecorder.xaml" />
-        <source-file src="src/wp8/UI/AudioRecorder.xaml.cs" />
-        <source-file src="src/wp8/UI/VideoCaptureTask.cs" />
-        <source-file src="src/wp8/UI/VideoRecorder.xaml" />
-        <source-file src="src/wp8/UI/VideoRecorder.xaml.cs" />
+        <source-file src="src/wp/Capture.cs" />
+        <source-file src="src/wp/UI/AudioCaptureTask.cs" />
+        <source-file src="src/wp/UI/AudioRecorder.xaml" />
+        <source-file src="src/wp/UI/AudioRecorder.xaml.cs" />
+        <source-file src="src/wp/UI/VideoCaptureTask.cs" />
+        <source-file src="src/wp/UI/VideoRecorder.xaml" />
+        <source-file src="src/wp/UI/VideoRecorder.xaml.cs" />
     </platform>
         
 </plugin>

http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/blob/aef15079/src/wp/Capture.cs
----------------------------------------------------------------------
diff --git a/src/wp/Capture.cs b/src/wp/Capture.cs
new file mode 100644
index 0000000..1f5a327
--- /dev/null
+++ b/src/wp/Capture.cs
@@ -0,0 +1,736 @@
+/*  
+	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.Collections.Generic;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Runtime.Serialization;
+using System.Windows.Media.Imaging;
+using Microsoft.Phone;
+using Microsoft.Phone.Tasks;
+using Microsoft.Xna.Framework.Media;
+using WPCordovaClassLib.Cordova.UI;
+using AudioResult = WPCordovaClassLib.Cordova.UI.AudioCaptureTask.AudioResult;
+using VideoResult = WPCordovaClassLib.Cordova.UI.VideoCaptureTask.VideoResult;
+using System.Windows;
+using System.Diagnostics;
+using Microsoft.Phone.Controls;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// Provides access to the audio, image, and video capture capabilities of the device
+    /// </summary>
+    public class Capture : BaseCommand
+    {
+        #region Internal classes (options and resultant objects)
+
+        /// <summary>
+        /// Represents captureImage action options.
+        /// </summary>
+        [DataContract]
+        public class CaptureImageOptions
+        {
+            /// <summary>
+            /// The maximum number of images the device user can capture in a single capture operation. The value must be greater than or equal to 1 (defaults to 1).
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "limit")]
+            public int Limit { get; set; }
+
+            public static CaptureImageOptions Default
+            {
+                get { return new CaptureImageOptions() { Limit = 1 }; }
+            }
+        }
+
+        /// <summary>
+        /// Represents captureAudio action options.
+        /// </summary>
+        [DataContract]
+        public class CaptureAudioOptions
+        {
+            /// <summary>
+            /// The maximum number of audio files the device user can capture in a single capture operation. The value must be greater than or equal to 1 (defaults to 1).
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "limit")]
+            public int Limit { get; set; }
+
+            public static CaptureAudioOptions Default
+            {
+                get { return new CaptureAudioOptions() { Limit = 1 }; }
+            }
+        }
+
+        /// <summary>
+        /// Represents captureVideo action options.
+        /// </summary>
+        [DataContract]
+        public class CaptureVideoOptions
+        {
+            /// <summary>
+            /// The maximum number of video files the device user can capture in a single capture operation. The value must be greater than or equal to 1 (defaults to 1).
+            /// </summary>
+            [DataMember(IsRequired = false, Name = "limit")]
+            public int Limit { get; set; }
+
+            public static CaptureVideoOptions Default
+            {
+                get { return new CaptureVideoOptions() { Limit = 1 }; }
+            }
+        }
+
+        /// <summary>
+        /// Represents getFormatData action options.
+        /// </summary>
+        [DataContract]
+        public class MediaFormatOptions
+        {
+            /// <summary>
+            /// File path
+            /// </summary>
+            [DataMember(IsRequired = true, Name = "fullPath")]
+            public string FullPath { get; set; }
+
+            /// <summary>
+            /// File mime type
+            /// </summary>
+            [DataMember(Name = "type")]
+            public string Type { get; set; }
+
+        }
+
+        /// <summary>
+        /// Stores image info
+        /// </summary>
+        [DataContract]
+        public class MediaFile
+        {
+
+            [DataMember(Name = "name")]
+            public string FileName { get; set; }
+
+            [DataMember(Name = "fullPath")]
+            public string FilePath { get; set; }
+
+            [DataMember(Name = "type")]
+            public string Type { get; set; }
+
+            [DataMember(Name = "lastModifiedDate")]
+            public string LastModifiedDate { get; set; }
+
+            [DataMember(Name = "size")]
+            public long Size { get; set; }
+
+            public MediaFile(string filePath, Picture image)
+            {
+                this.FilePath = filePath;
+                this.FileName = System.IO.Path.GetFileName(this.FilePath);
+                this.Type = MimeTypeMapper.GetMimeType(FileName);
+                this.Size = image.GetImage().Length;
+
+                using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    this.LastModifiedDate = storage.GetLastWriteTime(filePath).DateTime.ToString();
+                }
+
+            }
+
+            public MediaFile(string filePath, Stream stream)
+            {
+                this.FilePath = filePath;
+                this.FileName = System.IO.Path.GetFileName(this.FilePath);
+                this.Type = MimeTypeMapper.GetMimeType(FileName);
+                this.Size = stream.Length;
+
+                using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    this.LastModifiedDate = storage.GetLastWriteTime(filePath).DateTime.ToString();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Stores additional media file data
+        /// </summary>
+        [DataContract]
+        public class MediaFileData
+        {
+            [DataMember(Name = "height")]
+            public int Height { get; set; }
+
+            [DataMember(Name = "width")]
+            public int Width { get; set; }
+
+            [DataMember(Name = "bitrate")]
+            public int Bitrate { get; set; }
+
+            [DataMember(Name = "duration")]
+            public int Duration { get; set; }
+
+            [DataMember(Name = "codecs")]
+            public string Codecs { get; set; }
+
+            public MediaFileData(WriteableBitmap image)
+            {
+                this.Height = image.PixelHeight;
+                this.Width = image.PixelWidth;
+                this.Bitrate = 0;
+                this.Duration = 0;
+                this.Codecs = "";
+            }
+        }
+
+        #endregion
+
+        /// <summary>
+        /// Folder to store captured images
+        /// </summary>
+        private string isoFolder = "CapturedImagesCache";
+
+        /// <summary>
+        /// Capture Image options
+        /// </summary>
+        protected CaptureImageOptions captureImageOptions;
+
+        /// <summary>
+        /// Capture Audio options
+        /// </summary>
+        protected CaptureAudioOptions captureAudioOptions;
+
+        /// <summary>
+        /// Capture Video options
+        /// </summary>
+        protected CaptureVideoOptions captureVideoOptions;
+
+        /// <summary>
+        /// Used to open camera application
+        /// </summary>
+        private CameraCaptureTask cameraTask;
+
+        /// <summary>
+        /// Used for audio recording
+        /// </summary>
+        private AudioCaptureTask audioCaptureTask;
+
+        /// <summary>
+        /// Used for video recording
+        /// </summary>
+        private VideoCaptureTask videoCaptureTask;
+
+        /// <summary>
+        /// Stores information about captured files
+        /// </summary>
+        List<MediaFile> files = new List<MediaFile>();
+
+        /// <summary>
+        /// Launches default camera application to capture image
+        /// </summary>
+        /// <param name="options">may contains limit or mode parameters</param>
+        public void captureImage(string options)
+        {
+            try
+            {
+                try
+                {
+
+                    string args = JSON.JsonHelper.Deserialize<string[]>(options)[0];
+                    this.captureImageOptions = String.IsNullOrEmpty(args) ? CaptureImageOptions.Default : JSON.JsonHelper.Deserialize<CaptureImageOptions>(args);
+
+                }
+                catch (Exception ex)
+                {
+                    this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                    return;
+                }
+
+
+                cameraTask = new CameraCaptureTask();
+                cameraTask.Completed += this.cameraTask_Completed;
+                cameraTask.Show();
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+        /// <summary>
+        /// Launches our own audio recording control to capture audio
+        /// </summary>
+        /// <param name="options">may contains additional parameters</param>
+        public void captureAudio(string options)
+        {
+            try
+            {
+                try
+                {
+                    string args = JSON.JsonHelper.Deserialize<string[]>(options)[0];
+                    this.captureAudioOptions = String.IsNullOrEmpty(args) ? CaptureAudioOptions.Default : JSON.JsonHelper.Deserialize<CaptureAudioOptions>(args);
+
+                }
+                catch (Exception ex)
+                {
+                    this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                    return;
+                }
+
+                audioCaptureTask = new AudioCaptureTask();
+                audioCaptureTask.Completed += audioRecordingTask_Completed;
+                audioCaptureTask.Show();
+
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+        /// <summary>
+        /// Launches our own video recording control to capture video
+        /// </summary>
+        /// <param name="options">may contains additional parameters</param>
+        public void captureVideo(string options)
+        {
+            try
+            {
+                try
+                {
+                    string args = JSON.JsonHelper.Deserialize<string[]>(options)[0];
+                    this.captureVideoOptions = String.IsNullOrEmpty(args) ? CaptureVideoOptions.Default : JSON.JsonHelper.Deserialize<CaptureVideoOptions>(args);
+
+                }
+                catch (Exception ex)
+                {
+                    this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                    return;
+                }
+
+                videoCaptureTask = new VideoCaptureTask();
+                videoCaptureTask.Completed += videoRecordingTask_Completed;
+                videoCaptureTask.Show();
+
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+        /// <summary>
+        /// Retrieves the format information of the media file.
+        /// </summary>
+        /// <param name="options"></param>
+        public void getFormatData(string options)
+        {
+            try
+            {
+                MediaFormatOptions mediaFormatOptions;
+                try
+                {
+                    mediaFormatOptions = new MediaFormatOptions();
+                    string[] optionStrings = JSON.JsonHelper.Deserialize<string[]>(options);
+                    mediaFormatOptions.FullPath = optionStrings[0];
+                    mediaFormatOptions.Type = optionStrings[1];
+                }
+                catch (Exception ex)
+                {
+                    this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                    return;
+                }
+
+                if (string.IsNullOrEmpty(mediaFormatOptions.FullPath))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                }
+
+                string mimeType = mediaFormatOptions.Type;
+
+                if (string.IsNullOrEmpty(mimeType))
+                {
+                    mimeType = MimeTypeMapper.GetMimeType(mediaFormatOptions.FullPath);
+                }
+
+                if (mimeType.Equals("image/jpeg"))
+                {
+                    Deployment.Current.Dispatcher.BeginInvoke(() =>
+                    {
+                        WriteableBitmap image = ExtractImageFromLocalStorage(mediaFormatOptions.FullPath);
+
+                        if (image == null)
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "File not found"));
+                            return;
+                        }
+
+                        MediaFileData mediaData = new MediaFileData(image);
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, mediaData));
+                    });
+                }
+                else
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+                }
+            }
+            catch (Exception)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+            }
+        }
+
+        /// <summary>
+        /// Opens specified file in media player
+        /// </summary>
+        /// <param name="options">MediaFile to play</param>
+        public void play(string options)
+        {
+            try
+            {
+                MediaFile file;
+
+                try
+                {
+                    file = String.IsNullOrEmpty(options) ? null : JSON.JsonHelper.Deserialize<MediaFile[]>(options)[0];
+
+                }
+                catch (Exception ex)
+                {
+                    this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
+                    return;
+                }
+
+                if (file == null || String.IsNullOrEmpty(file.FilePath))
+                {
+                    DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "File path is missing"));
+                    return;
+                }
+
+                // if url starts with '/' media player throws FileNotFound exception
+                Uri fileUri = new Uri(file.FilePath.TrimStart(new char[] { '/', '\\' }), UriKind.Relative);
+
+                MediaPlayerLauncher player = new MediaPlayerLauncher();
+                player.Media = fileUri;
+                player.Location = MediaLocationType.Data;
+                player.Show();
+
+                this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+
+            }
+            catch (Exception e)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
+            }
+        }
+
+
+        /// <summary>
+        /// Handles result of capture to save image information 
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e">stores information about current captured image</param>
+        private void cameraTask_Completed(object sender, PhotoResult e)
+        {
+
+            if (e.Error != null)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+                return;
+            }
+
+            switch (e.TaskResult)
+            {
+                case TaskResult.OK:
+                    try
+                    {
+                        string fileName = System.IO.Path.GetFileName(e.OriginalFileName);
+
+                        // Save image in media library
+                        MediaLibrary library = new MediaLibrary();
+                        Picture image = library.SavePicture(fileName, e.ChosenPhoto);
+
+                        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);
+
+                        // Save image in isolated storage    
+
+                        // we should return stream position back after saving stream to media library
+                        rotImageStream.Seek(0, SeekOrigin.Begin);
+
+                        byte[] imageBytes = new byte[rotImageStream.Length];
+                        rotImageStream.Read(imageBytes, 0, imageBytes.Length);
+                        rotImageStream.Dispose();
+                        string pathLocalStorage = this.SaveImageToLocalStorage(fileName, isoFolder, imageBytes);
+                        imageBytes = null;
+                        // Get image data
+                        MediaFile data = new MediaFile(pathLocalStorage, image);
+
+                        this.files.Add(data);
+
+                        if (files.Count < this.captureImageOptions.Limit)
+                        {
+                            cameraTask.Show();
+                        }
+                        else
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                            files.Clear();
+                        }
+                    }
+                    catch (Exception)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error capturing image."));
+                    }
+                    break;
+
+                case TaskResult.Cancel:
+                    if (files.Count > 0)
+                    {
+                        // User canceled operation, but some images were made
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                        files.Clear();
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Canceled."));
+                    }
+                    break;
+
+                default:
+                    if (files.Count > 0)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                        files.Clear();
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Did not complete!"));
+                    }
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Handles result of audio recording tasks 
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e">stores information about current captured audio</param>
+        private void audioRecordingTask_Completed(object sender, AudioResult e)
+        {
+
+            if (e.Error != null)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+                return;
+            }
+
+            switch (e.TaskResult)
+            {
+                case TaskResult.OK:
+                    try
+                    {
+                        // Get image data
+                        MediaFile data = new MediaFile(e.AudioFileName, e.AudioFile);
+
+                        this.files.Add(data);
+
+                        if (files.Count < this.captureAudioOptions.Limit)
+                        {
+                            audioCaptureTask.Show();
+                        }
+                        else
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                            files.Clear();
+                        }
+                    }
+                    catch (Exception)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error capturing audio."));
+                    }
+                    break;
+
+                case TaskResult.Cancel:
+                    if (files.Count > 0)
+                    {
+                        // User canceled operation, but some audio clips were made
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                        files.Clear();
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Canceled."));
+                    }
+                    break;
+
+                default:
+                    if (files.Count > 0)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                        files.Clear();
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Did not complete!"));
+                    }
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Handles result of video recording tasks 
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e">stores information about current captured video</param>
+        private void videoRecordingTask_Completed(object sender, VideoResult e)
+        {
+
+            if (e.Error != null)
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
+                return;
+            }
+
+            switch (e.TaskResult)
+            {
+                case TaskResult.OK:
+                    try
+                    {
+                        // Get image data
+                        MediaFile data = new MediaFile(e.VideoFileName, e.VideoFile);
+
+                        this.files.Add(data);
+
+                        if (files.Count < this.captureVideoOptions.Limit)
+                        {
+                            videoCaptureTask.Show();
+                        }
+                        else
+                        {
+                            DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                            files.Clear();
+                        }
+                    }
+                    catch (Exception)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error capturing video."));
+                    }
+                    break;
+
+                case TaskResult.Cancel:
+                    if (files.Count > 0)
+                    {
+                        // User canceled operation, but some video clips were made
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                        files.Clear();
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Canceled."));
+                    }
+                    break;
+
+                default:
+                    if (files.Count > 0)
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.OK, files));
+                        files.Clear();
+                    }
+                    else
+                    {
+                        DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Did not complete!"));
+                    }
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Extract file from Isolated Storage as WriteableBitmap object
+        /// </summary>
+        /// <param name="filePath"></param>
+        /// <returns></returns>
+        private WriteableBitmap ExtractImageFromLocalStorage(string filePath)
+        {
+            try
+            {
+
+                var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
+
+                using (var imageStream = isoFile.OpenFile(filePath, FileMode.Open, FileAccess.Read))
+                {
+                    var imageSource = PictureDecoder.DecodeJpeg(imageStream);
+                    return imageSource;
+                }
+            }
+            catch (Exception)
+            {
+                return null;
+            }
+        }
+
+
+        /// <summary>
+        /// Saves captured image in isolated storage
+        /// </summary>
+        /// <param name="imageFileName">image file name</param>
+        /// <param name="imageFolder">folder to store images</param>
+        /// <returns>Image path</returns>
+        private string SaveImageToLocalStorage(string imageFileName, string imageFolder, byte[] imageBytes)
+        {
+            if (imageBytes == null)
+            {
+                throw new ArgumentNullException("imageBytes");
+            }
+            try
+            {
+                var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
+
+                if (!isoFile.DirectoryExists(imageFolder))
+                {
+                    isoFile.CreateDirectory(imageFolder);
+                }
+                string filePath = System.IO.Path.Combine("/" + imageFolder + "/", imageFileName);
+
+                using (IsolatedStorageFileStream stream = isoFile.CreateFile(filePath))
+                {
+                    stream.Write(imageBytes, 0, imageBytes.Length);
+                }
+
+                return filePath;
+            }
+            catch (Exception)
+            {
+                //TODO: log or do something else
+                throw;
+            }
+        }
+
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/blob/aef15079/src/wp/UI/AudioCaptureTask.cs
----------------------------------------------------------------------
diff --git a/src/wp/UI/AudioCaptureTask.cs b/src/wp/UI/AudioCaptureTask.cs
new file mode 100644
index 0000000..9f43d23
--- /dev/null
+++ b/src/wp/UI/AudioCaptureTask.cs
@@ -0,0 +1,107 @@
+/*  
+	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.IO;
+using System.Windows;
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Tasks;
+
+namespace WPCordovaClassLib.Cordova.UI
+{
+    /// <summary>
+    /// Allows an application to launch the Audio Recording application. 
+    /// Use this to allow users to record audio from your application.
+    /// </summary>
+    public class AudioCaptureTask
+    {
+        /// <summary>
+        /// Represents recorded audio returned from a call to the Show method of
+        /// a WPCordovaClassLib.Cordova.Controls.AudioCaptureTask object
+        /// </summary>
+        public class AudioResult : TaskEventArgs
+        {
+            /// <summary>
+            /// Initializes a new instance of the AudioResult class.
+            /// </summary>
+            public AudioResult()
+            { }
+
+            /// <summary>
+            /// Initializes a new instance of the AudioResult class
+            /// with the specified Microsoft.Phone.Tasks.TaskResult.
+            /// </summary>
+            /// <param name="taskResult">Associated Microsoft.Phone.Tasks.TaskResult</param>
+            public AudioResult(TaskResult taskResult)
+                : base(taskResult)
+            { }
+
+            /// <summary>
+            ///  Gets the file name of the recorded audio.
+            /// </summary>
+            public Stream AudioFile { get; internal set; }
+
+            /// <summary>
+            /// Gets the stream containing the data for the recorded audio.
+            /// </summary>
+            public string AudioFileName { get; internal set; }
+        }
+
+        /// <summary>
+        /// Occurs when a audio recording task is completed.
+        /// </summary>
+        public event EventHandler<AudioResult> Completed;
+
+        /// <summary>
+        /// Shows Audio Recording application
+        /// </summary>
+        public void Show()
+        {
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                var root = Application.Current.RootVisual as PhoneApplicationFrame;
+
+                root.Navigated += new System.Windows.Navigation.NavigatedEventHandler(NavigationService_Navigated);
+
+                string baseUrl = WPCordovaClassLib.Cordova.Commands.BaseCommand.GetBaseURL();
+                // dummy parameter is used to always open a fresh version
+                root.Navigate(new System.Uri(baseUrl + "CordovaLib/UI/AudioRecorder.xaml?dummy=" + Guid.NewGuid().ToString(), UriKind.Relative));
+
+            });
+        }
+
+        /// <summary>
+        /// Performs additional configuration of the recording application.
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void NavigationService_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+            if (!(e.Content is AudioRecorder)) return;
+
+            (Application.Current.RootVisual as PhoneApplicationFrame).Navigated -= NavigationService_Navigated;
+
+            AudioRecorder audioRecorder = (AudioRecorder)e.Content;
+
+            if (audioRecorder != null)
+            {
+                audioRecorder.Completed += this.Completed;
+            }
+            else if (this.Completed != null)
+            {
+                this.Completed(this, new AudioResult(TaskResult.Cancel));
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/blob/aef15079/src/wp/UI/AudioRecorder.xaml
----------------------------------------------------------------------
diff --git a/src/wp/UI/AudioRecorder.xaml b/src/wp/UI/AudioRecorder.xaml
new file mode 100644
index 0000000..0fd26ab
--- /dev/null
+++ b/src/wp/UI/AudioRecorder.xaml
@@ -0,0 +1,66 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License. 
+-->
+<phone:PhoneApplicationPage 
+    x:Class="WPCordovaClassLib.Cordova.UI.AudioRecorder"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
+    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    FontFamily="{StaticResource PhoneFontFamilyNormal}"
+    FontSize="{StaticResource PhoneFontSizeNormal}"
+    Foreground="{StaticResource PhoneForegroundBrush}"
+    SupportedOrientations="Portrait" Orientation="Portrait"
+    mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
+    shell:SystemTray.IsVisible="True">
+
+    <!--LayoutRoot is the root grid where all page content is placed-->
+    <Grid x:Name="LayoutRoot" Background="Transparent">
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="*"/>
+        </Grid.RowDefinitions>
+
+        <!--TitlePanel contains the name of the application and page title-->
+        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="0,17,0,28">
+            <TextBlock x:Name="PageTitle" Text="Audio recorder" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
+        </StackPanel>
+
+        <!--ContentPanel - place additional content here-->
+        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
+            <Button Name="btnStartStop" Content="Start" Height="72" HorizontalAlignment="Left" Margin="156,96,0,0"  VerticalAlignment="Top" Width="160" Click="btnStartStop_Click" />
+            <Button Name="btnTake" Content="Take" IsEnabled="False" Height="72" HorizontalAlignment="Left" Margin="155,182,0,0" VerticalAlignment="Top" Width="160" Click="btnTake_Click" />
+            <TextBlock Height="30" HorizontalAlignment="Left" Margin="168,60,0,0" Name="txtDuration" Text="Duration: 00:00" VerticalAlignment="Top" />
+        </Grid>
+    </Grid>
+ 
+    <!--Sample code showing usage of ApplicationBar-->
+    <!--<phone:PhoneApplicationPage.ApplicationBar>
+        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
+            <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1"/>
+            <shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Button 2"/>
+            <shell:ApplicationBar.MenuItems>
+                <shell:ApplicationBarMenuItem Text="MenuItem 1"/>
+                <shell:ApplicationBarMenuItem Text="MenuItem 2"/>
+            </shell:ApplicationBar.MenuItems>
+        </shell:ApplicationBar>
+    </phone:PhoneApplicationPage.ApplicationBar>-->
+
+</phone:PhoneApplicationPage>

http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/blob/aef15079/src/wp/UI/AudioRecorder.xaml.cs
----------------------------------------------------------------------
diff --git a/src/wp/UI/AudioRecorder.xaml.cs b/src/wp/UI/AudioRecorder.xaml.cs
new file mode 100644
index 0000000..bb4b8bc
--- /dev/null
+++ b/src/wp/UI/AudioRecorder.xaml.cs
@@ -0,0 +1,330 @@
+/*  
+	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 Microsoft.Phone.Controls;
+using Microsoft.Phone.Tasks;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using System;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Windows;
+using System.Windows.Threading;
+using WPCordovaClassLib.Cordova.Commands;
+using AudioResult = WPCordovaClassLib.Cordova.UI.AudioCaptureTask.AudioResult;
+
+namespace WPCordovaClassLib.Cordova.UI
+{
+    /// <summary>
+    /// Implements Audio Recording application
+    /// </summary>
+    public partial class AudioRecorder : PhoneApplicationPage
+    {
+
+        #region Constants
+
+        private const string RecordingStartCaption = "Start";
+        private const string RecordingStopCaption = "Stop";
+
+        private const string LocalFolderName = "AudioCache";
+        private const string FileNameFormat = "Audio-{0}.wav";
+
+        #endregion
+
+        #region Callbacks
+
+        /// <summary>
+        /// Occurs when a audio recording task is completed.
+        /// </summary>
+        public event EventHandler<AudioResult> Completed;
+
+        #endregion
+
+        #region Fields
+
+        /// <summary>
+        /// Audio source
+        /// </summary>
+        private Microphone microphone;
+
+        /// <summary>
+        /// Temporary buffer to store audio chunk
+        /// </summary>
+        private byte[] buffer;
+
+        /// <summary>
+        /// Recording duration
+        /// </summary>
+        private TimeSpan duration;
+
+        /// <summary>
+        /// Output buffer
+        /// </summary>
+        private MemoryStream memoryStream;
+
+        /// <summary>
+        /// Xna game loop dispatcher
+        /// </summary>
+        DispatcherTimer dtXna;
+
+        /// <summary>
+        /// Recording result, dispatched back when recording page is closed
+        /// </summary>
+        private AudioResult result = new AudioResult(TaskResult.Cancel);
+
+        /// <summary>
+        /// Whether we are recording audio now
+        /// </summary>
+        private bool IsRecording
+        {
+            get
+            {
+                return (this.microphone != null && this.microphone.State == MicrophoneState.Started);
+            }
+        }
+
+        #endregion
+
+        /// <summary>
+        /// Creates new instance of the AudioRecorder class.
+        /// </summary>
+        public AudioRecorder()
+        {
+
+            this.InitializeXnaGameLoop();
+
+            // microphone requires special XNA initialization to work
+            InitializeComponent();
+        }
+
+        /// <summary>
+        /// Starts recording, data is stored in memory
+        /// </summary>
+        private void StartRecording()
+        {
+            this.microphone = Microphone.Default;
+            this.microphone.BufferDuration = TimeSpan.FromMilliseconds(500);
+
+            this.btnTake.IsEnabled = false;
+            this.btnStartStop.Content = RecordingStopCaption;
+
+            this.buffer = new byte[microphone.GetSampleSizeInBytes(this.microphone.BufferDuration)];
+            this.microphone.BufferReady += new EventHandler<EventArgs>(MicrophoneBufferReady);
+
+            MemoryStream stream = new MemoryStream();
+            this.memoryStream = stream;
+            int numBits = 16;
+            int numBytes = numBits / 8;
+
+            // inline version from AudioFormatsHelper
+            stream.Write(System.Text.Encoding.UTF8.GetBytes("RIFF"), 0, 4);
+            stream.Write(BitConverter.GetBytes(0), 0, 4);
+            stream.Write(System.Text.Encoding.UTF8.GetBytes("WAVE"), 0, 4);
+            stream.Write(System.Text.Encoding.UTF8.GetBytes("fmt "), 0, 4);
+            stream.Write(BitConverter.GetBytes(16), 0, 4);
+            stream.Write(BitConverter.GetBytes((short)1), 0, 2);
+            stream.Write(BitConverter.GetBytes((short)1), 0, 2);
+            stream.Write(BitConverter.GetBytes(this.microphone.SampleRate), 0, 4);
+            stream.Write(BitConverter.GetBytes(this.microphone.SampleRate * numBytes), 0, 4);
+            stream.Write(BitConverter.GetBytes((short)(numBytes)), 0, 2);
+            stream.Write(BitConverter.GetBytes((short)(numBits)), 0, 2);
+            stream.Write(System.Text.Encoding.UTF8.GetBytes("data"), 0, 4);
+            stream.Write(BitConverter.GetBytes(0), 0, 4);
+
+            this.duration = new TimeSpan(0);
+
+            this.microphone.Start();
+        }
+
+        /// <summary>
+        /// Stops recording
+        /// </summary>
+        private void StopRecording()
+        {
+            this.microphone.Stop();
+
+            this.microphone.BufferReady -= MicrophoneBufferReady;
+
+            this.microphone = null;
+
+            btnStartStop.Content = RecordingStartCaption;
+
+            // check there is some data
+            this.btnTake.IsEnabled = true;
+        }
+
+        /// <summary>
+        /// Handles Start/Stop events
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void btnStartStop_Click(object sender, RoutedEventArgs e)
+        {
+
+            if (this.IsRecording)
+            {
+                this.StopRecording();
+            }
+            else
+            {
+                this.StartRecording();
+            }
+        }
+
+        /// <summary>
+        /// Handles Take button click
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void btnTake_Click(object sender, RoutedEventArgs e)
+        {
+            this.result = this.SaveAudioClipToLocalStorage();
+
+            if (Completed != null)
+            {
+                Completed(this, result);
+            }
+
+            if (this.NavigationService.CanGoBack)
+            {
+                this.NavigationService.GoBack();
+            }
+        }
+
+        /// <summary>
+        /// Handles page closing event, stops recording if needed and dispatches results.
+        /// </summary>
+        /// <param name="e"></param>
+        protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
+        {
+            if (IsRecording)
+            {
+                StopRecording();
+            }
+
+            this.FinalizeXnaGameLoop();
+
+            base.OnNavigatedFrom(e);
+        }
+
+        /// <summary>
+        /// Copies data from microphone to memory storages and updates recording state
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void MicrophoneBufferReady(object sender, EventArgs e)
+        {
+            this.microphone.GetData(this.buffer);
+            this.memoryStream.Write(this.buffer, 0, this.buffer.Length);
+            TimeSpan bufferDuration = this.microphone.BufferDuration;
+
+            this.Dispatcher.BeginInvoke(() =>
+            {
+                this.duration += bufferDuration;
+
+                this.txtDuration.Text = "Duration: " +
+                    this.duration.Minutes.ToString().PadLeft(2, '0') + ":" +
+                    this.duration.Seconds.ToString().PadLeft(2, '0');
+            });
+
+        }
+
+        /// <summary>
+        /// Writes audio data from memory to isolated storage
+        /// </summary>
+        /// <returns></returns>
+        private AudioResult SaveAudioClipToLocalStorage()
+        {
+            if (this.memoryStream == null || this.memoryStream.Length <= 0)
+            {
+                return new AudioResult(TaskResult.Cancel);
+            }
+
+            //this.memoryStream.UpdateWavStream();
+            long position = memoryStream.Position;
+            memoryStream.Seek(4, SeekOrigin.Begin);
+            memoryStream.Write(BitConverter.GetBytes((int)memoryStream.Length - 8), 0, 4);
+            memoryStream.Seek(40, SeekOrigin.Begin);
+            memoryStream.Write(BitConverter.GetBytes((int)memoryStream.Length - 44), 0, 4);
+            memoryStream.Seek(position, SeekOrigin.Begin);
+
+            // save audio data to local isolated storage
+
+            string filename = String.Format(FileNameFormat, Guid.NewGuid().ToString());
+
+            try
+            {
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+
+                    if (!isoFile.DirectoryExists(LocalFolderName))
+                    {
+                        isoFile.CreateDirectory(LocalFolderName);
+                    }
+
+                    string filePath = System.IO.Path.Combine("/" + LocalFolderName + "/", filename);
+
+                    this.memoryStream.Seek(0, SeekOrigin.Begin);
+
+                    using (IsolatedStorageFileStream fileStream = isoFile.CreateFile(filePath))
+                    {
+
+                        this.memoryStream.CopyTo(fileStream);
+                    }
+
+                    AudioResult result = new AudioResult(TaskResult.OK);
+                    result.AudioFileName = filePath;
+
+                    result.AudioFile = this.memoryStream;
+                    result.AudioFile.Seek(0, SeekOrigin.Begin);
+
+                    return result;
+                }
+
+
+
+            }
+            catch (Exception)
+            {
+                //TODO: log or do something else
+                throw;
+            }
+        }
+
+        /// <summary>
+        /// Special initialization required for the microphone: XNA game loop
+        /// </summary>
+        private void InitializeXnaGameLoop()
+        {
+            // Timer to simulate the XNA game loop (Microphone is from XNA)
+            this.dtXna = new DispatcherTimer();
+            this.dtXna.Interval = TimeSpan.FromMilliseconds(33);
+            this.dtXna.Tick += delegate { try { FrameworkDispatcher.Update(); } catch { } };
+            this.dtXna.Start();
+        }
+        /// <summary>
+        /// Finalizes XNA game loop for microphone
+        /// </summary>
+        private void FinalizeXnaGameLoop()
+        {
+            // Timer to simulate the XNA game loop (Microphone is from XNA)
+            if (dtXna != null)
+            {
+                dtXna.Stop();
+                dtXna = null;
+            }
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/blob/aef15079/src/wp/UI/VideoCaptureTask.cs
----------------------------------------------------------------------
diff --git a/src/wp/UI/VideoCaptureTask.cs b/src/wp/UI/VideoCaptureTask.cs
new file mode 100644
index 0000000..def2a88
--- /dev/null
+++ b/src/wp/UI/VideoCaptureTask.cs
@@ -0,0 +1,105 @@
+/*  
+	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.IO;
+using System.Windows;
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Tasks;
+
+namespace WPCordovaClassLib.Cordova.UI
+{
+    /// <summary>
+    /// Allows an application to launch the Video Recording application. 
+    /// Use this to allow users to record video from your application.
+    /// </summary>
+    public class VideoCaptureTask
+    {
+        /// <summary>
+        /// Represents recorded video returned from a call to the Show method of
+        /// a WPCordovaClassLib.Cordova.Controls.VideoCaptureTask object
+        /// </summary>
+        public class VideoResult : TaskEventArgs
+        {
+            /// <summary>
+            /// Initializes a new instance of the VideoResult class.
+            /// </summary>
+            public VideoResult()
+            { }
+
+            /// <summary>
+            /// Initializes a new instance of the VideoResult class
+            /// with the specified Microsoft.Phone.Tasks.TaskResult.
+            /// </summary>
+            /// <param name="taskResult">Associated Microsoft.Phone.Tasks.TaskResult</param>
+            public VideoResult(TaskResult taskResult)
+                : base(taskResult)
+            { }
+
+            /// <summary>
+            ///  Gets the file name of the recorded Video.
+            /// </summary>
+            public Stream VideoFile { get; internal set; }
+
+            /// <summary>
+            /// Gets the stream containing the data for the recorded Video.
+            /// </summary>
+            public string VideoFileName { get; internal set; }
+        }
+
+        /// <summary>
+        /// Occurs when a Video recording task is completed.
+        /// </summary>
+        public event EventHandler<VideoResult> Completed;
+
+        /// <summary>
+        /// Shows Video Recording application
+        /// </summary>
+        public void Show()
+        {
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                var root = Application.Current.RootVisual as PhoneApplicationFrame;
+
+                root.Navigated += new System.Windows.Navigation.NavigatedEventHandler(NavigationService_Navigated);
+
+                string baseUrl = WPCordovaClassLib.Cordova.Commands.BaseCommand.GetBaseURL();
+                // dummy parameter is used to always open a fresh version
+                root.Navigate(new System.Uri(baseUrl + "CordovaLib/UI/VideoRecorder.xaml?dummy=" + Guid.NewGuid().ToString(), UriKind.Relative));
+            });
+        }
+
+        /// <summary>
+        /// Performs additional configuration of the recording application.
+        /// </summary>
+        private void NavigationService_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+            if (!(e.Content is VideoRecorder)) return;
+
+            (Application.Current.RootVisual as PhoneApplicationFrame).Navigated -= NavigationService_Navigated;
+
+            VideoRecorder VideoRecorder = (VideoRecorder)e.Content;
+
+            if (VideoRecorder != null)
+            {
+                VideoRecorder.Completed += this.Completed;
+            }
+            else if (this.Completed != null)
+            {
+                this.Completed(this, new VideoResult(TaskResult.Cancel));
+            }
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/blob/aef15079/src/wp/UI/VideoRecorder.xaml
----------------------------------------------------------------------
diff --git a/src/wp/UI/VideoRecorder.xaml b/src/wp/UI/VideoRecorder.xaml
new file mode 100644
index 0000000..c78fdb0
--- /dev/null
+++ b/src/wp/UI/VideoRecorder.xaml
@@ -0,0 +1,52 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License. 
+-->
+<phone:PhoneApplicationPage 
+    x:Class="WPCordovaClassLib.Cordova.UI.VideoRecorder"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
+    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="480"
+    FontFamily="{StaticResource PhoneFontFamilyNormal}"
+    FontSize="{StaticResource PhoneFontSizeNormal}"
+    Foreground="{StaticResource PhoneForegroundBrush}"
+    SupportedOrientations="Landscape" Orientation="LandscapeLeft"
+    shell:SystemTray.IsVisible="False">
+   
+    <Canvas x:Name="LayoutRoot" Background="Transparent" Grid.ColumnSpan="1" Grid.Column="0">
+
+        <Rectangle 
+            x:Name="viewfinderRectangle"
+            Width="640" 
+            Height="480" 
+            HorizontalAlignment="Left" 
+            Canvas.Left="80"/>
+        
+    </Canvas>
+
+    <phone:PhoneApplicationPage.ApplicationBar>
+        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True" x:Name="PhoneAppBar" Opacity="0.0">
+            <shell:ApplicationBarIconButton IconUri="/Images/appbar.feature.video.rest.png" Text="Record"  x:Name="btnStartRecording" Click="StartRecording_Click" />
+            <shell:ApplicationBarIconButton IconUri="/Images/appbar.save.rest.png" Text="Take" x:Name="btnTakeVideo" Click="TakeVideo_Click"/>            
+        </shell:ApplicationBar>
+    </phone:PhoneApplicationPage.ApplicationBar>
+
+</phone:PhoneApplicationPage>

http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/blob/aef15079/src/wp/UI/VideoRecorder.xaml.cs
----------------------------------------------------------------------
diff --git a/src/wp/UI/VideoRecorder.xaml.cs b/src/wp/UI/VideoRecorder.xaml.cs
new file mode 100644
index 0000000..75bcfd9
--- /dev/null
+++ b/src/wp/UI/VideoRecorder.xaml.cs
@@ -0,0 +1,405 @@
+/*  
+	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.IO;
+using System.IO.IsolatedStorage;
+using System.Windows.Media;
+using System.Windows.Navigation;
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Shell;
+using Microsoft.Phone.Tasks;
+using VideoResult = WPCordovaClassLib.Cordova.UI.VideoCaptureTask.VideoResult;
+
+namespace WPCordovaClassLib.Cordova.UI
+{
+    public partial class VideoRecorder : PhoneApplicationPage
+    {
+
+        #region Constants
+
+        /// <summary>
+        /// Caption for record button in ready state
+        /// </summary>
+        private const string RecordingStartCaption = "Record";
+
+        /// <summary>
+        /// Caption for record button in recording state
+        /// </summary>
+        private const string RecordingStopCaption = "Stop";
+
+        /// <summary>
+        /// Start record icon URI
+        /// </summary>
+        private const string StartIconUri = "/Images/appbar.feature.video.rest.png";
+
+        /// <summary>
+        /// Stop record icon URI
+        /// </summary>
+        private const string StopIconUri = "/Images/appbar.stop.rest.png";
+
+        /// <summary>
+        /// Folder to save video clips
+        /// </summary>
+        private const string LocalFolderName = "VideoCache";
+
+        /// <summary>
+        /// File name format
+        /// </summary>
+        private const string FileNameFormat = "Video-{0}.mp4";
+
+        /// <summary>
+        /// Temporary file name
+        /// </summary>
+        private const string defaultFileName = "NewVideoFile.mp4";
+
+        #endregion
+
+        #region Callbacks
+        /// <summary>
+        /// Occurs when a video recording task is completed.
+        /// </summary>
+        public event EventHandler<VideoResult> Completed;
+
+        #endregion
+
+        #region Fields
+
+        /// <summary>
+        /// Viewfinder for capturing video
+        /// </summary>
+        private VideoBrush videoRecorderBrush;
+
+        /// <summary>
+        /// Path to save video clip
+        /// </summary>
+        private string filePath;
+
+        /// <summary>
+        /// Source for capturing video. 
+        /// </summary>
+        private CaptureSource captureSource;
+
+        /// <summary>
+        /// Video device
+        /// </summary>
+        private VideoCaptureDevice videoCaptureDevice;
+
+        /// <summary>
+        /// File sink so save recording video in Isolated Storage
+        /// </summary>
+        private FileSink fileSink;
+
+        /// <summary>
+        /// For managing button and application state 
+        /// </summary>
+        private enum VideoState { Initialized, Ready, Recording, CameraNotSupported };
+
+        /// <summary>
+        /// Current video state
+        /// </summary>
+        private VideoState currentVideoState;
+
+        /// <summary>
+        /// Stream to return result
+        /// </summary>
+        private MemoryStream memoryStream;
+
+        /// <summary>
+        /// Recording result, dispatched back when recording page is closed
+        /// </summary>
+        private VideoResult result = new VideoResult(TaskResult.Cancel);
+
+        #endregion
+
+        /// <summary>
+        /// Initializes components
+        /// </summary>
+        public VideoRecorder()
+        {
+            InitializeComponent();
+
+            PhoneAppBar = (ApplicationBar)ApplicationBar;
+            PhoneAppBar.IsVisible = true;
+            btnStartRecording = ((ApplicationBarIconButton)ApplicationBar.Buttons[0]);
+            btnTakeVideo = ((ApplicationBarIconButton)ApplicationBar.Buttons[1]);
+        }
+
+        /// <summary>
+        /// Initializes the video recorder then page is loading
+        /// </summary>
+        protected override void OnNavigatedTo(NavigationEventArgs e)
+        {
+            base.OnNavigatedTo(e);
+            this.InitializeVideoRecorder();
+        }
+
+        /// <summary>
+        /// Disposes camera and media objects then leave the page
+        /// </summary>
+        protected override void OnNavigatedFrom(NavigationEventArgs e)
+        {
+            this.DisposeVideoRecorder();
+
+            if (this.Completed != null)
+            {
+                this.Completed(this, result);
+            }
+            base.OnNavigatedFrom(e);
+        }
+
+        /// <summary>
+        /// Handles TakeVideo button click
+        /// </summary>
+        private void TakeVideo_Click(object sender, EventArgs e)
+        {
+            this.result = this.SaveVideoClip();
+            this.NavigateBack();
+        }
+
+        private void NavigateBack()
+        {
+            if (this.NavigationService.CanGoBack)
+            {
+                this.NavigationService.GoBack();
+            }
+        }
+
+        /// <summary>
+        /// Resaves video clip from temporary directory to persistent 
+        /// </summary>
+        private VideoResult SaveVideoClip()
+        {
+            try
+            {
+                using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                {
+                    if (string.IsNullOrEmpty(filePath) || (!isoFile.FileExists(filePath)))
+                    {
+                        return new VideoResult(TaskResult.Cancel);
+                    }
+
+                    string fileName = String.Format(FileNameFormat, Guid.NewGuid().ToString());
+                    string newPath = Path.Combine("/" + LocalFolderName + "/", fileName);
+                    isoFile.CopyFile(filePath, newPath);
+                    isoFile.DeleteFile(filePath);
+
+                    memoryStream = new MemoryStream();
+                    using (IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream(newPath, FileMode.Open, isoFile))
+                    {
+                        fileStream.CopyTo(memoryStream);
+                    }
+
+                    VideoResult result = new VideoResult(TaskResult.OK);
+                    result.VideoFileName = newPath;
+                    result.VideoFile = this.memoryStream;
+                    result.VideoFile.Seek(0, SeekOrigin.Begin);
+                    return result;
+                }
+
+            }
+            catch (Exception)
+            {
+                return new VideoResult(TaskResult.None);
+            }
+        }
+
+        /// <summary>
+        /// Updates the buttons on the UI thread based on current state. 
+        /// </summary>
+        /// <param name="currentState">current UI state</param>
+        private void UpdateUI(VideoState currentState)
+        {
+            Dispatcher.BeginInvoke(delegate
+            {
+                switch (currentState)
+                {
+                    case VideoState.CameraNotSupported:
+                        btnStartRecording.IsEnabled = false;
+                        btnTakeVideo.IsEnabled = false;
+                        break;
+
+                    case VideoState.Initialized:
+                        btnStartRecording.Text = RecordingStartCaption;
+                        btnStartRecording.IconUri = new Uri(StartIconUri, UriKind.Relative);
+                        btnTakeVideo.IsEnabled = false;
+                        break;
+
+                    case VideoState.Ready:
+                        btnStartRecording.Text = RecordingStartCaption;
+                        btnStartRecording.IconUri = new Uri(StartIconUri, UriKind.Relative);
+                        btnTakeVideo.IsEnabled = true;
+                        break;
+
+                    case VideoState.Recording:
+                        btnStartRecording.Text = RecordingStopCaption;
+                        btnStartRecording.IconUri = new Uri(StopIconUri, UriKind.Relative);
+                        btnTakeVideo.IsEnabled = false;
+                        break;
+
+                    default:
+                        break;
+                }
+                currentVideoState = currentState;
+            });
+        }
+
+        /// <summary>
+        /// Initializes VideoRecorder
+        /// </summary>
+        public void InitializeVideoRecorder()
+        {
+            if (captureSource == null)
+            {
+                captureSource = new CaptureSource();
+                fileSink = new FileSink();
+                videoCaptureDevice = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
+
+                if (videoCaptureDevice != null)
+                {
+                    videoRecorderBrush = new VideoBrush();
+                    videoRecorderBrush.SetSource(captureSource);
+                    viewfinderRectangle.Fill = videoRecorderBrush;
+                    captureSource.Start();
+                    this.UpdateUI(VideoState.Initialized);
+                }
+                else
+                {
+                    this.UpdateUI(VideoState.CameraNotSupported);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Sets recording state: start recording 
+        /// </summary>
+        private void StartVideoRecording()
+        {
+            try
+            {
+                if ((captureSource.VideoCaptureDevice != null) && (captureSource.State == CaptureState.Started))
+                {
+                    captureSource.Stop();
+                    fileSink.CaptureSource = captureSource;
+                    filePath = System.IO.Path.Combine("/" + LocalFolderName + "/", defaultFileName);
+
+                    using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+                    {
+                        if (!isoFile.DirectoryExists(LocalFolderName))
+                        {
+                            isoFile.CreateDirectory(LocalFolderName);
+                        }
+
+                        if (isoFile.FileExists(filePath))
+                        {
+                            isoFile.DeleteFile(filePath);
+                        }
+                    }
+
+                    fileSink.IsolatedStorageFileName = filePath;
+                }
+
+                if (captureSource.VideoCaptureDevice != null
+                    && captureSource.State == CaptureState.Stopped)
+                {
+                    captureSource.Start();
+                }
+                this.UpdateUI(VideoState.Recording);
+            }
+            catch (Exception)
+            {
+                this.result = new VideoResult(TaskResult.None);
+                this.NavigateBack();
+            }
+        }
+
+        /// <summary>
+        /// Sets the recording state: stop recording
+        /// </summary>
+        private void StopVideoRecording()
+        {
+            try
+            {
+                if ((captureSource.VideoCaptureDevice != null) && (captureSource.State == CaptureState.Started))
+                {
+                    captureSource.Stop();
+                    fileSink.CaptureSource = null;
+                    fileSink.IsolatedStorageFileName = null;
+                    this.StartVideoPreview();
+                }
+            }
+            catch (Exception)
+            {
+                this.result = new VideoResult(TaskResult.None);
+                this.NavigateBack();
+            }
+        }
+
+        /// <summary>
+        /// Sets the recording state: display the video on the viewfinder. 
+        /// </summary>
+        private void StartVideoPreview()
+        {
+            try
+            {
+                if ((captureSource.VideoCaptureDevice != null) && (captureSource.State == CaptureState.Stopped))
+                {
+                    videoRecorderBrush.SetSource(captureSource);
+                    viewfinderRectangle.Fill = videoRecorderBrush;
+                    captureSource.Start();
+                    this.UpdateUI(VideoState.Ready);
+                }
+            }
+            catch (Exception)
+            {
+                this.result = new VideoResult(TaskResult.None);
+                this.NavigateBack();
+            }
+        }
+
+        /// <summary>
+        /// Starts video recording 
+        /// </summary>
+        private void StartRecording_Click(object sender, EventArgs e)
+        {
+            if (currentVideoState == VideoState.Recording)
+            {
+                this.StopVideoRecording();
+            }
+            else
+            {
+                this.StartVideoRecording();
+            }
+        }
+
+        /// <summary>
+        /// Releases resources
+        /// </summary>
+        private void DisposeVideoRecorder()
+        {
+            if (captureSource != null)
+            {
+                if ((captureSource.VideoCaptureDevice != null) && (captureSource.State == CaptureState.Started))
+                {
+                    captureSource.Stop();
+                }
+                captureSource = null;
+                videoCaptureDevice = null;
+                fileSink = null;
+                videoRecorderBrush = null;
+            }
+        }
+
+    }
+}


Mime
View raw message