Tuesday, January 29, 2013

Cloud Print in MonoDroid or MonoTouch.

Printed from Google Cloud Print via Mono Droid.
I spent quite a bit of time.. trying to print in Mono for Android via Google Cloud Print, I needed it for a piece or three I've been working on.

Stack Overflow Google Cloud Print

This link, basically got me through everything, and here is the code, tweaked a bit to remove the Proxy issue.  If you need the Proxy code, you can go pull from the original post.

Update :

Git Hub Repo with Google Cloud Print for Monodroid.

[You should easily be able to modify this to work for MonoTouch also.  I submitted the project to Xamarin, and they said they would have the Documentation team add an example]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using System.Web;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.IO;
using System.ServiceModel.Web;

namespace GoogleCloudPrint
{
    public class GoogleCloudPrint
    {
        public string UserName { get; set; }
        public string Password { get; set; }
        public string Source { get; set; }
        
        private const int ServiceTimeout = 10000;
        

        
        public CloudPrintJob PrintDocument (string printerId, string title, byte[] document, String mimeType)
        {
            try
            {
                string authCode;
                if (!Authorize (out authCode))
                return new CloudPrintJob { success = false };
                
                var b64 = Convert.ToBase64String (document);
                
                var request = (HttpWebRequest)WebRequest.Create ("http://www.google.com/cloudprint/submit?output=json&printerid=" + printerId);
                request.Method = "POST";
                
                // Setup the web request
                request.ServicePoint.Expect100Continue = false;
                
                // Add the headers
                request.Headers.Add ("X-CloudPrint-Proxy", Source);
                request.Headers.Add ("Authorization", "GoogleLogin auth=" + authCode);
                
                var p = new PostData ();
                
                p.Params.Add (new PostDataParam { Name = "printerid", Value = printerId, Type = PostDataParamType.Field });
                p.Params.Add (new PostDataParam { Name = "capabilities", Value = "{\"capabilities\":[{}]}", Type = PostDataParamType.Field });
                p.Params.Add (new PostDataParam { Name = "contentType", Value = "dataUrl", Type = PostDataParamType.Field });
                p.Params.Add (new PostDataParam { Name = "title", Value = title, Type = PostDataParamType.Field });
                
                p.Params.Add (new PostDataParam
                              {
                    Name = "content",
                    Type = PostDataParamType.Field,
                    Value = "data:" + mimeType + ";base64," + b64
                });
                
                var postData = p.GetPostData ();

                
                byte[] data = Encoding.UTF8.GetBytes (postData);
                
                request.ContentType = "multipart/form-data; boundary=" + p.Boundary;
                
                Stream stream = request.GetRequestStream ();
                stream.Write (data, 0, data.Length);
                stream.Close ();
                
                // Get response
                var response = (HttpWebResponse)request.GetResponse ();
                var responseContent = new StreamReader (response.GetResponseStream ()).ReadToEnd ();
                
                var serializer = new DataContractJsonSerializer (typeof (CloudPrintJob));
                var ms = new MemoryStream (Encoding.Unicode.GetBytes (responseContent));
                var printJob = serializer.ReadObject (ms) as CloudPrintJob;
                
                return printJob;
            }
            catch (Exception ex)
            {
                return new CloudPrintJob { success = false, message = ex.Message };
            }
        }
        
        public CloudPrinters Printers
        {
            get
            {
                var printers = new CloudPrinters ();
                
                string authCode;
                if (!Authorize (out authCode))
                return new CloudPrinters { success = false };
                
                try
                {
                    var request = (HttpWebRequest)WebRequest.Create ("http://www.google.com/cloudprint/search?output=json");
                    request.Method = "POST";
                    
                    // Setup the web request
                    request.ServicePoint.Expect100Continue = false;
                    
                    // Add the headers
                    request.Headers.Add ("X-CloudPrint-Proxy", Source);
                    request.Headers.Add ("Authorization", "GoogleLogin auth=" + authCode);
                    
                    request.ContentType = "application/x-www-form-urlencoded";
                    request.ContentLength = 0;
                    
                    var response = (HttpWebResponse)request.GetResponse ();
                    var responseContent = new StreamReader (response.GetResponseStream ()).ReadToEnd ();
                    
                    var serializer = new DataContractJsonSerializer (typeof (CloudPrinters));
                    var ms = new MemoryStream (Encoding.Unicode.GetBytes (responseContent));
                    printers = serializer.ReadObject (ms) as CloudPrinters;
                    
                    return printers;
                }
                catch (Exception)
                {
                    return printers;
                }
            }
        }
        
