You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by pu...@apache.org on 2012/02/22 09:15:23 UTC

[20/29] stage 2 - fix errors

http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/blob/4789ce6d/framework/Cordova/Commands/MimeTypeMapper.cs
----------------------------------------------------------------------
diff --git a/framework/Cordova/Commands/MimeTypeMapper.cs b/framework/Cordova/Commands/MimeTypeMapper.cs
new file mode 100644
index 0000000..3493b98
--- /dev/null
+++ b/framework/Cordova/Commands/MimeTypeMapper.cs
@@ -0,0 +1,99 @@
+/*  
+	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.Collections.Generic;
+using System.IO;
+
+namespace WP7GapClassLib.Cordova.Commands
+{
+    /// <summary>
+    /// Represents file extension to mime type mapper.
+    /// </summary>
+    public static class MimeTypeMapper
+    {
+        /// <summary>
+        /// For unknown type it is recommended to use 'application/octet-stream'
+        /// http://stackoverflow.com/questions/1176022/unknown-file-type-mime
+        /// </summary>
+        private static string DefaultMimeType = "application/octet-stream";
+        
+        /// <summary>
+        /// Stores mime type for all necessary extension
+        /// </summary>
+        private static readonly Dictionary<string, string> MIMETypesDictionary = new Dictionary<string, string>
+                                                                             {                                                                                
+                                                                                 {"avi", "video/x-msvideo"},
+                                                                                 {"bmp", "image/bmp"},                                                                                                                                                                
+                                                                                 {"gif", "image/gif"},                                                                                                                                                               
+                                                                                 {"jpe", "image/jpeg"},
+                                                                                 {"jpeg", "image/jpeg"},
+                                                                                 {"jpg", "image/jpeg"},                                                                                                                                                             
+                                                                                 {"mov", "video/quicktime"},
+                                                                                 {"mp2", "audio/mpeg"},
+                                                                                 {"mp3", "audio/mpeg"},
+                                                                                 {"mp4", "video/mp4"},
+                                                                                 {"mpe", "video/mpeg"},
+                                                                                 {"mpeg", "video/mpeg"},
+                                                                                 {"mpg", "video/mpeg"},
+                                                                                 {"mpga", "audio/mpeg"},                                                                                
+                                                                                 {"pbm", "image/x-portable-bitmap"},
+                                                                                 {"pcm", "audio/x-pcm"},
+                                                                                 {"pct", "image/pict"},
+                                                                                 {"pgm", "image/x-portable-graymap"},
+                                                                                 {"pic", "image/pict"},
+                                                                                 {"pict", "image/pict"},
+                                                                                 {"png", "image/png"},
+                                                                                 {"pnm", "image/x-portable-anymap"},
+                                                                                 {"pnt", "image/x-macpaint"},
+                                                                                 {"pntg", "image/x-macpaint"},
+                                                                                 {"ppm", "image/x-portable-pixmap"},
+                                                                                 {"qt", "video/quicktime"},
+                                                                                 {"ra", "audio/x-pn-realaudio"},
+                                                                                 {"ram", "audio/x-pn-realaudio"},
+                                                                                 {"ras", "image/x-cmu-raster"},
+                                                                                 {"rgb", "image/x-rgb"},
+                                                                                 {"snd", "audio/basic"},
+                                                                                 {"txt", "text/plain"},
+                                                                                 {"tif", "image/tiff"},
+                                                                                 {"tiff", "image/tiff"},
+                                                                                 {"wav", "audio/x-wav"},
+                                                                                 {"wbmp", "image/vnd.wap.wbmp"},
+
+                                                                             };
+        /// <summary>
+        /// Gets mime type by file extension
+        /// </summary>
+        /// <param name="fileName">file name to extract extension</param>
+        /// <returns>mime type</returns>
+        public static string GetMimeType(string fileName)
+        {
+            string ext = Path.GetExtension(fileName);
+
+            // invalid extension
+            if (string.IsNullOrEmpty(ext) || !ext.StartsWith("."))
+            {
+                return DefaultMimeType;
+            }
+
+            ext = ext.Remove(0, 1);
+
+            if (MIMETypesDictionary.ContainsKey(ext))
+            {
+                return MIMETypesDictionary[ext];
+            }
+
+            return DefaultMimeType;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/blob/4789ce6d/framework/Cordova/Commands/Notification.cs
----------------------------------------------------------------------
diff --git a/framework/Cordova/Commands/Notification.cs b/framework/Cordova/Commands/Notification.cs
new file mode 100644
index 0000000..5746662
--- /dev/null
+++ b/framework/Cordova/Commands/Notification.cs
@@ -0,0 +1,203 @@
+/*  
+	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.Windows;
+using System.Windows.Controls;
+using Microsoft.Devices;
+using System.Runtime.Serialization;
+using System.Threading;
+using System.Windows.Resources;
+using Microsoft.Phone.Controls;
+using Microsoft.Xna.Framework.Audio;
+
+namespace WP7GapClassLib.Cordova.Commands
+{
+    public class Notification : BaseCommand
+    {
+        static ProgressBar progressBar = null;
+        const int DEFAULT_DURATION = 5;
+
+        // alert, confirm, blink, vibrate, beep
+        // blink api - doesn't look like there is an equivalent api we can use...
+        // vibrate api - http://msdn.microsoft.com/en-us/library/microsoft.devices.vibratecontroller(v=VS.92).aspx
+        // beep api - can probably use: http://msdn.microsoft.com/en-us/library/microsoft.phone.scheduler.alarm(v=VS.92).aspx
+        //          - examples of alarm class http://mkdot.net/blogs/filip/archive/2011/06/06/windows-phone-multitasking-part-2-2.aspx
+
+        //MessageBoxResult res = MessageBox.Show("Could not call script: " + ex.Message, "caption", MessageBoxButton.OKCancel);
+
+        [DataContract]
+        public class AlertOptions
+        {
+            [OnDeserializing]
+            public void OnDeserializing(StreamingContext context)
+            {
+                // set defaults
+                this.message = "message";
+                this.title = "Alert";
+                this.buttonLabel = "ok";
+            }
+
+            /// <summary>
+            /// message to display in the alert box
+            /// </summary>
+            [DataMember]
+            public string message;
+
+            /// <summary>
+            /// title displayed on the alert window
+            /// </summary>
+            [DataMember]
+            public string title;
+
+            /// <summary>
+            /// text to display on the button
+            /// </summary>
+            [DataMember]
+            public string buttonLabel;
+        }
+
+        public void alert(string options)
+        {
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                AlertOptions alertOpts = JSON.JsonHelper.Deserialize<AlertOptions>(options);
+                MessageBoxResult res = MessageBox.Show(alertOpts.message, alertOpts.title,MessageBoxButton.OK);
+
+                DispatchCommandResult(new PluginResult(PluginResult.Status.OK,(int)res));
+            });
+        }
+
+        public void confirm(string options)
+        {
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                AlertOptions alertOpts = JSON.JsonHelper.Deserialize<AlertOptions>(options);
+
+                MessageBoxResult res = MessageBox.Show(alertOpts.message, alertOpts.title, MessageBoxButton.OKCancel);
+                DispatchCommandResult(new PluginResult(PluginResult.Status.OK, (int)res));
+            });
+        }
+
+        public void beep(string count)
+        {
+            int times = int.Parse(count);
+
+            StreamResourceInfo sri = Application.GetResourceStream(new Uri("/WP7GapClassLib;component/resources/notification-beep.wav", UriKind.Relative));
+            if (sri != null)
+            {
+                SoundEffect effect = SoundEffect.FromStream(sri.Stream);
+                SoundEffectInstance inst = effect.CreateInstance();
+                ThreadPool.QueueUserWorkItem((o) =>
+                {
+                    // cannot interact with UI !!
+                    do
+                    {
+                        inst.Play();
+                        Thread.Sleep(effect.Duration + TimeSpan.FromMilliseconds(100));
+                    }
+                    while (--times > 0);
+
+               });
+
+            }
+         
+            // TODO: may need a listener to trigger DispatchCommandResult after the alarm has finished executing...
+            DispatchCommandResult();
+        }
+
+        // Display an inderminate progress indicator
+        public void activityStart(string unused)
+        {
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                var t1 = Application.Current.RootVisual;
+                PhoneApplicationFrame frame =  t1 as PhoneApplicationFrame;
+
+                if (frame != null)
+                {
+                    PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
+
+                    if (page != null)
+                    {
+                        var temp = page.FindName("LayoutRoot");
+                        Grid grid = temp as Grid;
+                        if (grid != null)
+                        {
+                            if (progressBar != null)
+                            {
+                                grid.Children.Remove(progressBar);
+                            }
+                            progressBar = new ProgressBar();
+                            progressBar.IsIndeterminate = true;
+                            progressBar.IsEnabled = true;
+
+                            grid.Children.Add(progressBar);
+                        }
+                    }
+                }
+            });
+        }
+
+
+        // Remove our inderminate progress indicator
+        public void activityStop(string unused)
+        {
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                if (progressBar != null)
+                {
+                    progressBar.IsEnabled = false;
+                    PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
+                    if (frame != null)
+                    {
+                        PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
+                        if (page != null)
+                        {
+                            Grid grid = page.FindName("LayoutRoot") as Grid;
+                            if (grid != null)
+                            {
+                                grid.Children.Remove(progressBar);
+                            }
+                        }
+                    }
+                    progressBar = null;
+                }
+            });
+        }
+
+        public void vibrate(string vibrateDuration)
+        {
+            int msecs = 200; // set default
+
+            try
+            {
+                msecs = int.Parse(vibrateDuration);
+                if (msecs < 1)
+                {
+                    msecs = 1;
+                }
+            }
+            catch (FormatException)
+            {
+
+            }
+
+            VibrateController.Default.Start(TimeSpan.FromMilliseconds(msecs));
+
+            // TODO: may need to add listener to trigger DispatchCommandResult when the vibration ends...
+            DispatchCommandResult();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/blob/4789ce6d/framework/Cordova/DOMStorageHelper.cs
----------------------------------------------------------------------
diff --git a/framework/Cordova/DOMStorageHelper.cs b/framework/Cordova/DOMStorageHelper.cs
new file mode 100644
index 0000000..987d8ab
--- /dev/null
+++ b/framework/Cordova/DOMStorageHelper.cs
@@ -0,0 +1,145 @@
+/*  
+	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.Windows.Shapes;
+using System.IO.IsolatedStorage;
+using System.Collections.Generic;
+using Microsoft.Phone.Controls;
+using System.Linq;
+using WP7GapClassLib.Cordova.JSON;
+
+/*
+ * Translates DOMStorage API between JS and Isolated Storage
+ * Missing pieces : QUOTA_EXCEEDED_ERR  + StorageEvent  
+ * */
+
+namespace WP7GapClassLib
+{
+    public class DOMStorageHelper
+    {
+        protected WebBrowser webBrowser1;
+
+        public DOMStorageHelper(WebBrowser gapBrowser)
+        {
+            this.webBrowser1 = gapBrowser;
+            // always clear session at creation
+            UserSettings["sessionStorage"] = new Dictionary<string, string>();
+
+            if (!UserSettings.Contains("localStorage"))
+            {
+                UserSettings["localStorage"] = new Dictionary<string, string>();
+                UserSettings.Save();
+            }
+            Application.Current.Exit += new EventHandler(OnAppExit);
+        }
+
+        void OnAppExit(object sender, EventArgs e)
+        {
+            UserSettings.Remove("sessionStorage");
+            UserSettings.Save();
+        }
+
+        protected IsolatedStorageSettings UserSettings
+        {
+            get
+            {
+                return IsolatedStorageSettings.ApplicationSettings;
+            }
+        }
+
+        protected Dictionary<string, string> getStorageByType(string type)
+        {
+            if (!UserSettings.Contains(type))
+            {
+                UserSettings[type] = new Dictionary<string, string>();
+                UserSettings.Save();
+            }
+            return UserSettings[type] as Dictionary<string,string>;
+        }
+
+
+        public void HandleStorageCommand(string commandStr)
+        {
+            
+            string[] split = commandStr.Split('/');
+            if (split.Length > 3)
+            {
+                string api = split[0];
+                string type = split[1]; // localStorage || sessionStorage
+                string command = split[2];
+                string param = split[3];
+
+                Dictionary<string, string> currentStorage = getStorageByType(type);
+
+                switch (command)
+                {
+                    case "get":
+                        {
+
+                            if (currentStorage.Keys.Contains(param))
+                            {
+                                string value = currentStorage[param];
+                                webBrowser1.InvokeScript("execScript", "window." + type + ".onResult('" + param + "','" + value + "');");
+                            }
+                            else
+                            {
+                                webBrowser1.InvokeScript("execScript", "window." + type + ".onResult('" + param + "');");
+                            }
+
+                        }
+                        break;
+                    case "load":
+                        {
+                            string[] keys = currentStorage.Keys.ToArray();
+                            string jsonString = JsonHelper.Serialize(keys);
+                            string callbackJS = "window." + type + ".onKeysChanged('" + jsonString + "');";
+                            webBrowser1.InvokeScript("execScript", callbackJS);
+                        }
+                        break;
+                    case "set":
+                        {
+                            // TODO: check that length is not out of bounds
+                            currentStorage[param] = split[4];
+                            UserSettings.Save();
+                            string[] keys = currentStorage.Keys.ToArray();
+                            string jsonString = JsonHelper.Serialize(keys);
+                            string callbackJS = "window." + type + ".onKeysChanged('" + jsonString + "');";
+                            webBrowser1.InvokeScript("execScript", callbackJS);
+                        }
+                        break;
+                    case "remove":
+                        currentStorage.Remove(param);
+                        UserSettings.Save();
+                        break;
+                    case "clear":
+                        currentStorage = new Dictionary<string, string>();
+                        UserSettings[type] = currentStorage;
+                        UserSettings.Save();
+                        break;
+                }
+
+            }
+
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/blob/4789ce6d/framework/Cordova/JSON/JsonHelper.cs
----------------------------------------------------------------------
diff --git a/framework/Cordova/JSON/JsonHelper.cs b/framework/Cordova/JSON/JsonHelper.cs
new file mode 100644
index 0000000..bf5a401
--- /dev/null
+++ b/framework/Cordova/JSON/JsonHelper.cs
@@ -0,0 +1,86 @@
+/*  
+	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.Windows.Shapes;
+using System.Runtime.Serialization.Json;
+using System.IO;
+using System.Collections.Generic;
+using System.Text;
+
+namespace WP7GapClassLib.Cordova.JSON
+{
+    /// <summary>
+    /// Provides JSON serialization/deserialization functionality.
+    /// </summary>
+    public static class JsonHelper
+    {
+        /// <summary>
+        /// Serializes object to JSON string representation
+        /// </summary>
+        /// <param name="obj">object to serialize</param>
+        /// <returns>JSON representation of the object. Returns 'null' string for null passed as argument</returns>
+        public static string Serialize(object obj)
+        {
+            if (obj == null)
+            {
+                return "null";
+            }
+
+            DataContractJsonSerializer ser = new DataContractJsonSerializer(obj.GetType());
+
+            MemoryStream ms = new MemoryStream();
+            ser.WriteObject(ms, obj);
+
+            ms.Position = 0;
+            
+            string json = String.Empty;
+
+            using(StreamReader sr = new StreamReader(ms))
+            {
+                json = sr.ReadToEnd();
+            }
+
+            ms.Close();
+
+            return json;
+
+        }
+
+        /// <summary>
+        /// Parses json string to object instance
+        /// </summary>
+        /// <typeparam name="T">type of the object</typeparam>
+        /// <param name="json">json string representation of the object</param>
+        /// <returns>Deserialized object instance</returns>
+        public static T Deserialize<T>(string json)
+        {
+           DataContractJsonSerializer deserializer = new DataContractJsonSerializer(typeof(T));
+
+           using (MemoryStream mem = new MemoryStream(Encoding.UTF8.GetBytes(json)))
+           {
+               return (T)deserializer.ReadObject(mem);
+           }
+
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/blob/4789ce6d/framework/Cordova/NativeExecution.cs
----------------------------------------------------------------------
diff --git a/framework/Cordova/NativeExecution.cs b/framework/Cordova/NativeExecution.cs
new file mode 100644
index 0000000..720d3f9
--- /dev/null
+++ b/framework/Cordova/NativeExecution.cs
@@ -0,0 +1,204 @@
+/*  
+	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.Diagnostics;
+using System.Threading;
+using Microsoft.Devices;
+using Microsoft.Phone.Controls;
+using WP7GapClassLib.Cordova.Commands;
+
+namespace WP7GapClassLib.Cordova
+{
+    /// <summary>
+    /// Implements logic to execute native command and return result back.
+    /// All commands are executed asynchronous.
+    /// </summary>
+    public class NativeExecution
+    {
+        /// <summary>
+        /// Reference to web part where application is hosted
+        /// </summary>
+        private readonly WebBrowser webBrowser;
+
+        /// <summary>
+        /// Creates new instance of a NativeExecution class. 
+        /// </summary>
+        /// <param name="browser">Reference to web part where application is hosted</param>
+        public NativeExecution(ref WebBrowser browser)
+        {
+            if (browser == null)
+            {
+                throw new ArgumentNullException("browser");
+            }
+
+            this.webBrowser = browser;
+        }
+
+        /// <summary>
+        /// Returns where application is running on emulator
+        /// </summary>
+        /// <returns>True if running on emulator, otherwise False</returns>
+        public static bool IsRunningOnEmulator()
+        {
+            return Microsoft.Devices.Environment.DeviceType == DeviceType.Emulator;
+        }
+
+        /// <summary>
+        /// Executes command and returns result back.
+        /// </summary>
+        /// <param name="commandCallParams">Command to execute</param>
+        public void ProcessCommand(CordovaCommandCall commandCallParams)
+        {
+
+            if (commandCallParams == null)
+            {
+                throw new ArgumentNullException("commandCallParams");
+            }
+
+            try
+            {
+
+                BaseCommand bc = CommandFactory.CreateByServiceName(commandCallParams.Service);
+
+                if (bc == null)
+                {
+                    this.OnCommandResult(commandCallParams.CallbackId, new PluginResult(PluginResult.Status.CLASS_NOT_FOUND_EXCEPTION));
+                    return;
+                }
+
+                EventHandler<PluginResult> OnCommandResultHandler = delegate(object o, PluginResult res)
+                {
+                    this.OnCommandResult(commandCallParams.CallbackId, res);
+                };
+
+                bc.OnCommandResult += OnCommandResultHandler;
+
+                EventHandler<ScriptCallback> OnCustomScriptHandler = delegate(object o, ScriptCallback script)
+                {
+                    this.InvokeScriptCallback(script);
+                };
+
+
+                bc.OnCustomScript += OnCustomScriptHandler;
+
+                // TODO: alternative way is using thread pool (ThreadPool.QueueUserWorkItem) instead of 
+                // new thread for every command call; but num threads are not sufficient - 2 threads per CPU core
+
+
+                Thread thread = new Thread(func =>
+                {
+
+                    try
+                    {
+                        bc.InvokeMethodNamed(commandCallParams.Action, commandCallParams.Args);
+                    }
+                    catch (Exception)
+                    {
+                        bc.OnCommandResult -= OnCommandResultHandler;
+                        bc.OnCustomScript -= OnCustomScriptHandler;
+
+                        Debug.WriteLine("failed to InvokeMethodNamed :: " + commandCallParams.Action + " on Object :: " + commandCallParams.Service);
+
+                        this.OnCommandResult(commandCallParams.CallbackId, new PluginResult(PluginResult.Status.INVALID_ACTION));
+
+                        return;
+                    }
+                });
+
+                thread.Start();
+            }
+            catch (Exception ex)
+            {
+                // ERROR
+                Debug.WriteLine(String.Format("Unable to execute command :: {0}:{1}:{3} ", 
+                    commandCallParams.Service, commandCallParams.Action, ex.Message));
+
+                this.OnCommandResult(commandCallParams.CallbackId, new PluginResult(PluginResult.Status.ERROR));
+                return;
+            }
+        }
+
+        /// <summary>
+        /// Handles command execution result.
+        /// </summary>
+        /// <param name="callbackId">Command callback identifier on client side</param>
+        /// <param name="result">Execution result</param>
+        private void OnCommandResult(string callbackId, PluginResult result)
+        {
+            #region  args checking
+            
+            if (result == null)
+            {
+                Debug.WriteLine("OnCommandResult missing result argument");
+                return;
+            }
+
+            if (String.IsNullOrEmpty(callbackId))
+            {
+                Debug.WriteLine("OnCommandResult missing callbackId argument");
+                return;
+            }
+
+            #endregion
+
+            string status = ((int)result.Result).ToString();
+            string jsonResult = result.ToJSONString();
+
+            ScriptCallback scriptCallback = null;
+
+            if (String.IsNullOrEmpty(result.Cast))
+            {
+                scriptCallback = new ScriptCallback("CordovaCommandResult", new string[] { status, callbackId, jsonResult });
+            }
+            else
+            {
+                scriptCallback = new ScriptCallback("CordovaCommandResult", new string[] { status, callbackId, jsonResult, result.Cast });
+            }
+
+            this.InvokeScriptCallback(scriptCallback);
+
+        }
+
+        /// <summary>
+        /// Executes client java script
+        /// </summary>
+        /// <param name="script">Script to execute on client side</param>
+        private void InvokeScriptCallback(ScriptCallback script)
+        {
+            if (script == null)
+            {
+                throw new ArgumentNullException("script");
+            }
+
+            if (String.IsNullOrEmpty(script.ScriptName))
+            {
+                throw new ArgumentNullException("ScriptName");
+            }
+
+            this.webBrowser.Dispatcher.BeginInvoke((ThreadStart)delegate()
+            {
+                try
+                {
+                    this.webBrowser.InvokeScript(script.ScriptName, script.Args);
+                }
+                catch (Exception ex)
+                {
+                    Debug.WriteLine("Exception in InvokeScriptCallback :: " + ex.Message);
+                }
+            });
+        }
+
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/blob/4789ce6d/framework/Cordova/OrientationHelper.cs
----------------------------------------------------------------------
diff --git a/framework/Cordova/OrientationHelper.cs b/framework/Cordova/OrientationHelper.cs
new file mode 100644
index 0000000..5631c93
--- /dev/null
+++ b/framework/Cordova/OrientationHelper.cs
@@ -0,0 +1,128 @@
+/*  
+	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.Windows.Shapes;
+using Microsoft.Phone.Controls;
+
+namespace WP7GapClassLib.Cordova
+{
+    public class OrientationHelper
+    {
+        protected WebBrowser gapBrowser;
+        protected PhoneApplicationPage page;
+       // private PageOrientation CurrentOrientation = PageOrientation.PortraitUp;
+        //private PageOrientation[] SupportedOrientations; // TODO:
+
+        public OrientationHelper(WebBrowser gapBrowser, PhoneApplicationPage gapPage)
+        {
+            this.gapBrowser = gapBrowser;
+            page = gapPage;
+
+            page.OrientationChanged += new EventHandler<OrientationChangedEventArgs>(page_OrientationChanged);
+            gapBrowser.LoadCompleted += new System.Windows.Navigation.LoadCompletedEventHandler(gapBrowser_LoadCompleted);
+
+
+        }
+
+        void gapBrowser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+            int i = 0;
+
+            switch (this.page.Orientation)
+            {
+                case PageOrientation.Portrait: // intentional fall through
+                case PageOrientation.PortraitUp:
+                    i = 0;
+                    break;
+                case PageOrientation.PortraitDown:
+                    i = 180;
+                    break;
+                case PageOrientation.Landscape: // intentional fall through
+                case PageOrientation.LandscapeLeft:
+                    i = -90;
+                    break;
+                case PageOrientation.LandscapeRight:
+                    i = 90;
+                    break;
+            }
+            // Cordova.fireEvent('orientationchange', window);
+            string jsCallback = String.Format("window.orientation = {0};", i);
+
+            try
+            {
+                gapBrowser.InvokeScript("execScript", jsCallback);
+            }
+            catch(Exception)
+            {
+            }
+        }
+
+        void page_OrientationChanged(object sender, OrientationChangedEventArgs e)
+        {
+            int i = 0;
+
+            switch (e.Orientation)
+            {
+                case PageOrientation.Portrait: // intentional fall through
+                case PageOrientation.PortraitUp:
+                    i = 0;
+                    break;
+                case PageOrientation.PortraitDown:
+                    i = 180;
+                    break;
+                case PageOrientation.Landscape: // intentional fall through
+                case PageOrientation.LandscapeLeft:
+                    i = -90;
+                    break;
+                case PageOrientation.LandscapeRight:
+                    i = 90;
+                    break;
+            }
+            // Cordova.fireEvent('orientationchange', window);
+            string jsCallback = String.Format("window.orientation = {0};", i);
+
+            try
+            {
+                
+                gapBrowser.InvokeScript("execScript", jsCallback);
+
+                jsCallback = "var evt = document.createEvent('HTMLEvents');";
+                jsCallback += "evt.initEvent( 'orientationchange', true, false );";
+                jsCallback += "window.dispatchEvent(evt);";
+                jsCallback += "if(window.onorientationchange){window.onorientationchange(evt);}";
+
+                gapBrowser.InvokeScript("execScript", jsCallback);
+            }
+            catch (Exception)
+            {
+            }
+        }
+
+        public void HandleCommand(string commandStr)
+        {
+
+        }
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/blob/4789ce6d/framework/Cordova/PhoneGapCommandCall.cs
----------------------------------------------------------------------
diff --git a/framework/Cordova/PhoneGapCommandCall.cs b/framework/Cordova/PhoneGapCommandCall.cs
new file mode 100644
index 0000000..8692472
--- /dev/null
+++ b/framework/Cordova/PhoneGapCommandCall.cs
@@ -0,0 +1,86 @@
+/*  
+	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.Windows.Shapes;
+using System.Linq;
+
+namespace WP7GapClassLib.Cordova
+{
+    /// <summary>
+    /// Represents Cordova native command call: action callback, etc
+    /// </summary>
+    public class CordovaCommandCall
+    {
+        public String Service {get; private set;}
+        public String Action {get; private set;}
+        public String CallbackId {get; private set;}
+        public String Args {get; private set;}
+        
+        /// <summary>
+        /// Retrieves command call parameters and creates wrapper for them
+        /// </summary>
+        /// <param name="commandStr">Command string in the form 'service/action/callback/args'</param>
+        /// <returns>New class instance or null of string does not represent Cordova command</returns>
+        public static CordovaCommandCall Parse(string commandStr)
+        {
+            if (string.IsNullOrEmpty(commandStr))
+            {
+                return null;
+                //throw new ArgumentNullException("commandStr");
+            }
+
+            string[] split = commandStr.Split('/');
+            if (split.Length < 3)
+            {
+                return null;
+            }
+
+            CordovaCommandCall commandCallParameters = new CordovaCommandCall();
+
+            commandCallParameters.Service = split[0];
+            commandCallParameters.Action = split[1];
+            commandCallParameters.CallbackId = split[2];
+            commandCallParameters.Args = split.Length <= 3 ? String.Empty : String.Join("/", split.Skip(3));
+
+            // sanity check for illegal names
+            // was failing with ::
+            // CordovaCommandResult :: 1, Device1, {"status":1,"message":"{\"name\":\"XD.....
+            if (commandCallParameters.Service.IndexOfAny(new char[] { '@', ':', ',', '!', ' ' }) > -1)
+            {
+                return null;
+            }
+
+
+            return commandCallParameters;
+        }
+
+
+        /// <summary>
+        /// Private ctr to disable class creation.
+        /// New class instance must be initialized via CordovaCommandCall.Parse static method.
+        /// </summary>
+        private CordovaCommandCall() { }
+            
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/blob/4789ce6d/framework/Cordova/PluginResult.cs
----------------------------------------------------------------------
diff --git a/framework/Cordova/PluginResult.cs b/framework/Cordova/PluginResult.cs
new file mode 100644
index 0000000..cb1ea92
--- /dev/null
+++ b/framework/Cordova/PluginResult.cs
@@ -0,0 +1,161 @@
+/*  
+	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.Windows.Shapes;
+using System.Text;
+using System.Diagnostics;
+
+namespace WP7GapClassLib.Cordova
+{
+	/// <summary>
+	/// Represents command execution result
+	/// </summary>
+	public class PluginResult : EventArgs
+	{
+		/// <summary>
+		/// Predefined resultant messages
+		/// </summary>
+		public static string[] StatusMessages = new string[] 
+		{
+			"No result",
+			"OK",
+			"Class not found",
+			"Illegal access",
+			"Instantiation error",
+			"Malformed url",
+			"IO error",
+			"Invalid action",
+			"JSON error",
+			"Error"
+		};
+
+		/// <summary>
+		/// Possible command results status codes
+		/// </summary>
+		public enum Status :int
+		{
+			NO_RESULT = 0,
+			OK,
+			CLASS_NOT_FOUND_EXCEPTION,
+			ILLEGAL_ACCESS_EXCEPTION,
+			INSTANTIATION_EXCEPTION,
+			MALFORMED_URL_EXCEPTION,
+			IO_EXCEPTION,
+			INVALID_ACTION,
+			JSON_EXCEPTION,
+			ERROR
+		};
+
+		public Status Result {get; private set;}
+		public string Message {get; set;}
+		public String Cast { get; private set; }
+
+		public bool KeepCallback { get; set; }        
+
+		/// <summary>
+		/// Whether command succeded or not
+		/// </summary>
+		public bool IsSuccess
+		{
+			get
+			{
+				return this.Result == Status.OK || this.Result == Status.NO_RESULT;
+			}
+		}
+
+		/// <summary>
+		/// Creates new instance of the PluginResult class.
+		/// </summary>
+		/// <param name="status">Execution result</param>
+		public PluginResult(Status status)
+			: this(status, PluginResult.StatusMessages[(int)status])
+		{
+		}
+
+		/// <summary>
+		/// Creates new instance of the PluginResult class.
+		/// </summary>
+		/// <param name="status">Execution result</param>
+		/// <param name="message">The message</param>
+		public PluginResult(Status status, object message)
+			: this(status, message, null)
+		{
+		}
+
+		/// <summary>
+		/// Creates new instance of the PluginResult class.
+		/// </summary>
+		/// <param name="status">Execution result</param>
+		/// <param name="message">The message</param>
+		/// <param name="cast">The cast parameter</param>
+		public PluginResult(Status status, object message, string cast)
+		{
+			this.Result = status;
+			this.Message = JSON.JsonHelper.Serialize(message);
+			this.Cast = cast;
+		}
+
+		public string ToJSONString()
+		{
+            string res = String.Format("\"status\":{0},\"message\":{1},\"keepCallback\":{2}", 
+                (int)this.Result, 
+                this.Message, 
+                this.KeepCallback.ToString().ToLower() );
+
+            res = "{" + res + "}";
+            return res;
+
+		}
+
+		public string ToCallbackString(string callbackId, string successCallback, string errorCallback)
+		{
+			//return String.Format("{0}('{1}',{2});", successCallback, callbackId, this.ToJSONString());
+
+			if (this.IsSuccess)
+			{
+				StringBuilder buf = new StringBuilder("");
+				if (this.Cast != null)
+				{
+					buf.Append("var temp = " + this.Cast + "(" + this.ToJSONString() + ");\n");
+					buf.Append(String.Format("{0}('{1}',temp);", successCallback, callbackId));
+				}
+				else
+				{
+					buf.Append(String.Format("{0}('{1}',{2});", successCallback, callbackId, this.ToJSONString()));
+				}
+				return buf.ToString();
+			}
+			else
+			{
+				return String.Format("{0}('{1}',{2});", errorCallback, callbackId, this.ToJSONString());
+			}
+		}
+
+		public override String ToString()
+		{
+			return this.ToJSONString();
+		}
+
+	}
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/blob/4789ce6d/framework/Cordova/ScriptCallback.cs
----------------------------------------------------------------------
diff --git a/framework/Cordova/ScriptCallback.cs b/framework/Cordova/ScriptCallback.cs
new file mode 100644
index 0000000..900954a
--- /dev/null
+++ b/framework/Cordova/ScriptCallback.cs
@@ -0,0 +1,68 @@
+/*
+ * Cordova is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ *
+ * Copyright (c) 2005-2011, Nitobi Software Inc.
+ * Copyright (c) 2011, Microsoft Corporation
+ */
+
+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.Windows.Shapes;
+using WP7GapClassLib.Cordova.JSON;
+
+namespace WP7GapClassLib.Cordova
+{
+    /// <summary>
+    /// Represents client script function to execute
+    /// </summary>
+    public class ScriptCallback : EventArgs
+    {
+        /// <summary>
+        /// The scripting function to execute.
+        /// </summary>
+        public string ScriptName { get; private set; }
+
+        /// <summary>
+        /// A variable number of strings to pass to the function as parameters.
+        /// </summary>
+        public string[] Args { get; private set; }
+
+        /// <summary>
+        /// Creates new instance of a ScriptCallback class.
+        /// </summary>
+        /// <param name="function">The scripting function to execute</param>
+        /// <param name="args">A variable number of strings to pass to the function as parameters</param>
+        public ScriptCallback(string function, string[] args)
+        {
+            this.ScriptName = function;
+            this.Args = args;
+        }
+
+        /// <summary>
+        /// Creates new instance of a ScriptCallback class.
+        /// </summary>
+        /// <param name="function">The scripting function to execute</param>
+        /// <param name="id">The id argument</param>
+        /// <param name="msg">The message argument</param>
+        /// <param name="value">The value argument</param>
+        public ScriptCallback(string function, string id, object msg, object value)
+        {
+            this.ScriptName = function;
+
+            String arg = String.Format("{{\"id\": {0}, \"msg\": {1}, \"value\": {2}}}",
+                 JsonHelper.Serialize(id), JsonHelper.Serialize(msg), JsonHelper.Serialize(value));
+
+            this.Args = new string[] { arg };
+        }
+
+        
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/blob/4789ce6d/framework/Cordova/UI/AudioCaptureTask.cs
----------------------------------------------------------------------
diff --git a/framework/Cordova/UI/AudioCaptureTask.cs b/framework/Cordova/UI/AudioCaptureTask.cs
new file mode 100644
index 0000000..dad7f1b
--- /dev/null
+++ b/framework/Cordova/UI/AudioCaptureTask.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 WP7GapClassLib.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 WP7GapClassLib.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);
+
+                // dummy parameter is used to always open a fresh version
+                root.Navigate(new System.Uri("/WP7GapClassLib;component/Cordova/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/incubator-cordova-wp7/blob/4789ce6d/framework/Cordova/UI/AudioRecorder.xaml
----------------------------------------------------------------------
diff --git a/framework/Cordova/UI/AudioRecorder.xaml b/framework/Cordova/UI/AudioRecorder.xaml
new file mode 100644
index 0000000..5f64016
--- /dev/null
+++ b/framework/Cordova/UI/AudioRecorder.xaml
@@ -0,0 +1,48 @@
+<phone:PhoneApplicationPage 
+    x:Class="WP7GapClassLib.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/incubator-cordova-wp7/blob/4789ce6d/framework/Cordova/UI/AudioRecorder.xaml.cs
----------------------------------------------------------------------
diff --git a/framework/Cordova/UI/AudioRecorder.xaml.cs b/framework/Cordova/UI/AudioRecorder.xaml.cs
new file mode 100644
index 0000000..73fe016
--- /dev/null
+++ b/framework/Cordova/UI/AudioRecorder.xaml.cs
@@ -0,0 +1,385 @@
+/*  
+	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;
+using System.Windows.Threading;
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Tasks;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using AudioResult = WP7GapClassLib.Cordova.UI.AudioCaptureTask.AudioResult;
+
+namespace WP7GapClassLib.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);
+
+            this.memoryStream = new MemoryStream();
+            this.WriteWavHeader(this.memoryStream, this.microphone.SampleRate);
+
+            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 (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 (this.IsRecording)
+            {
+                StopRecording();
+            }
+
+            this.FinalizeXnaGameLoop();
+
+            if (this.Completed != null)
+            {
+                this.Completed(this, result);
+            }
+
+            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.UpdateWavHeader(this.memoryStream);
+            
+            // 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)
+            this.dtXna.Stop();
+            this.dtXna = null;
+        }
+
+
+        #region Wav format
+        // Original source http://damianblog.com/2011/02/07/storing-wp7-recorded-audio-as-wav-format-streams/
+
+        /// <summary>
+        /// Adds wav file format header to the stream
+        /// https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
+        /// </summary>
+        /// <param name="stream">Wav stream</param>
+        /// <param name="sampleRate">Sample Rate</param>
+        private void WriteWavHeader(Stream stream, int sampleRate)
+        {
+            const int bitsPerSample = 16;
+            const int bytesPerSample = bitsPerSample / 8;
+            var encoding = System.Text.Encoding.UTF8;
+
+            // ChunkID Contains the letters "RIFF" in ASCII form (0x52494646 big-endian form).
+            stream.Write(encoding.GetBytes("RIFF"), 0, 4);
+
+            // NOTE this will be filled in later
+            stream.Write(BitConverter.GetBytes(0), 0, 4);
+
+            // Format Contains the letters "WAVE"(0x57415645 big-endian form).
+            stream.Write(encoding.GetBytes("WAVE"), 0, 4);
+
+            // Subchunk1ID Contains the letters "fmt " (0x666d7420 big-endian form).
+            stream.Write(encoding.GetBytes("fmt "), 0, 4);
+
+            // Subchunk1Size 16 for PCM.  This is the size of therest of the Subchunk which follows this number.
+            stream.Write(BitConverter.GetBytes(16), 0, 4);
+
+            // AudioFormat PCM = 1 (i.e. Linear quantization) Values other than 1 indicate some form of compression.
+            stream.Write(BitConverter.GetBytes((short)1), 0, 2);
+
+            // NumChannels Mono = 1, Stereo = 2, etc.
+            stream.Write(BitConverter.GetBytes((short)1), 0, 2);
+
+            // SampleRate 8000, 44100, etc.
+            stream.Write(BitConverter.GetBytes(sampleRate), 0, 4);
+
+            // ByteRate =  SampleRate * NumChannels * BitsPerSample/8
+            stream.Write(BitConverter.GetBytes(sampleRate * bytesPerSample), 0, 4);
+
+            // BlockAlign NumChannels * BitsPerSample/8 The number of bytes for one sample including all channels.
+            stream.Write(BitConverter.GetBytes((short)(bytesPerSample)), 0, 2);
+
+            // BitsPerSample    8 bits = 8, 16 bits = 16, etc.
+            stream.Write(BitConverter.GetBytes((short)(bitsPerSample)), 0, 2);
+
+            // Subchunk2ID Contains the letters "data" (0x64617461 big-endian form).
+            stream.Write(encoding.GetBytes("data"), 0, 4);
+
+            // NOTE to be filled in later
+            stream.Write(BitConverter.GetBytes(0), 0, 4);
+        }
+
+        /// <summary>
+        /// Updates wav file format header
+        /// https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
+        /// </summary>
+        /// <param name="stream">Wav stream</param>
+        private void UpdateWavHeader(Stream stream)
+        {
+            if (!stream.CanSeek) throw new Exception("Can't seek stream to update wav header");
+
+            var oldPos = stream.Position;
+
+            // ChunkSize  36 + SubChunk2Size
+            stream.Seek(4, SeekOrigin.Begin);
+            stream.Write(BitConverter.GetBytes((int)stream.Length - 8), 0, 4);
+
+            // Subchunk2Size == NumSamples * NumChannels * BitsPerSample/8 This is the number of bytes in the data.
+            stream.Seek(40, SeekOrigin.Begin);
+            stream.Write(BitConverter.GetBytes((int)stream.Length - 44), 0, 4);
+
+            stream.Seek(oldPos, SeekOrigin.Begin);
+        }
+
+        #endregion
+
+
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/blob/4789ce6d/framework/Cordova/UI/VideoCaptureTask.cs
----------------------------------------------------------------------
diff --git a/framework/Cordova/UI/VideoCaptureTask.cs b/framework/Cordova/UI/VideoCaptureTask.cs
new file mode 100644
index 0000000..a954a2a
--- /dev/null
+++ b/framework/Cordova/UI/VideoCaptureTask.cs
@@ -0,0 +1,104 @@
+/*  
+	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 WP7GapClassLib.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 WP7GapClassLib.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);
+
+                // dummy parameter is used to always open a fresh version
+                root.Navigate(new System.Uri("/WP7GapClassLib;component/Cordova/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/incubator-cordova-wp7/blob/4789ce6d/framework/Cordova/UI/VideoRecorder.xaml
----------------------------------------------------------------------
diff --git a/framework/Cordova/UI/VideoRecorder.xaml b/framework/Cordova/UI/VideoRecorder.xaml
new file mode 100644
index 0000000..24e0078
--- /dev/null
+++ b/framework/Cordova/UI/VideoRecorder.xaml
@@ -0,0 +1,34 @@
+<phone:PhoneApplicationPage 
+    x:Class="WP7GapClassLib.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>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/blob/4789ce6d/framework/Cordova/UI/VideoRecorder.xaml.cs
----------------------------------------------------------------------
diff --git a/framework/Cordova/UI/VideoRecorder.xaml.cs b/framework/Cordova/UI/VideoRecorder.xaml.cs
new file mode 100644
index 0000000..96961d5
--- /dev/null
+++ b/framework/Cordova/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 = WP7GapClassLib.Cordova.UI.VideoCaptureTask.VideoResult;
+
+namespace WP7GapClassLib.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;
+            }
+        }
+
+    }
+}
\ No newline at end of file