Monday, December 24, 2012

Notification Notch Control for Windows Phone

Many of you would have wondered why there is no Notification Notch in +Windows Phone, similar to the one seen first in Android and then later in iOS5 as well. However, there might be some restriction/limitation for them (WP team) to implement Notification Notch at this point of time but this shouldn't stop you from implementing it in your application. In today's article I am going to explain how to create a Notification Notch and how to use it in your application.

Well, our objective will be completed in 3 steps:

1. Create a custom behavior i.e. "Drag Notch Behavior" to mimic drag and expand/close nature of a notification notch.
2. Create a sample user control i.e. "Drag Notch Control" to use this behavior.
3. Create a sample project to use this control.

PREREQUISITE:  

1. Knowledge of Behavior in .Net

BUILDING HELPER CLASSES:

There are a few extension methods that are required to easily find and manipulate a FrameworkElemant from code. For doing so I've shamelessly pinched the code from "Joost van Schaik"  code article - "Simple Windows Phone 7 / Silverlight drag/flick behavior". These Libraries can be found under utilities in my sample code attached.








 










DRAG NOTCH BEHAVIOR:

Code for DragNotch Behavior is implemented by following class:
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows.Media;
using System.Windows.Media.Animation;
using nishantcop_Behaviors.Utilities;

namespace nishantcop_Behaviors.Behaviors
{
    public class DragNotchBehavior : Behavior
    {
        private FrameworkElement _elementToAnimate;
        public event NotchStateChangedHandler NotchStateChanged;

        public delegate void NotchStateChangedHandler(object sender, NotchStateChangedEventArgs e);

        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.Loaded += AssociatedObjectLoaded;
            AssociatedObject.ManipulationDelta += AssociatedObjectManipulationDelta;
            AssociatedObject.ManipulationCompleted += AssociatedObjectManipulationCompleted;

        }

        void AssociatedObjectLoaded(object sender, RoutedEventArgs e)
        {
            _elementToAnimate = AssociatedObject.GetElementToAnimate();
            if (!(_elementToAnimate.RenderTransform is CompositeTransform))
            {
                _elementToAnimate.RenderTransform = new CompositeTransform();
                _elementToAnimate.RenderTransformOrigin = new Point(0.5, 0.5);
            }
            StartPosition = AssociatedObject.GetTranslatePoint();
        }

        void AssociatedObjectManipulationDelta(object sender, ManipulationDeltaEventArgs e)
        {
            //var dx = e.DeltaManipulation.Translation.X;
            var dy = e.DeltaManipulation.Translation.Y;
            var currentPosition = _elementToAnimate.GetTranslatePoint();
            _elementToAnimate.SetTranslatePoint(currentPosition.X, currentPosition.Y + dy);
        }

        private void AssociatedObjectManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
        {
            // Create a storyboard that will emulate a 'flick'
            var currentPosition = _elementToAnimate.GetTranslatePoint();
            var velocity = e.FinalVelocities.LinearVelocity;
            if (velocity.Y > 0)
            {
                //var to = new Point(currentPosition.X, 360 + (velocity.Y / BrakeSpeed));
            }

            var storyboard = new Storyboard { FillBehavior = FillBehavior.HoldEnd };

            var to = new Point(currentPosition.X + (velocity.X / BrakeSpeed),
                currentPosition.Y + (velocity.Y / BrakeSpeed));
            storyboard.AddTranslationAnimation(_elementToAnimate, currentPosition, GetEndPoint(),
                new Duration(TimeSpan.FromMilliseconds(500)),
                new CubicEase { EasingMode = EasingMode.EaseOut });
            storyboard.Begin();
        }

        protected override void OnDetaching()
        {
            AssociatedObject.Loaded -= AssociatedObjectLoaded;
            AssociatedObject.ManipulationCompleted -= AssociatedObjectManipulationCompleted;
            AssociatedObject.ManipulationDelta -= AssociatedObjectManipulationDelta;
            base.OnDetaching();
        }

        private Point GetEndPoint()
        {
            double midPoint = Math.Abs(StartPosition.Y) - Math.Abs(_elementToAnimate.GetTranslatePoint().Y);
            if (midPoint < Math.Abs(StartPosition.Y) / 2)
            {
                
                NotchStateChanged(this, new NotchStateChangedEventArgs(false));
                return StartPosition;
            }
            else
            {
                IsNotchExpanded = true;
                NotchStateChanged(this, new NotchStateChangedEventArgs(true));
                return EndPosition;
            }
        }