        private bool Authorize (out string authCode)
        {
            var result = false;
            authCode = "";
            
            var queryString = String.Format ("https://www.google.com/accounts/ClientLogin?accountType=HOSTED_OR_GOOGLE&Email={0}&Passwd={1}&service=cloudprint&source={2}",
                                             UserName, Password, Source);
            var request = (HttpWebRequest)WebRequest.Create (queryString);
            
            request.ServicePoint.Expect100Continue = false;
            
            var response = (HttpWebResponse)request.GetResponse ();
            var responseContent = new StreamReader (response.GetResponseStream ()).ReadToEnd ();
            
            var split = responseContent.Split ('\n');
            foreach (var s in split)
            {
                var nvsplit = s.Split ('=');
                if (nvsplit.Length == 2)
                {
                    if (nvsplit[0] == "Auth")
                    {
                        authCode = nvsplit[1];
                        result = true;
                    }
                }
            }
            
            return result;
        }

    }

    [DataContract]
    public class CloudPrinter
    {
        [DataMember (Order = 0)]
        public string id { get; set; }
        
        [DataMember (Order = 1)]
        public string name { get; set; }
        
        [DataMember (Order = 2)]
        public string description { get; set; }
        
        [DataMember (Order = 3)]
        public string proxy { get; set; }
        
        [DataMember (Order = 4)]
        public string status { get; set; }
        
        [DataMember (Order = 5)]
        public string capsHash { get; set; }
        
        [DataMember (Order = 6)]
        public string createTime { get; set; }
        
        [DataMember (Order = 7)]
        public string updateTime { get; set; }
        
        [DataMember (Order = 8)]
        public string accessTime { get; set; }
        
        [DataMember (Order = 9)]
        public bool confirmed { get; set; }
        
        [DataMember (Order = 10)]
        public int numberOfDocuments { get; set; }
        
        [DataMember (Order = 11)]
        public int numberOfPages { get; set; }
    }

    [DataContract]
    public class CloudPrinters
    {
        [DataMember (Order = 0)]
        public bool success { get; set; }
        
        [DataMember (Order = 1)]
        public List<CloudPrinter> printers { get; set; }
    }



    [DataContract]
    public class CloudPrintJob
    {
        [DataMember (Order = 0)]
        public bool success { get; set; }
        
        [DataMember (Order = 1)]
        public string message { get; set; }
    }



    internal class PostData
    {
        private const String CRLF = "\r\n";
        
        public string Boundary { get; set; }
        private List<PostDataParam> _mParams;
        
        public List<PostDataParam> Params
        {
            get { return _mParams; }
            set { _mParams = value; }
        }
        
        public PostData ()
        {
            // Get boundary, default is --AaB03x
            Boundary = "----CloudPrintFormBoundary" + DateTime.UtcNow;
            
            // The set of parameters
            _mParams = new List<PostDataParam> ();
        }
        
