Skip to content

Commit 0d4951b

Browse files
committed
Android Testing, sample/client, APNS Feedback
Some Android C2DM Testing performed, messages should be working for Android now! I also added a Client library for Mono for Android apps (that helps make registering for C2DM more simple, as well as handling incoming notifications), and a client sample app for Mono for Android to show you how to deal with c2dm in your android app. There's a MonoTouch client sample as well (there's really not a client library needed for monotouch since it's pretty simple code). Finally, fixed some issues with the APNS Enhanced format error feedback. Long story short we have to wait after sending each message to see if an error returns or not (since apple never returns a success), otherwise we may send messages after a bad message while waiting for apple's response, that apple will discard and not tell us about, before it closes the connection due to the first message being bad... Lame, but necessary!
1 parent 88270de commit 0d4951b

25 files changed

+955
-50
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 11.00
3+
# Visual Studio 2010
4+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PushSharp.ClientSample.MonoForAndroid", "PushSharp.ClientSample.MonoForAndroid\PushSharp.ClientSample.MonoForAndroid.csproj", "{3F402F02-F634-4D1B-A6B0-0D70D79CF992}"
5+
EndProject
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PushSharp.Client.MonoForAndroid", "..\..\PushSharp.Client\PushSharp.Client.MonoForAndroid\PushSharp.Client.MonoForAndroid.csproj", "{887745C1-B5C2-4BA7-9473-0617C1E5378B}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{3F402F02-F634-4D1B-A6B0-0D70D79CF992}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{3F402F02-F634-4D1B-A6B0-0D70D79CF992}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{3F402F02-F634-4D1B-A6B0-0D70D79CF992}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{3F402F02-F634-4D1B-A6B0-0D70D79CF992}.Release|Any CPU.Build.0 = Release|Any CPU
18+
{887745C1-B5C2-4BA7-9473-0617C1E5378B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19+
{887745C1-B5C2-4BA7-9473-0617C1E5378B}.Debug|Any CPU.Build.0 = Debug|Any CPU
20+
{887745C1-B5C2-4BA7-9473-0617C1E5378B}.Release|Any CPU.ActiveCfg = Release|Any CPU
21+
{887745C1-B5C2-4BA7-9473-0617C1E5378B}.Release|Any CPU.Build.0 = Release|Any CPU
22+
EndGlobalSection
23+
GlobalSection(MonoDevelopProperties) = preSolution
24+
StartupItem = PushSharp.ClientSample.MonoForAndroid\PushSharp.ClientSample.MonoForAndroid.csproj
25+
EndGlobalSection
26+
EndGlobal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Properties>
2+
<MonoDevelop.Ide.Workspace ActiveConfiguration="Debug" />
3+
<MonoDevelop.Ide.Workbench ActiveDocument="PushSharp.ClientSample.MonoForAndroid\Properties\AndroidManifest.xml">
4+
<Files>
5+
<File FileName="..\..\PushSharp.Client\PushSharp.Client.MonoForAndroid\C2dmBroadcastReceiver.cs" Line="14" Column="42" />
6+
<File FileName="..\..\PushSharp.Client\PushSharp.Client.MonoForAndroid\C2dmClient.cs" Line="14" Column="42" />
7+
<File FileName="..\..\PushSharp.Client\PushSharp.Client.MonoForAndroid\C2dmExceptions.cs" Line="14" Column="27" />
8+
<File FileName="..\..\PushSharp.Client\PushSharp.Client.MonoForAndroid\C2dmService.cs" Line="14" Column="27" />
9+
<File FileName="PushSharp.ClientSample.MonoForAndroid\PushService.cs" Line="25" Column="74" />
10+
<File FileName="PushSharp.ClientSample.MonoForAndroid\Properties\AndroidManifest.xml" Line="6" Column="64" />
11+
</Files>
12+
</MonoDevelop.Ide.Workbench>
13+
<MonoDevelop.Ide.DebuggingService.Breakpoints>
14+
<BreakpointStore />
15+
</MonoDevelop.Ide.DebuggingService.Breakpoints>
16+
<MonoDevelop.Ide.DebuggingService.PinnedWatches />
17+
</Properties>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Any raw assets you want to be deployed with your application can be placed in
2+
this directory (and child directories) and given a Build Action of "AndroidAsset".
3+
4+
These files will be deployed with you package and will be accessible using Android's
5+
AssetManager, like this:
6+
7+
public class ReadAsset : Activity
8+
{
9+
protected override void OnCreate (Bundle bundle)
10+
{
11+
base.OnCreate (bundle);
12+
13+
InputStream input = Assets.Open ("my_asset.txt");
14+
}
15+
}
16+
17+
Additionally, some Android functions will automatically load asset files:
18+
19+
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
using System;
2+
3+
using Android.App;
4+
using Android.Content;
5+
using Android.Runtime;
6+
using Android.Views;
7+
using Android.Widget;
8+
using Android.OS;
9+
using Android.Util;
10+
11+
namespace PushSharp.ClientSample.MonoForAndroid
12+
{
13+
[Activity(Label = "C2DM-Sharp Sample", MainLauncher = true,
14+
LaunchMode= Android.Content.PM.LaunchMode.SingleTask)]
15+
public class DefaultActivity : Activity
16+
{
17+
//NOTE: You need to put your own email here!
18+
// Whichever one you registered as the 'Role' email with google
19+
public const string senderIdEmail = "[email protected]";
20+
21+
22+
TextView textRegistrationStatus = null;
23+
TextView textRegistrationId = null;
24+
TextView textLastMsg = null;
25+
Button buttonRegister = null;
26+
bool registered = false;
27+
28+
protected override void OnCreate(Bundle bundle)
29+
{
30+
base.OnCreate(bundle);
31+
32+
// Set our view from the "main" layout resource
33+
SetContentView(Resource.Layout.Main);
34+
35+
textRegistrationStatus = FindViewById<TextView>(Resource.Id.textRegistrationStatus);
36+
textRegistrationId = FindViewById<TextView>(Resource.Id.textRegistrationId);
37+
textLastMsg = FindViewById<TextView>(Resource.Id.textLastMessage);
38+
buttonRegister = FindViewById<Button>(Resource.Id.buttonRegister);
39+
40+
Log.Info("C2DM-Sharp-UI", "Hello World");
41+
42+
this.buttonRegister.Click += delegate
43+
{
44+
if (!registered)
45+
{
46+
Log.Info("C2DM-Sharp", "Registering...");
47+
PushSharp.Client.MonoForAndroid.C2dmClient.Register(this, senderIdEmail);
48+
}
49+
else
50+
{
51+
Log.Info("C2DM-Sharp", "Unregistering...");
52+
PushSharp.Client.MonoForAndroid.C2dmClient.Unregister(this);
53+
}
54+
55+
RunOnUiThread(() =>
56+
{
57+
//Disable the button so that we can't click it again
58+
//until we get back to the activity from a notification
59+
this.buttonRegister.Enabled = false;
60+
});
61+
};
62+
}
63+
64+
protected override void OnResume()
65+
{
66+
base.OnResume();
67+
68+
updateView();
69+
}
70+
71+
void updateView()
72+
{
73+
//Get the stored latest registration id
74+
var registrationId = PushSharp.Client.MonoForAndroid.C2dmClient.GetRegistrationId(this);
75+
76+
//If it's empty, we need to register
77+
if (string.IsNullOrEmpty(registrationId))
78+
{
79+
registered = false;
80+
this.textRegistrationStatus.Text = "Registered: No";
81+
this.textRegistrationId.Text = "Id: N/A";
82+
this.buttonRegister.Text = "Register...";
83+
84+
Log.Info("C2DM-Sharp", "Not registered...");
85+
}
86+
else
87+
{
88+
registered = true;
89+
this.textRegistrationStatus.Text = "Registered: Yes";
90+
this.textRegistrationId.Text = "Id: " + registrationId;
91+
this.buttonRegister.Text = "Unregister...";
92+
93+
Log.Info("C2DM-Sharp", "Already Registered: " + registrationId);
94+
}
95+
96+
var prefs = GetSharedPreferences("c2dm.client.sample", FileCreationMode.Private);
97+
this.textLastMsg.Text = "Last Msg: " + prefs.GetString("last_msg", "N/A");
98+
99+
//Enable the button as it was normally disabled
100+
this.buttonRegister.Enabled = true;
101+
}
102+
}
103+
}
104+
105+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.pushsharp.test">
3+
<uses-sdk android:minSdkVersion="8" />
4+
<application android:label="PushSharp.ClientSample.MonoForAndroid"></application>
5+
<permission android:name="com.pushsharp.test.permission.C2D_MESSAGE" android:protectionLevel="signature" />
6+
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
7+
<uses-permission android:name="android.permission.INTERNET" />
8+
<uses-permission android:name="com.pushsharp.test.permission.C2D_MESSAGE" />
9+
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
10+
</manifest>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System.Reflection;
2+
using System.Runtime.CompilerServices;
3+
using Android.App;
4+
5+
// Information about this assembly is defined by the following attributes.
6+
// Change them to the values specific to your project.
7+
8+
[assembly: AssemblyTitle("PushSharp.ClientSample.MonoForAndroid")]
9+
[assembly: AssemblyDescription("")]
10+
[assembly: AssemblyConfiguration("")]
11+
[assembly: AssemblyCompany("")]
12+
[assembly: AssemblyProduct("")]
13+
[assembly: AssemblyCopyright("20363")]
14+
[assembly: AssemblyTrademark("")]
15+
[assembly: AssemblyCulture("")]
16+
17+
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
18+
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
19+
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
20+
21+
[assembly: AssemblyVersion("1.0.0")]
22+
23+
// The following attributes are used to specify the signing key for the assembly,
24+
// if desired. See the Mono documentation for more information about signing.
25+
26+
//[assembly: AssemblyDelaySign(false)]
27+
//[assembly: AssemblyKeyFile("")]
28+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
7+
using Android.App;
8+
using Android.Content;
9+
using Android.OS;
10+
using Android.Runtime;
11+
using Android.Views;
12+
using Android.Widget;
13+
using Android.Util;
14+
using PushSharp.Client.MonoForAndroid;
15+
16+
namespace PushSharp.ClientSample.MonoForAndroid
17+
{
18+
//You must subclass this!
19+
[BroadcastReceiver(Permission = C2dmClient.GOOGLE_PERMISSION_C2DM_SEND)]
20+
[IntentFilter(new string[] { C2dmClient.GOOGLE_ACTION_C2DM_INTENT_RECEIVE },
21+
Categories = new string[] { "com.pushsharp.test" })]
22+
[IntentFilter(new string[] { C2dmClient.GOOGLE_ACTION_C2DM_INTENT_REGISTRATION },
23+
Categories = new string[] { "com.pushsharp.test" })]
24+
//[C2dmReceiver]
25+
//[C2dmReceiveIntentFilter("c2dmsharp.client.sample")]
26+
//[C2dmRegistrationIntentFilter("c2dmsharp.client.sample")]
27+
public class SampleBroadcastReceiver : C2dmBroadcastReceiver<PushService>
28+
{
29+
}
30+
31+
[Service] //Must use the service tag
32+
public class PushService : C2dmService
33+
{
34+
public override void OnRegistrationError(Exception ex)
35+
{
36+
Log.Error("C2DM-Sharp-Service", "Registration Failed: " + ex.Message);
37+
38+
//Create notification or do something useful
39+
}
40+
41+
public override void OnRegistered(string registrationId)
42+
{
43+
Log.Info("C2DM-Sharp-Service", "Registered: " + registrationId);
44+
//Send back to the server
45+
// var wc = new WebClient();
46+
// var result = wc.UploadString("http://your.server.com/api/register/", "POST",
47+
// "{ 'registrationId' : '" + registrationId + "' }");
48+
49+
createNotification("C2DM# Registered...", "The device has been Registered, Tap to View!");
50+
}
51+
52+
public override void OnUnregistered(string lastRegistrationId)
53+
{
54+
Log.Info("C2DM-Sharp-Service", "Unregistered: " + lastRegistrationId);
55+
//Remove from the web service
56+
// var wc = new WebClient();
57+
// var result = wc.UploadString("http://your.server.com/api/unregister/", "POST",
58+
// "{ 'registrationId' : '" + lastRegistrationId + "' }");
59+
60+
createNotification("C2DM# Unregistered...", "The device has been unregistered, Tap to View!");
61+
}
62+
63+
public override void OnMessageReceived(Bundle extras)
64+
{
65+
Log.Info("C2DM-Sharp-Service", "Message Received!");
66+
67+
var msg = new StringBuilder();
68+
foreach (var key in extras.KeySet())
69+
msg.AppendLine(key + "=" + extras.Get(key).ToString());
70+
71+
//Store the message
72+
var prefs = GetSharedPreferences("c2dm.client.sample", FileCreationMode.Private);
73+
var edit = prefs.Edit();
74+
edit.PutString("last_msg", msg.ToString());
75+
edit.Commit();
76+
77+
createNotification("C2DM# Msg Rec'd", "Message Received for C2DM-Sharp... Tap to View!");
78+
}
79+
80+
void createNotification(string title, string desc)
81+
{
82+
//Create notification
83+
var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
84+
85+
//Create an intent to show ui
86+
var uiIntent = new Intent(this, typeof(DefaultActivity));
87+
88+
//Create the notification
89+
var notification = new Notification(Android.Resource.Drawable.SymActionEmail, title);
90+
91+
//Auto cancel will remove the notification once the user touches it
92+
notification.Flags = NotificationFlags.AutoCancel;
93+
94+
//Set the notification info
95+
//we use the pending intent, passing our ui intent over which will get called
96+
//when the notification is tapped.
97+
notification.SetLatestEventInfo(this,
98+
title,
99+
desc,
100+
PendingIntent.GetActivity(this, 0, uiIntent, 0));
101+
102+
//Show the notification
103+
notificationManager.Notify(1, notification);
104+
}
105+
}
106+
}
107+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup>
4+
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5+
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6+
<ProductVersion>10.0.0</ProductVersion>
7+
<SchemaVersion>2.0</SchemaVersion>
8+
<ProjectGuid>{3F402F02-F634-4D1B-A6B0-0D70D79CF992}</ProjectGuid>
9+
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
10+
<OutputType>Library</OutputType>
11+
<RootNamespace>PushSharp.ClientSample.MonoForAndroid</RootNamespace>
12+
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
13+
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
14+
<AndroidResgenClass>Resource</AndroidResgenClass>
15+
<AndroidApplication>True</AndroidApplication>
16+
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
17+
<AssemblyName>PushSharp.ClientSample.MonoForAndroid</AssemblyName>
18+
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
19+
</PropertyGroup>
20+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
21+
<DebugSymbols>true</DebugSymbols>
22+
<DebugType>full</DebugType>
23+
<Optimize>false</Optimize>
24+
<OutputPath>bin\Debug</OutputPath>
25+
<DefineConstants>DEBUG;</DefineConstants>
26+
<ErrorReport>prompt</ErrorReport>
27+
<WarningLevel>4</WarningLevel>
28+
<ConsolePause>false</ConsolePause>
29+
<AndroidLinkMode>None</AndroidLinkMode>
30+
</PropertyGroup>
31+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
32+
<DebugType>none</DebugType>
33+
<Optimize>true</Optimize>
34+
<OutputPath>bin\Release</OutputPath>
35+
<ErrorReport>prompt</ErrorReport>
36+
<WarningLevel>4</WarningLevel>
37+
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
38+
<ConsolePause>false</ConsolePause>
39+
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
40+
</PropertyGroup>
41+
<ItemGroup>
42+
<Reference Include="System" />
43+
<Reference Include="System.Xml" />
44+
<Reference Include="System.Core" />
45+
<Reference Include="Mono.Android" />
46+
</ItemGroup>
47+
<ItemGroup>
48+
<Compile Include="Resources\Resource.designer.cs" />
49+
<Compile Include="Properties\AssemblyInfo.cs" />
50+
<Compile Include="PushService.cs" />
51+
<Compile Include="DefaultActivity.cs" />
52+
</ItemGroup>
53+
<ItemGroup>
54+
<None Include="Resources\AboutResources.txt" />
55+
<None Include="Assets\AboutAssets.txt" />
56+
<None Include="Properties\AndroidManifest.xml" />
57+
</ItemGroup>
58+
<ItemGroup>
59+
<AndroidResource Include="Resources\layout\Main.axml" />
60+
<AndroidResource Include="Resources\values\Strings.xml" />
61+
<AndroidResource Include="Resources\drawable\Icon.png" />
62+
</ItemGroup>
63+
<Import Project="$(MSBuildExtensionsPath)\Novell\Novell.MonoDroid.CSharp.targets" />
64+
<ItemGroup>
65+
<ProjectReference Include="..\..\..\PushSharp.Client\PushSharp.Client.MonoForAndroid\PushSharp.Client.MonoForAndroid.csproj">
66+
<Project>{887745C1-B5C2-4BA7-9473-0617C1E5378B}</Project>
67+
<Name>PushSharp.Client.MonoForAndroid</Name>
68+
</ProjectReference>
69+
</ItemGroup>
70+
</Project>

0 commit comments

Comments
 (0)