        /// 
        /// This function tries to collapse a notch if it's expanded.
        /// 
        public void TryRollBackNotch()
        {
            var currentPosition = _elementToAnimate.GetTranslatePoint();
            if (currentPosition != StartPosition)
            {
                var storyboard = new Storyboard { FillBehavior = FillBehavior.HoldEnd };

                storyboard.AddTranslationAnimation(_elementToAnimate, currentPosition, StartPosition,
                    new Duration(TimeSpan.FromMilliseconds(500)),
                    new CubicEase { EasingMode = EasingMode.EaseOut });
                storyboard.Begin();
                storyboard.Completed += new EventHandler(storyboard_Completed);
            }
        }

        void storyboard_Completed(object sender, EventArgs e)
        {
            var storyboard = sender as Storyboard;
            storyboard.Completed -= storyboard_Completed;
            IsNotchExpanded = false;
            NotchStateChanged(this, new NotchStateChangedEventArgs(false));
        }

        #region IsNotchExpandedProperty
        public bool IsNotchExpanded
        {
            get { return (bool)GetValue(IsNotchExpandedProperty); }
            set { SetValue(IsNotchExpandedProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsNotchExpanded.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsNotchExpandedProperty =
            DependencyProperty.Register("IsNotchExpanded", typeof(bool), typeof(DragNotchBehavior), new PropertyMetadata(false, OnIsNotchExpandedPropertyChanged));

        private static void OnIsNotchExpandedPropertyChanged(DependencyObject source,
        DependencyPropertyChangedEventArgs e)
        {
            DragNotchBehavior behavior = source as DragNotchBehavior;
            bool time = (bool)e.NewValue;
            // Put some update logic here...
        } 
        #endregion

        #region BrakeSpeed
        public const string BrakeSpeedPropertyName = "BrakeSpeed";

        /// 
        /// Describes how fast the element should brake, i.e. come to rest,
        /// after a flick. Higher = apply more brake ;-)
        /// 
        public int BrakeSpeed
        {
            get { return (int)GetValue(BrakeSpeedProperty); }
            set { SetValue(BrakeSpeedProperty, value); }
        }

        public static readonly DependencyProperty BrakeSpeedProperty = DependencyProperty.Register(
            BrakeSpeedPropertyName,
            typeof(int),
            typeof(DragNotchBehavior),
            new PropertyMetadata(10));

        #endregion

        #region StartPosition
        public Point StartPosition
        {
            get { return (Point)GetValue(StartPositionProperty); }
            set { SetValue(StartPositionProperty, value); }
        }

        // Using a DependencyProperty as the backing store for StartPosition.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty StartPositionProperty =
            DependencyProperty.Register("StartPosition", typeof(Point), typeof(DragNotchBehavior), new PropertyMetadata(new Point(0, 0)));
        #endregion

        #region End Position
        public Point EndPosition
        {
            get { return (Point)GetValue(EndPositionProperty); }
            set { SetValue(EndPositionProperty, value); }
        }

        // Using a DependencyProperty as the backing store for EndPosition.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty EndPositionProperty =
            DependencyProperty.Register("EndPosition", typeof(Point), typeof(DragNotchBehavior), new PropertyMetadata(new Point(0, 0))); 
        #endregion
        
    }
}

This behavior exposes following important properties:
  1. StartPosition: Element starts it's animation at this point. 
  2. EndPostion: Element ends it's animation at this point.
  3. BrakeSpeed: Speed by which animation should come to halt. 
  4. TryRollBackNotch:  A Function to force close/Rollback your Notification Notch control. 
  5. NotchStateChanged: An Event to notify the change in status of Notification Notch control i.e. closed or expanded.  
If you wish to fiddle around with it you can change the behavior to move horizontally only, contrasted to it's default vertical movement.

For adding a NotchStateChanged Event you will need to provide an customized EventArgs class. Code for which will look like this: 
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;

namespace nishantcop_Behaviors.Utilities
{
    public class NotchStateChangedEventArgs : EventArgs
    {
        private bool _IsNotchExpanded;

        public NotchStateChangedEventArgs(bool isNotchExpanded)
        {
            this._IsNotchExpanded = isNotchExpanded;
            //this.exceededPercentage = exceededPercentage;
            // not shown - validation for input
        }

        public bool IsNotchExpanded
        {
            get { return this._IsNotchExpanded; }
        }
    }
}

This class provides current state for your Notch Control. We handle this class in our Drag Notch Behavior whenever we are animating our control for expanding and closing it. 

So, that's about the Drag Notch Behavior now lets take a look into how exactly to use it in your project. 

DRAG NOTCH CONTROL

Now we need a DragNotch control that can be used across our application. A sample xaml for DragNotch control is: 


 
  
            
        
  
   
  
  
  
 
This control simply contains a Grid and our DragNotchBehavior is attached to it. For this custom behavior we need to set it's x:fieldModifier = "public" so that we can access it from code behind. Also we need to set the TranslateY property of CompositeTransform to a suitable position so that it won't be visible until it's being dragged. For this demo I've set this value to -700. However it can be easily tweaked as per your requirement.

Code behind for your DragNotch control:

using System;
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;

namespace WPTestBehavior
{
 public partial class DragNotch : UserControl
 {
  public DragNotch()
  {
   // Required to initialize variables
   InitializeComponent();
  }

        private void Notch_NotchStateChanged(object sender, nishantcop_Behaviors.Utilities.NotchStateChangedEventArgs e)
        {
            bool b = e.IsNotchExpanded;
        }
        public void RollBackNotch()
        {
            Notch.TryRollBackNotch();
        }
 }
}

In code behind we are handling the NotchStateChanged event from our behavior and a public method (RollBackNotch) is exposed so that you can force your control to collapsed.

Now, your control is ready you can fill in whatever you want inside this grid, for your notification purpose or whatever you may want to call it :-)

USE DRAG NOTCH CONTROL

For using the DragNotch control in last step I've created this sample page:



    
    
        
            
            
        

        
        

        
        
         
         
         
        
    
This page contains ListBox filled with some sample data and our "TestDragNotch" control to overlay it when it's expanded to demonstrate the Notification Notch behavior.

VIDEO

Sunday, December 2, 2012

Mashape API - How to Consume in WP

Today I'll talk about Mashape APIs and their integration into WP project.  Before getting into more details I'll like to answer few FAQs.

Q1 : What is Mashape?
Ans: Mashape is API cloud based API hub. You can used hosted API there and can quickly develop an application.

Q2 : How can I use that in WP Application?
Ans: You can consume their API(s) using REST services in WP. (Soon, auto generated client libraries will also be supported for WP code).

Prerequisite:

1. Json Client Library: This library will be helpful to consume Json response from Mashape's AP. Download the source code and compile it for the WP version you are going to use and then add in your project and use.  

How to consume REST API in Windows Phone

For my sample app I am consuming  QR code generator API.

1. Create a WebClient Request:

WebClient client = new WebClient();

2. Set Content Type in request's Header:

client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";


3. Add Authorization Header: An authorization code is required to authenticate each request, this code can be easily generated by a tool provided at this link for all registered Mashape users





client.Headers["X-Mashape-Authorization"] = "YourAutorizationHeaderNeedToGenerateFromTool";


4. Add Data for Web Request:

StringBuilder postData = new StringBuilder();
postData.AppendFormat("{0}={1}", "content", HttpUtility.UrlEncode("TestQRCode"));
postData.AppendFormat("&{0}={1}", "quality", HttpUtility.UrlEncode("L"));
postData.AppendFormat("&{0}={1}", "type", HttpUtility.UrlEncode("text"));
postData.AppendFormat("&{0}={1}", "size", HttpUtility.UrlEncode("5"));


5. Create Mashape API's URI: This URI responsible to call a specific API listed in Mashape cloud listing. You can get the URL from Mashape site as shown here


var uri = new Uri("https://mutationevent-qr-code-generator.p.mashape.com/generate.php?", UriKind.Absolute);


6. Register a Asyn Call Completed Event:

client.UploadStringCompleted += new UploadStringCompletedEventHandler(client_UploadStringCompleted)


7. Make Async http Call:

client.UploadStringAsync(uri, "POST", postData.ToString());


8. Consume Response in completed event:

void client_UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
{
    var client = sender as WebClient;
    client.UploadStringCompleted -= client_UploadStringCompleted;
    string response = string.Empty;
    if (!e.Cancelled)
    {
        response = HttpUtility.UrlDecode(e.Result);
    }
}

You are done!!!

A full working sample can be found at the links given below.

Source Code: WP7, WP8