        public string GetPostData ()
        {
            var sb = new StringBuilder ();
            foreach (var p in _mParams)
            {
                sb.Append ("--" + Boundary).Append (CRLF);
                
                if (p.Type == PostDataParamType.File)
                {
                    sb.Append (string.Format ("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"", p.Name, p.FileName)).Append (CRLF);
                    sb.Append ("Content-Type: ").Append (p.FileMimeType).Append (CRLF);
                    sb.Append ("Content-Transfer-Encoding: base64").Append (CRLF);
                    sb.Append ("").Append (CRLF);
                    sb.Append (p.Value).Append (CRLF);
                }
                else
                {
                    sb.Append (string.Format ("Content-Disposition: form-data; name=\"{0}\"", p.Name)).Append (CRLF);
                    sb.Append ("").Append (CRLF);
                    sb.Append (p.Value).Append (CRLF);
                }
            }
            
            sb.Append ("--" + Boundary + "--").Append (CRLF);
            
            return sb.ToString ();
        }
    }
    
    public enum PostDataParamType
    {
        Field,
        File
    }
    
    public class PostDataParam
    {
        public string Name { get; set; }
        public string FileName { get; set; }
        public string FileMimeType { get; set; }
        public string Value { get; set; }
        public PostDataParamType Type { get; set; }
        
        public PostDataParam ()
        {
            FileMimeType = "text/plain";
        }
    }
}

Monday, January 28, 2013

OData And Entity Framework.

OData is probably one of the most powerful tools I've found.  It supports Interceptions, which means you can have authentication on any methods you need / want to lock down.

Most of Mobile development is making web service methods that allow you to Update / Create and Delete your Entities.  With OData.. most of that work can be quickly handled.

Frankly, getting it into use takes about 10 minutes, just to start working with it.  The needs of your project will determine how much additional work you need to do (Securing it, if it is a publicly exposed service, etc)  If you additionally install Entity Framework Power Tools, you get a whole lot of bang for your buck with an existing Database, with Code First.

I want to show you some urls, you can use, to see how powerful this tool is.

http://services.odata.org/website/odata.svc/ODataCustomers?$format=json
http://services.odata.org/OData/OData.svc/Products?$format=atom

Basically, on the fly, it will let me convert the Entity Objects, to Json format.. Atom, and XML (I believe these are the main formats that can be returned)

A great reference for using OData : http://www.odata.org/documentation/uri-conventions

If you go down this route, you might as well get a copy of LinqPad, it's a life saver.

LinqPad - The only way to fly with data and Linq.

Useful for getting the ODataService (Which allows $format=json in your OData Queries) if you are using an ASP.net Stack.

WCF Data Services ToolKit (Nuget)


A link for how to secure your OData Service with Custom Authentication.

Custom Authentication for OData.


LinqPad display of an OData Data Source.

Tuesday, January 22, 2013

Entity Framework.

Before I get going on heavy coding for the day, I wanted to post some comments on a debacle I had a bit ago.  I kept the link, and it was a really annoying situation.

I was wanting Code First Entity Framework to be implemented in my project, and for a bit, I worked on it manually.  Then I found some Nuget packages for Entity Framework that reverse engineer your dbcontext from an existing database.  I was in Visual Studio 2012, and I couldn't get the darn thing to work.  Kept telling me that it couldn't find T4VSHost, so I went on a google trip.   Found this link and the workaround.

Entity Framework Error.

I went to this location :

C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\Extensions\Microsoft\Entity Framework Tools\Templates\Includes\EF.Utility.CS.ttinclude (for C#)

Deleted this line.

<#@ CleanupBehavior Processor="T4VSHost" CleanupAfterProcessingTemplate="true" #>

That was pretty much the end of my EntityFramework problems for what I was trying to get to happen.  Since then, I've been using AutoMapper, and the custom converters, or just selecting into new types with my lambda's.

AutoMapper Converters

Entity Framework allows a lot more flexibility than Linq to SQL, but it comes with additional overhead, and more "rules".   I love Code First, gives me a lot more control, without losing any of my customization.

Monday, January 21, 2013

User Management System.



Example JSON response, for the management system for a "ListUser" request.


It's just an idea.  That's all.  Maybe it will have a bigger use.  Have some plans for it.  Maybe they'll work, maybe not.
Maybe I'll have to buy some hardware and deploy it to places for free to get it going.  Doesn't matter.  I'm going to try it out.  

Sunday, January 20, 2013

Maybe I get ahead of myself. A little Background.

With the barcode integration mentioned in previous posts.. what we were able to do is actually pretty cool.  Honestly though, it wouldn't have worked without Youth Impact committing $3k for an ID card system.  I didn't implement the tech I wanted to, I'd have preferred NFC tech.  It's secure, and the login functionality could have all been handled better, but the problem is that there just aren't enough devices that are low cost, that would support that pathway; considering our budget constraints.

One of the ID cards used at Youth Impact.

At first, I was reliant on this project for my barcodes:   GenCode 128


The program ran with that for a while.  Larry at the front desk had a book of barcodes with pictures and names of the participants and he clocked them in and out.  He had to maintain that book, those pictures and those names and those barcodes.

At a certain point, I integrated Telerik Reporting, and their barcode reporting object, so that they could just export to Excel.  That was $400, that was hard for me to spend, and I guess most amounts of money I spend are hard to spend.  When I pay for things.. they hit me personally.  What I'm able to get for myself or my children, and use to learn.   Being a nerd is expensive.  My wage directly impacts what I can purchase to grow in my career.  A nerd without toys is a nerd that isn't growing.  People think that those toys I have, are for browsing or for my own enjoyment, and I won't lie, I do enjoy them, but without those devices, those toys, I can't learn how to conquer them.  Without software packages.. I have to commit 3-4 times the amount of time I already spend.. which slows development progress to a crawl.  I tried to do native Android.. it wasn't going to work.  Without lambda's and partial classes, I'm half the man I could be.  I had to go .net.  I don't have a vast Development team. It's me, and a guy I know, that I value, and now a younger staff member, who is at Weber State for a comp sci degree.  

Back to the topic at hand though, the great thing about this ID card system, is that now, the kids can directly interact with the systems.. so it isn't just some system behind a computer screen.  With the Android kiosks out there, the possibility is raised that the kids can actually go interact with the systems without a staff member.  

There are so many applications to build to fill all the needs I see.  

There are a lot of donors for Youth Impact.  That contribute at various levels, and in different ways.  I respect their contributions.  I don't have money like they do, I can't help them with cash, but the systems I build might one day be more important than any amount of money I could give.   I guess I'll finish this post off with that.

Saturday, January 19, 2013

Login for Mono Touch.


So folks.  Looks like making good code still has some worth.  Ipad / Iphone app, with Login functionality now.  This code is placed in the AppDelegate.cs in Mono Touch for the interface on app start.


            RootElement re = new RootElement("Login");
            Section creds = new Section("Credentials");
            Section welcome = new Section("Welcome to Youth Impact!");
            UIImageView ie;
            if(UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone) ie = new UIImageView(UIImage.FromBundle("resized.png"));
            else ie = new UIImageView(UIImage.FromBundle("small2.png"));
            welcome.Add(ie);
            login = new EntryElement("Login","Enter your Email", "");
            password = new EntryElement("Password","","",true);

            creds.Add(login);
            creds.Add(password);
            Section button = new Section();
            button.Add(new StringElement("Sign In",delegate { Validate();}));

            re.Add(welcome);
            re.Add(creds);
            re.Add(button);

            window = new UIWindow (UIScreen.MainScreen.Bounds);
            DialogViewController dialog = new DialogViewController(re);


            window.RootViewController = new DialogViewController(re);

I only put in a bit of logic for swapping the image out for IPhone / Ipad.  There is some code to do for when the app shifts Orientation.  As your properly sized image needs to be replaced with an image with the proper width / height due to the orientation change.

Using that if(UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone), should provide you the ability to handle that.. and maybe I'll just update this post later.

In the Validate method, I check the two Entries, one for password, and one for Login.. and use my web service call to validate the user.. if the response comes back as valid credentials, then I load the default ViewController that was generated when you create a "Universal" iphone / ipad app in Mono Touch.

Most of this code was taken as ill-gotten gains from this great recipe on Xamarin:

Xamarin Login Window Recipe.


Friday, January 18, 2013

Monotouch Development.


Yeah, if you've been here because of my links, you'll notice that the title of the blog changed.

Mobile Development.  Started working a bit in Mono Touch.

The great thing about working in Xamarin, is that you can re-use almost all your data access, if you started in Android.

I imported my model classes.  I imported my service call methods.



Made a nice little MasterViewController, and a DetailView where I could render custom data from the data made in my calls. Sorry, I had to blur the names.. because those kids don't deserve to have some stupid developer expose the fact that they are in a program at an address that can be found pretty easily.

Creating this app, took about 30 minutes.   (I had to read a little).  I can actually say nowadays, that my new personality is stronger / faster / happier, than I can easily explain and I'm going to take advantage of that.





Monday, January 14, 2013

Preferences in Monodroid.

Jared, a coder that's helping me out sometimes, went and found this gem.

Been trying to get him to post on here, but instead I'll post his code that he dug up for me.

Had to tweak what he put in, to work a bit cleaner in the app, but it was great research done,
and I really needed it for a deployment about a week ago.

            ISharedPreferences prefs = GetPreferences (FileCreationMode.Append);
            ISharedPreferencesEditor editor = prefs.Edit ();
            editor.PutString ("LoginName", creds);
            editor.Commit ();

This code will let you pretty much append anything you need via code into your preferences.

You just need to put in your string for the preference name.. and the value.  You can, of course, change the type a bit there in that editor variable.

User Management for Mobile Worlds.

Simple App Management Model.

I always like to start things out simply.  So, I've been working on this User Management Service, for mobile applications.  There are some columns missing.  Like LastLogin on the User.. etc.  Probably some tables for Application, if I were building an auto-updating Mobile Application service.  *coughs* Don't really want to get too much into the things I've left out on here at points.

The great thing about cloud work, is that you have no infrastructure.  I've seen a whole pile of nerds go gabby over how many servers etc that they manage, but once you commit capital to those resources, in a lot of ways it's like buying a new car.. just driving them off the lot, you lose value.  Not even that, if you have capital for those assets, then even worse from a business aspect, you have to have nerds to take care of that new ferrari server you've got parked in that rack.  That rack probably has AC cooling.  Why do the rack if you aren't going to cool your hardware off and risk component damage?  Then you need a locked room.  Not a great idea to have a ferrari, if you can't stop who takes it for a spin, right?  That's the problem with infrastructure, it starts to own you.

By staying up in the cloud, my servers get cheaper, the more competition there is for pricing, which happens pretty dang often (Thank you microsoft and amazon for being in a world domination tour, oh, Amazon is winning, MS, you better start making stuff as cheap as the competition, or you'll find that only C# sharp survives).

I'm going to throw some examples up here for components. I frequently use :

SendGrid.  Great service in Azure.  It's pretty much an add-in for your Azure account, and once you add that service to your account, you'll have the credentials to send your emails for your applications.

            SendGrid myMessage = SendGrid.GenerateInstance();
            myMessage.AddTo(u.UserEmail);
            myMessage.From = new System.Net.Mail.MailAddress("youremail", "youremaildisplayname");
            myMessage.Subject = "Welcome to {System}";
            string greeting = string.Format("Hello, {0}", u.FirstName);

            myMessage.Text = greeting + "\r\n" + "We are glad to have you in {System}.";

            // Create credentials, specifying your user name and password.
            var credentials = new NetworkCredential("credentialsgiveninsignupprocess", "");

            // Create an REST transport for sending email.
            var transportREST = SendGridMail.Transport.SMTP.GenerateInstance(credentials);

            // Send the email.
            transportREST.Deliver(myMessage);

Next comes to security.  I can't afford to have passwords in my account management system.  I can afford a salt and a hash.  So, here is the code for that.

        private static byte[] Hash(string value, string salt)
        {
            return Hash(Encoding.UTF8.GetBytes(value), Encoding.UTF8.GetBytes(salt));
        }

       private static byte[] Hash(byte[] value, byte[] salt)
        {
            byte[] saltedValue = value.Concat(salt).ToArray();
            
            return new SHA256Managed().ComputeHash(saltedValue);
        }

Oh, the return on that SHA-256, I just saved as varbinary (32).  I'm sure there are many different types you could use, but I was up at 1am in the morning, and when I finally got it working properly, I just called it quits for the night.

Two more simple utilities that I've found necessary, and are working quite well for me.  Remember to post on https folks.