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