Skip to content

Commit 23a6baf

Browse files
committed
Initial Commit of Code
Very early, untested code! Use at your own risk!
1 parent 4ab5e72 commit 23a6baf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+3806
-6
lines changed

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ bin
33
obj
44

55
# mstest test results
6-
TestResults
6+
TestResults
7+
8+
.suo
+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Collections.Specialized;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Web;
7+
using PushSharp.Common;
8+
9+
namespace PushSharp.Android
10+
{
11+
public class AndroidNotification : Notification
12+
{
13+
public AndroidNotification()
14+
{
15+
this.Platform = PlatformType.Android;
16+
17+
this.RegistrationId = string.Empty;
18+
this.CollapseKey = string.Empty;
19+
this.Data = new NameValueCollection();
20+
this.DelayWhileIdle = null;
21+
}
22+
23+
/// <summary>
24+
/// Registration ID of the Device
25+
/// </summary>
26+
public string RegistrationId
27+
{
28+
get;
29+
set;
30+
}
31+
32+
/// <summary>
33+
/// Only the latest message with the same collapse key will be delivered
34+
/// </summary>
35+
public string CollapseKey
36+
{
37+
get;
38+
set;
39+
}
40+
41+
/// <summary>
42+
/// Key/Value pairs to be sent to the Device (as extras in the Intent)
43+
/// </summary>
44+
public NameValueCollection Data
45+
{
46+
get;
47+
set;
48+
}
49+
50+
/// <summary>
51+
/// If true, C2DM will only be delivered once the device's screen is on
52+
/// </summary>
53+
public bool? DelayWhileIdle
54+
{
55+
get;
56+
set;
57+
}
58+
59+
internal string GetPostData()
60+
{
61+
var sb = new StringBuilder();
62+
63+
sb.AppendFormat("registration_id={0}&collapse_key={1}&", //&auth={2}&",
64+
HttpUtility.UrlEncode(this.RegistrationId),
65+
HttpUtility.UrlEncode(this.CollapseKey)
66+
//HttpUtility.UrlEncode(this.GoogleLoginAuthorizationToken)
67+
);
68+
69+
if (this.DelayWhileIdle.HasValue)
70+
sb.AppendFormat("delay_while_idle={0}&", this.DelayWhileIdle.Value ? "true" : "false");
71+
72+
foreach (var key in this.Data.AllKeys)
73+
{
74+
sb.AppendFormat("data.{0}={1}&",
75+
HttpUtility.UrlEncode(key),
76+
HttpUtility.UrlEncode(this.Data[key]));
77+
}
78+
79+
//Remove trailing & if necessary
80+
if (sb.Length > 0 && sb[sb.Length - 1] == '&')
81+
sb.Remove(sb.Length - 1, 1);
82+
83+
return sb.ToString();
84+
}
85+
86+
internal int GetMessageSize()
87+
{
88+
//http://groups.google.com/group/android-c2dm/browse_thread/thread/c70575480be4f883?pli=1
89+
// suggests that the max size of 1024 bytes only includes
90+
// only char counts of: keys, values, and the collapse_data value
91+
int size = HttpUtility.UrlEncode(this.CollapseKey).Length;
92+
93+
foreach (var key in this.Data.AllKeys)
94+
size += HttpUtility.UrlEncode(key).Length + HttpUtility.UrlEncode(this.Data[key]).Length;
95+
96+
return size;
97+
}
98+
}
99+
}
+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Collections.Specialized;
4+
using System.Linq;
5+
using System.Net;
6+
using System.Text;
7+
using PushSharp.Common;
8+
9+
namespace PushSharp.Android
10+
{
11+
public class AndroidPushChannel : PushChannelBase
12+
{
13+
AndroidPushChannelSettings androidSettings = null;
14+
string googleAuthToken = string.Empty;
15+
C2dmMessageTransportAsync transport;
16+
17+
public AndroidPushChannel(AndroidPushChannelSettings settings) : base(settings)
18+
{
19+
androidSettings = settings;
20+
21+
//Go get the auth token from google
22+
try
23+
{
24+
RefreshGoogleAuthToken();
25+
}
26+
catch (GoogleLoginAuthorizationException glaex)
27+
{
28+
this.Events.RaiseChannelException(glaex);
29+
}
30+
31+
transport = new C2dmMessageTransportAsync();
32+
transport.UpdateGoogleClientAuthToken += new Action<string>((newToken) =>
33+
{
34+
this.googleAuthToken = newToken;
35+
});
36+
37+
transport.MessageResponseReceived += new Action<C2dmMessageTransportResponse>(transport_MessageResponseReceived);
38+
39+
transport.UnhandledException += new Action<AndroidNotification, Exception>(transport_UnhandledException);
40+
}
41+
42+
void transport_UnhandledException(AndroidNotification notification, Exception exception)
43+
{
44+
this.Events.RaiseChannelException(exception);
45+
}
46+
47+
void transport_MessageResponseReceived(C2dmMessageTransportResponse response)
48+
{
49+
if (response.ResponseStatus == MessageTransportResponseStatus.Ok)
50+
this.Events.RaiseNotificationSent(response.Message);
51+
else if (response.ResponseStatus == MessageTransportResponseStatus.InvalidRegistration)
52+
{
53+
//Device subscription is no good!
54+
this.Events.RaiseDeviceSubscriptionExpired(PlatformType.Android, response.Message.RegistrationId);
55+
}
56+
else
57+
{
58+
//TODO: Raise error response
59+
this.Events.RaiseNotificationSendFailure(response.Message, new Exception(response.ResponseStatus.ToString()));
60+
}
61+
}
62+
63+
protected override void SendNotification(Notification notification)
64+
{
65+
transport.Send(notification as AndroidNotification, this.googleAuthToken, androidSettings.SenderID, androidSettings.ApplicationID);
66+
}
67+
68+
69+
/// <summary>
70+
/// Explicitly refreshes the Google Auth Token. Usually not necessary.
71+
/// </summary>
72+
public void RefreshGoogleAuthToken()
73+
{
74+
string authUrl = "https://www.google.com/accounts/ClientLogin";
75+
76+
var data = new NameValueCollection();
77+
78+
data.Add("Email", this.androidSettings.SenderID);
79+
data.Add("Passwd", this.androidSettings.Password);
80+
data.Add("accountType", "GOOGLE_OR_HOSTED");
81+
data.Add("service", "ac2dm");
82+
data.Add("source", this.androidSettings.ApplicationID);
83+
84+
var wc = new WebClient();
85+
86+
try
87+
{
88+
var authStr = Encoding.ASCII.GetString(wc.UploadValues(authUrl, data));
89+
90+
//Only care about the Auth= part at the end
91+
if (authStr.Contains("Auth="))
92+
googleAuthToken = authStr.Substring(authStr.IndexOf("Auth=") + 5);
93+
else
94+
throw new GoogleLoginAuthorizationException("Missing Auth Token");
95+
}
96+
catch (WebException ex)
97+
{
98+
var result = "Unknown Error";
99+
try { result = (new System.IO.StreamReader(ex.Response.GetResponseStream())).ReadToEnd(); }
100+
catch { }
101+
102+
throw new GoogleLoginAuthorizationException(result);
103+
}
104+
}
105+
}
106+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using PushSharp.Common;
6+
7+
namespace PushSharp.Android
8+
{
9+
public class AndroidPushChannelSettings : PushChannelSettings
10+
{
11+
public AndroidPushChannelSettings(string senderID, string password, string applicationID)
12+
{
13+
this.SenderID = senderID;
14+
this.Password = password;
15+
this.ApplicationID = applicationID;
16+
}
17+
18+
public string SenderID { get; private set; }
19+
public string Password { get; private set; }
20+
21+
public string ApplicationID { get; private set; }
22+
}
23+
}
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using PushSharp.Common;
6+
7+
namespace PushSharp.Android
8+
{
9+
public class AndroidPushService : PushServiceBase
10+
{
11+
public AndroidPushService(PushChannelSettings channelSettings, PushServiceSettings serviceSettings = null)
12+
: base(channelSettings, serviceSettings)
13+
{
14+
}
15+
16+
protected override PushChannelBase CreateChannel(PushChannelSettings channelSettings)
17+
{
18+
return new AndroidPushChannel(channelSettings as AndroidPushChannelSettings);
19+
}
20+
}
21+
}

0 commit comments

Comments
 (0)