FCM notification from web api c# to android - c#

I'm developing API via c# that will send notification to specific user ( android user) , then when user open the notification I want to redirect him to specific activity.
So I needed to send data along with notification message. I've tested it using Firebase Console and it's working fine , The notification is received and my launcher activity receive the extra from data has been sent
I've also tested it from my backend and the notification is received except that my launcher intent doesn't receive any extra.
I've been struggling for hours now , Any idea would help !
this is my code from c#
public String getNotification ()
{
string serverKey = "xxxx";
var result = "-1";
try
{
var webAddr = "https://fcm.googleapis.com/fcm/send";
var regID = "xxxx";
var httpWebRequest = (HttpWebRequest)WebRequest.Create(webAddr);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Headers.Add("Authorization:key=" + serverKey);
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = "{\"to\": \"" + regID +
"\",\"notification\": {\"title\": \"Testing\",\"body\": \"Hi Testing\"}" +
"," + "\"data:\"" + "{\"mymsg\":" + "\"h\" }}";
streamWriter.Write(json);
streamWriter.Flush();
}
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
result = streamReader.ReadToEnd();
}
return result;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return "Can't Send";
}
}
}
And this is my launcher activity :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("test" , "in main");
if (getIntent().getStringExtra("mymsg") != null) {
Log.d("test" , "has extra");
Intent intent = new Intent(this, Main2Activity.class);
startActivity(intent);
finish();
} else {
Log.d("test" , "no extra");
}

It looks like you have the wrong JSON:
," + "\"data:\"" + "{\"mymsg\":" + "\"h\" }} will be:
"data:" {
"mymsg":"h"
}
just correct your JSON. But I recommend using c# classes and serialization. Look at this simple example:
var payload = new {
to = "XXXX",
notification = new
{
body = "Test",
title = "Test"
},
data = new {
mymsg = "h"
}
}
// Using Newtonsoft.Json
string postbody = JsonConvert.SerializeObject(payload).ToString();
But its just example. You should create classes instead of anonym objects and using JsonProperty or another way to serialize the object. Something like that:
/// <summary>
/// Data for sending push-messages.
/// </summary>
public class PushData
{
/// <summary>
/// [IOS] Displaying message
/// </summary>
[JsonProperty("alert")]
public Alert Alert { get; set; }
/// <summary>
/// [IOS] badge value (can accept string representaion of number or "Increment")
/// </summary>
[JsonProperty("badge")]
public Int32? Badge { get; set; }
/// <summary>
/// [IOS] The name of sound to play
/// </summary>
[JsonProperty("sound")]
public String Sound { get; set; }
/// <summary>
/// [IOS>=7] Content to download in background
/// </summary>
/// <remarks>
/// Set 1 for silent mode
/// </remarks>
[JsonProperty("content-available")]
public Int32? ContentAvailable { get; set; }
/// <summary>
/// [IOS>=8] Category of interactive push with additional actions
/// </summary>
[JsonProperty("category")]
public String Category { get; set; }
/// <summary>
/// [Android] Used for collapsing some messages with same collapse_key
/// </summary>
[JsonProperty(PropertyName = "collapse_key")]
public String CollapseKey { get; set; }
/// <summary>
/// [Android] This parameter specifies how long (in seconds) the message should be kept in GCM storage if the device is offline.
/// The maximum time to live supported is 4 weeks, and the default value is 4 weeks.
/// </summary>
/// <value>
/// Time_to_live value of 0 means messages that can't be delivered immediately will be discarded
/// </value>
[JsonProperty("time_to_live")]
public Int32 TimeToLive { get; set; }
/// <summary>
/// [Android] Uri of activity to open when push activated by user
/// </summary>
[JsonProperty("url")]
public String Url { get; set; }
/// <summary>
/// Payload for push
/// </summary>
[JsonProperty("data")]
public Payload Payload { get; set; }
}
with message builder which serialize your message body to correct json string.

Related

SOAPEnvelope class replacement after WSE 3.0 decomission

I ran into an issue recently when in an existing Console Application project we're forced to remove a reference to WSE 3.0. When I tried to remove it from references (because I could not find where it's used) it turned out there is only one place and it's using the SoapEnvelope class.
A little bit about the application: it's a console application that connects to an Exchange Server and listens to incoming emails, around 2-3k emails daily.
The SoapEnvelope class is used to read and parse the email body:
/// <summary>
/// Reads content of received HTTP request.
/// </summary>
/// <param name="client">The specified client.</param>
/// <returns>The watermark string if the read is successful; otherwise the last watermark.</returns>
public string Read(TcpClient client)
{
try
{
_myReadBuffer = new byte[client.ReceiveBufferSize];
using (var networkStream = client.GetStream())
{
var httpRequest = new MailboxHttpRequest(networkStream);
if (httpRequest.HasBody)
{
var httpResponse = new MailboxHttpResponse(_mailbox);
httpResponse.ParseBody(httpRequest.Body);
httpResponse.Send(networkStream, httpRequest.Body);
}
return httpRequest.Watermark;
}
}
catch (Exception ex)
{
Logger.WriteErrorLine("[EventsCollector] Error calling Read in MailboxNotificationManager", ex);
return _mailbox.LastWatermark;
}
finally
{
_myReadBuffer = null;
}
}
And the ParseBody methods looks like this:
/// <summary>
/// Parses the XML body content of receive notification and initialized email processing, if new message was received.
/// </summary>
/// <param name="body">XML content of received notification.</param>
public void ParseBody(string body)
{
var soapEnvelope = new SoapEnvelope() { InnerXml = body };
var serializer = new XmlSerializer(typeof(SendNotificationResponseType));
using (var reader = new XmlNodeReader(soapEnvelope.Body.FirstChild))
{
var notificationResponse = (SendNotificationResponseType)serializer.Deserialize(reader);
if (notificationResponse.ResponseMessages != null) // Process notification, if request contains response message
{
_result = processNotification(notificationResponse);
}
}
}
As you can see, it creates a SoapEnvelope class to access the body`s first child.
MailboxHttpRequest class
private class MailboxHttpRequest
{
private readonly Regex _httpRequestBodyLengthPattern = new Regex(#"Content-Length: (?<BodyLength>\d*)", RegexOptions.Compiled);
private readonly Regex _httpRequestBodyPattern = new Regex(#"<\?xml .*", RegexOptions.Compiled);
private readonly Regex _httpRequestWatermarkPattern = new Regex(#"<t:Watermark>(?<Watermark>.*?)<\/t:Watermark>", RegexOptions.Compiled);
private const int _bufferSize = 8192;
public bool HasBody
{
get { return !String.IsNullOrEmpty(Body); }
}
public string Body { get; private set; }
public string Watermark { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="MailboxHttpRequest"/> class and reads incoming messages
/// </summary>
/// <param name="networkStream">The network stream.</param>
public MailboxHttpRequest(NetworkStream networkStream)
{
var completeRequest = new StringBuilder();
var readBuffer = new byte[_bufferSize];
do // Read incoming message that might consist of many parts
{
var newDataLength = networkStream.Read(readBuffer, 0, readBuffer.Length);
completeRequest.Append(Encoding.ASCII.GetString(readBuffer, 0, newDataLength));
}
while (networkStream.DataAvailable || !requestWasFullyReceived(completeRequest.ToString()));
Body = _httpRequestBodyPattern.Match(completeRequest.ToString())
.Value;
Watermark = _httpRequestWatermarkPattern.Match(Body).Groups["Watermark"].Value;
}
}
The downside is that I`m not able test this part of code since I cant listen to same mailbox so I cant check what and how does the passed string look like.
If anybody has any suggestion on how to replace the SoapEnvelope class, would be greatly appreciate it.
Cosmin

JsonSerializer.DeserializeAsync<T> Not deserializing

I am working with my first Blazor app and running into a strange problem. I have a Web API that returns a fairly basic JSON response:
{
"entityId": 26,
"notifications": [
{
"type": "Success",
"message": "The operation completed successfully."
}
],
"hasErrors": false,
"hasWarnings": false
}
There is a standard POCO class that matches the above properties. From the Blazor app, when I try to get the response from an Http.PutAsJsonAsync<T>, an instance of the POCO class is created (so it's not null and doesn't throw an error), but none of the values above are actually present. The EntityId property is null and Notifications is instantiated but empty. The way I'm trying to access the response is:
var result = await Http.PutAsJsonAsync<ManufacturerModel>($"manufacturers", (ManufacturerModel)context.Model);
if (result.IsSuccessStatusCode)
{
var response = await JsonSerializer.DeserializeAsync<EntityResponseModel>(result.Content.ReadAsStream());
//response isn't null - it's just a newly created object with nothing mapped
}
Via the console in Chrome, I've confirmed the proper JSON is returned so it's really confusing why it'd create a new instance of that class, but not map any of the values.
Any ideas?
**** EDIT - including POCO definition ****
public class EntityResponseModel : BaseModel
{
/// <summary>
/// Gets or sets the Id of the affected entity
/// </summary>
public long? EntityId { get; set; }
}
public class BaseModel
{
public BaseModel()
{
this.Notifications = new EntityNotificationCollection();
}
#region Validation
/// <summary>
/// Adds a notification to this model.
/// </summary>
/// <param name="type">The type of notification</param>
/// <param name="message">The message to display</param>
public void AddNotification(EntityNotificationTypes type, string message)
{
this.Notifications.Add(new EntityNotification { Type = type, Message = message });
}
/// <summary>
/// Gets or sets the collection of notifications
/// </summary>
public EntityNotificationCollection Notifications { get; private set; }
/// <summary>
/// Gets whether errors exist on this model.
/// </summary>
//[JsonIgnore]
public bool HasErrors { get => this.Notifications.HasErrors; }
/// <summary>
/// Gets whether warnings exist on this model
/// </summary>
//[JsonIgnore]
public bool HasWarnings { get => this.Notifications.HasWarnings; }
#endregion
}
You can specify your serialization settings and define it as being case sensitive or insensitive.
CharlieFace provided this answer above.
Looks like you need to add your JsonAttribute to managing your case sensitivity.
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
};
This is my full solution:
var retList = new List<PocoSomeClassData> ();
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode) {
using (var reponseStream = await response.Content.ReadAsStreamAsync())
{
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
};
retList = await JsonSerializer.DeserializeAsync < List<PocoSomeClassData> > (reponseStream,options);
}
}

How to convert voice speech in format .wav and to use it

I am developing a bot which will receive voice ( from facebook channel ) and will convert it in .wav .
I'm using a sample of : How can a bot receive a voice file from Facebook Messenger (MP4) and convert it to a format that is recognized by speech engines like Bing or Google?
I had a problem during converting i think .
Here is my code :
messagescontroller.cs :
namespace SpeechToText.Controllers
{
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using Microsoft.Bot.Connector;
using Services;
using System.Web.Configuration;
using System.IO;
[BotAuthentication]
public class MessagesController : ApiController
{
private readonly MicrosoftCognitiveSpeechService speechService = new MicrosoftCognitiveSpeechService();
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
var connector = new ConnectorClient(new Uri(activity.ServiceUrl));
string message = string.Empty;
IncomingFacebookVoiceMessage voice = null;
try
{
voice = new IncomingFacebookVoiceMessage(activity);
//await SendReply(activity, "The type of your voice file is " + voice.ContentType);
}
catch
{
message = "Send me a voice message instead!"; // no voice file found
}
try
{
if (voice != null)
{
//Download original MP4 voice message
voice.DownloadFile();
var mp4 = voice.GetLocalPathAndFileName();
//Convert MP4 to WAV
// var wavFolder = Utils.GetHomeFolder() + WebConfigurationManager.AppSettings["WAVFilesFolder"];
var wavFolder = "C:" + #"\" + "Teste" ;
var converter = new AudioFileFormatConverter(mp4, wavFolder);
var wav = converter.ConvertMP4ToWAV();
//Convert .WAV file to text
var bing = new MicrosoftCognitiveSpeechService(); //gets the path + filename
// var text = await bing.GetTextFromAudioAsync(wav); //takes path+filename to WAV file, returns text
// convert string to stream
byte[] data = File.ReadAllBytes(wav);
//byte[] byteArray = Encoding.ASCII.GetBytes(contents);
MemoryStream stream = new MemoryStream(data);
var text = await this.speechService.GetTextFromAudioAsync(stream);
if (string.IsNullOrWhiteSpace(text))
{
message = "Looks like you didn't say anything.";
}
else
{
message = text;
}
//Clean up files from disk
voice.RemoveFromDisk();
converter.RemoveTargetFileFromDisk();
}
}
catch (Exception ex)
{
message = "Woah! " + ex.Message.Trim().Trim('.') + "!";
}
Activity reply = activity.CreateReply(message);
await connector.Conversations.ReplyToActivityAsync(reply);
}
else
{
await this.HandleSystemMessage(activity);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
/// <summary>
/// Gets the JwT token of the bot.
/// </summary>
/// <param name="connector"></param>
/// <returns>JwT token of the bot</returns>
/// <summary>
/// Handles the system activity.
/// </summary>
/// <param name="activity">The activity.</param>
/// <returns>Activity</returns>
private async Task<Activity> HandleSystemMessage(Activity activity)
{
switch (activity.Type)
{
case ActivityTypes.DeleteUserData:
// Implement user deletion here
// If we handle user deletion, return a real message
break;
case ActivityTypes.ConversationUpdate:
// Greet the user the first time the bot is added to a conversation.
if (activity.MembersAdded.Any(m => m.Id == activity.Recipient.Id))
{
var connector = new ConnectorClient(new Uri(activity.ServiceUrl));
var response = activity.CreateReply();
response.Text = "Hi! I am SpeechToText Bot. I can understand the content of any audio and convert it to text. Try sending me a wav file.";
await connector.Conversations.ReplyToActivityAsync(response);
}
break;
case ActivityTypes.ContactRelationUpdate:
// Handle add/remove from contact lists
break;
case ActivityTypes.Typing:
// Handle knowing that the user is typing
break;
case ActivityTypes.Ping:
break;
}
return null;
}
}
}
-------------------- IncomingFacebookVoiceMessage.cs
using Microsoft.Bot.Connector;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Configuration;
namespace SpeechToText
{
/// <summary>
/// Represents an incoming voice message from facebook messenger,
/// where the voice data is in an MP4 file (the message contains a link to
download it).
/// </summary>
public class IncomingFacebookVoiceMessage
{
#region Properties
/// <summary>
/// URL of the MP4 file sent by user and stored on facebook's servers.
/// </summary>
public Uri MP4FileUrl { get; private set; }
/// <summary>
/// Local filename of the MP4 file after it has been downloaded from Facebook.
/// </summary>
private string MP4LocalFileName { get; set; }
/// <summary>
/// Path to the folder on local disk containing the downloaded voice messages from Facebook.
/// This is configured in Web.config using the FacebookDownloadedVoiceMessagesFolder key.
/// The path in the Web.config will be relative to the site's root folder.
/// </summary>
public string VoiceMessageFolder { get; private set; }
/// <summary>
/// Content-type of the attachment (for debugging - it's not always MP4).
/// </summary>
public string ContentType { get; private set; }
#endregion
#region Constructors
/// <summary>
/// Constructor that uses an MP4 file link that is
/// received in activity.Attachments by bot framework.
/// </summary>
/// <param name="MP4FileUrl">URL of the MP4 file sent by user and stored on facebook's servers.</param>
public IncomingFacebookVoiceMessage(string MP4FileUrl)
{
if (string.IsNullOrWhiteSpace(MP4FileUrl))
{
throw new Exception("The MP4 file URL was empty.");
}
this.MP4FileUrl = new Uri(MP4FileUrl);
this.VoiceMessageFolder = GetVoiceMessagesFolderFromWebConfig();
}
/// <summary>
/// A shortcut constructor that extracts the URL of the MP4 voice message file
/// from the Activity object received by the controller in the Bot Framework.
/// </summary>
/// <param name="activity">The Activity object that contains an attachment of type video/mp4. If no attachment, throws an exception.</param>
public IncomingFacebookVoiceMessage(IMessageActivity activity)
{
var mp4Attachment = activity.Attachments?.FirstOrDefault(a => a.ContentType.Equals("video/mp4") || a.ContentType.Contains("audio") || a.ContentType.Contains("video"));
if (mp4Attachment == null)
{
throw new Exception("The message didn't have a voice attachment.");
}
else
{
this.MP4FileUrl = new Uri(mp4Attachment.ContentUrl);
this.VoiceMessageFolder = GetVoiceMessagesFolderFromWebConfig();
this.ContentType = mp4Attachment.ContentType; //for debugging. Different devices send different content-types, e.g. audio/aac and video/mp4
}
}
#endregion
#region Public methods
/// <summary>
/// Downloads the MP4 file containing the voice message from Facebook.
/// </summary>
/// <returns>The filename (without path) of the MP4 file stored on local disk.</returns>
public string DownloadFile()
{
var filename = GetRandomFileName();
var filenameWithPath = VoiceMessageFolder + #"\" + filename;
//if folder doesn't exist, create it
if (!Directory.Exists(VoiceMessageFolder))
{
Directory.CreateDirectory(VoiceMessageFolder);
}
using (var client = new WebClient())
{
client.DownloadFile(this.MP4FileUrl, filenameWithPath);
}
MP4LocalFileName = filename;
return filename;
}
/// <summary>
/// Removes the downloaded MP4 file from the local disk to clean up space.
/// </summary>
/// <returns>True if successfully removed, false otherwise.</returns>
public bool RemoveFromDisk()
{
try
{
File.Delete(GetLocalPathAndFileName());
return true;
}
catch
{
return false;
}
}
/// <summary>
/// Returns the full local path and filename to the downloaded MP4 voice message.
/// </summary>
/// <returns>E.g. D:\home\site\wwwroot\abc.mp4</returns>
public string GetLocalPathAndFileName()
{
if (string.IsNullOrWhiteSpace(MP4LocalFileName))
{
throw new Exception("The voice message has not been downloaded yet.");
}
return VoiceMessageFolder + #"\" + MP4LocalFileName;
}
#endregion
#region Private methods
/// <summary>
/// Reads Web.config and returns the path to the folder which will store downloaded messages.
/// The folder in the config must be relative to the site's root.
/// </summary>
/// <returns>Full path to the folder that will be used to store MP4 voice messages.</returns>
private string GetVoiceMessagesFolderFromWebConfig()
{
//return Utils.GetHomeFolder() + WebConfigurationManager.AppSettings["FacebookDownloadedVoiceMessagesFolder"];
return "C:" + #"\" + "Teste" ;
}
/// <summary>
/// Generates a random filename using a new GUID.
/// </summary>
/// <returns>A random file name in the format "msg-GUID.mp4".</returns>
private string GetRandomFileName()
{
return "msg-" + Guid.NewGuid() + ".mp4";
}
#endregion
}
}
---------------AudioFileFormatConverter.cs
using MediaToolkit;
using MediaToolkit.Model;
using System;
using System.IO;
using System.Web.Configuration;
namespace SpeechToText
{
/// <summary>
/// Converts audio files between various formats using the open-source
FFMPEG software.
/// </summary>
public class AudioFileFormatConverter
{
#region Properties
/// <summary>
/// Path + filename to the source file.
/// </summary>
public string SourceFileNameAndPath { get; private set; }
/// <summary>
/// Path + filename to the file which is the result of the conversion.
/// </summary>
public string ConvertedFileNameAndPath { get; private set; }
/// <summary>
/// The folder where the converted files will be stored.
/// </summary>
public string TargetPath { get; private set; }
#endregion
#region Constructors
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="sourceFileNameAndPath">Filename and path to the source
file.</param>
/// <param name="targetPath">The folder where the converted files will
be stored.</param>
public AudioFileFormatConverter(string sourceFileNameAndPath, string
targetPath)
{
if (string.IsNullOrWhiteSpace(sourceFileNameAndPath))
{
throw new Exception("Empty source filename.");
}
else if (string.IsNullOrWhiteSpace(targetPath))
{
throw new Exception("Empty target path.");
}
else
{
this.SourceFileNameAndPath = sourceFileNameAndPath;
this.TargetPath = targetPath;
//create folder if it's not there
if (!Directory.Exists(TargetPath))
{
Directory.CreateDirectory(TargetPath);
}
}
}
#endregion
#region Public methods
/// <summary>
/// Converts a source MP4 file to a target WAV file and returns the path
and filename of the converted file.
/// </summary>
/// <returns>The path and filename of the converted file.</returns>
public string ConvertMP4ToWAV()
{
ConvertedFileNameAndPath = TargetPath + #"\" +
Path.GetFileNameWithoutExtension(SourceFileNameAndPath) + ".wav";
//use the same
file name as original, but different folder and extension
var inputFile = new MediaFile { Filename = SourceFileNameAndPath };
var outputFile = new MediaFile { Filename = ConvertedFileNameAndPath
};
using (var engine = new Engine(GetFFMPEGBinaryPath()))
{
engine.Convert(inputFile, outputFile);
}
return ConvertedFileNameAndPath;
}
/// <summary>
/// Removes the converted file from disk to free up space.
/// Doesn't remove the source file.
/// </summary>
/// <returns>True if file deleted successfully, false if cannot delete,
exception if filename is empty.</returns>
public bool RemoveTargetFileFromDisk()
{
if (string.IsNullOrWhiteSpace(ConvertedFileNameAndPath))
{
throw new Exception("The file has not been converted yet, so it cannot be deleted.");
}
try
{
File.Delete(ConvertedFileNameAndPath);
return true;
}
catch
{
return false;
}
}
#endregion
#region Private methods
/// <summary>
/// Reads the config to determine the location of ffmpeg.exe.
/// </summary>
/// <returns>The full path and filename to the ffmpeg.exe program.
</returns>
public string GetFFMPEGBinaryPath()
{
// return Utils.GetHomeFolder() +
WebConfigurationManager.AppSettings["FFMPEGBinaryLocation"];
return "D:" + #"\" + "Teste";
}
#endregion
}
}
Here is my output message :
Can you please help me how to resolve the problem...where i should store the audio register in facebook messenger ?
I think all you are missing is ffmpeg.exe in the GetFFMPEGBinaryPath() method. Once I added that to the end of the path, the code you have functions as expected.
public string GetFFMPEGBinaryPath()
{
return "D:" + #"\Teste\ffmpeg-20170921-183fd30-win64-static\bin\ffmpeg.exe";
}

SharePoint online: can a Windows Client application use OAuth for authentication?

We build a Windows client application for SharePoint online with SharePoint Client Object Model. We want to use OAuth to authentication for this Windows Client application, but we didn't find a way to do it; and the docs on MSDN is ambiguous.
This article gives an example, however, when I create the new application with the link https://<TENANT>.sharepoint.com/_layouts/appregnew.aspx, the option of "An app running on a client machine" is disabled, is there a setting in SharePoint online site to enable this?
I got this working after a lot of trying
I guess it's not the most wonderfull code but here it is:
/// <summary>
/// Sets needed values
/// </summary>
/// <param name="clientId">The ClientId from the application</param>
/// <param name="redirectUri">The RedirectUri where the browser has to be send.</param>
/// <param name="resource">The source you want to access</param>
public OneDriveConnection(string clientId, string clientSecret, string redirectUri, string resource)
{
this._clientId = clientId;
this._redirectUri = Uri.EscapeDataString(redirectUri);
this._resource = Uri.EscapeDataString(resource);
this._clientSecret = clientSecret;
}
Next I create a browser where the user is prompted to log in:
/// <summary>
/// Authorizes the application
/// </summary>
public void Authorize()
{
/* EXAMPLE: GET https://login.windows.net/common/oauth2/authorize
* ?response_type=code
* &client_id=acb81092-056e-41d6-a553-36c5bd1d4a72
* &redirect_uri=https://mycoolwebapp.azurewebsites.net
* &resource=https:%2f%2foutlook.office365.com%2f
* &state=5fdfd60b-8457-4536-b20f-fcb658d19458 */
string baseUri = "https://login.windows.net/common/oauth2/authorize";
string authorizationUri = string.Format(baseUri
+ "?response_type=code"
+ "&client_id={0}"
+ "&redirect_uri={1}"
+ "&resource={2}"
+ "&state={3}", this._clientId, this._redirectUri, this._resource, "5fdfd60b-8457-4536-b20f-fcb658d19458");
// Create the form
Form webBrowserForm = new Form();
webBrowserForm.MaximizeBox = false;
webBrowserForm.MinimizeBox = false;
webBrowserForm.Size = new System.Drawing.Size(580, 890);
webBrowserForm.Text = "Webbrowser";
webBrowserForm.FormBorderStyle = FormBorderStyle.FixedDialog;
webBrowserForm.StartPosition = FormStartPosition.CenterScreen;
// Create the WebBrowser
WebBrowser webBrowser = new WebBrowser();
webBrowser.Width = 580;
webBrowser.Height = 890;
webBrowser.Location = new System.Drawing.Point(0, 0);
webBrowser.ShowPageSetupDialog();
// Hook event to the webBrowser
webBrowser.Navigated += webBrowser_Navigated;
// Show the webBrowser and form to the user
webBrowserForm.Controls.Add(webBrowser);
webBrowserForm.Show();
// Navigate to the authorizationUri
webBrowser.Navigate(authorizationUri);
}
Here I check if there is a code to execute the GetTokenInformation method:
/// <summary>
/// When the url has code in it and contains a session_state get the code and do the GetTokenInformation
/// </summary>
private void webBrowser_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
if (e.Url.AbsoluteUri.Contains("code=") && e.Url.AbsoluteUri.Contains("session_state"))
{
string[] splited = e.Url.AbsoluteUri.Split(new char[] { '=', '&' });
_code = splited[1];
if (!string.IsNullOrWhiteSpace(_code)
&& !string.IsNullOrWhiteSpace(_redirectUri)
&& !string.IsNullOrWhiteSpace(_clientId))
{
GetTokenInformation(_code, _redirectUri, _clientId, _clientSecret);
}
else
{
_connected = false;
}
}
}
In the GetTokenInformation method I get the TokenInformation which I put in a TokenInformation class using the Newtonsoft.Json dll
/// <summary>
/// This method gets tokeninformation: access_token, token_type, expires_in, resource, refresh_token, scope, id_token
/// </summary>
/// <param name="code">Code from the authorize request</param>
/// <param name="redirectUri">Reply url for your application</param>
/// <param name="clientId">Your applications client id in Windows Azure Directory</param>
/// <param name="clientSecret">Your applications client secret</param>
private void GetTokenInformation(string code, string redirectUri, string clientId, string clientSecret)
{
// Get the token information that is set above in the constructor with the help of the clientId, clientSecret and code and as well as the redirectUri without it you can't connect to it otherwise it will crash if you don't do it like that
string baseUri = "https://login.windows.net/common/oauth2/token";
string parameters = string.Format("grant_type=authorization_code"
+ "&code={0}"
+ "&redirect_uri={1}"
+ "&client_id={2}"
+ "&client_secret={3}", code, redirectUri, clientId, clientSecret);
string response = HttpPost(baseUri, parameters);
if (!string.IsNullOrWhiteSpace(response))
{
_tokenInformation = JsonConvert.DeserializeObject<TokenInformation>(response);
_connected = true;
}
else
{
_connected = false;
}
}
And here is my TokenInformation class using the Newtonsoft.Json dll:
[JsonObject(MemberSerialization.OptIn)]
class TokenInformation
{
[JsonProperty(PropertyName = "access_token")]
public string AccessToken { get; set; }
[JsonProperty(PropertyName = "token_type")]
public string TokenType { get; set; }
[JsonProperty(PropertyName = "expires_in")]
public int ExpiresIn { get; set; }
[JsonProperty(PropertyName = "expires_on")]
public int ExpiresOn { get; set; }
[JsonProperty(PropertyName = "resource")]
public string Resource { get; set; }
[JsonProperty(PropertyName = "refresh_token")]
public string RefreshToken { get; set; }
[JsonProperty(PropertyName = "scope")]
public string Scope { get; set; }
[JsonProperty(PropertyName = "id_token")]
public string IdToken { get; set; }
}
This is where I found the requests which I needed to make to connect to SharePoint/Office365: link
You can try using an app as the "proxy" to do oAuth as a workaround. The "implicit" oAuth flow which is what native apps use to connect directly to services is not yet available AFAIK

Problem dynamically adding XML items to listbox, C#?

I'm pulling feeds for my RSS project and I am running into a problem of not knowing how to allow the user to load more items into the collection. At the current moment, everything loads at once. While this is in some cases all right, I would like the user to be able to choose how things get loaded in case they have a slow mobile connection.
This is borrowed code and thus it only adds to my confusion.
Where could i be able to inject code into this sample to allow a dynamic loading of items, say, 30 at a time?
Rss Class:
namespace MyRSSService
{
public class RssService
{
/// Gets the RSS items.
/// <param name="rssFeed">The RSS feed.</param>
/// <param name="onGetRssItemsCompleted">The on get RSS items completed.</param>
/// <param name="onError">The on error.</param>
public static void GetRssItems(string rssFeed, Action<IList<RssItem>> onGetRssItemsCompleted = null, Action<Exception> onError = null, Action onFinally = null)
{
WebClient webClient = new WebClient();
// register on download complete event
webClient.OpenReadCompleted += delegate(object sender, OpenReadCompletedEventArgs e)
{
try
{
// report error
if (e.Error != null)
{
if (onError != null)
{
onError(e.Error);
}
return;
}
// convert rss result to model
IList<RssItem> rssItems = new List<RssItem>();
Stream stream = e.Result;
XmlReader response = XmlReader.Create(stream);
{
SyndicationFeed feeds = SyndicationFeed.Load(response);
foreach (SyndicationItem f in feeds.Items)
{
RssItem rssItem = new RssItem(f.Title.Text, f.Summary.Text, f.PublishDate.ToString(), f.Links[0].Uri.AbsoluteUri);
rssItems.Add(rssItem);
}
}
// notify completed callback
if (onGetRssItemsCompleted != null)
{
onGetRssItemsCompleted(rssItems);
}
}
finally
{
// notify finally callback
if (onFinally != null)
{
onFinally();
}
}
};
webClient.OpenReadAsync(new Uri(rssFeed));
}
}
}
items setting class:
namespace MyRSSService
{
public class RssItem
{
/// <summary>
/// Initializes a new instance of the <see cref="RssItem"/> class.
/// </summary>
/// <param name="title">The title.</param>
/// <param name="summary">The summary.</param>
/// <param name="publishedDate">The published date.</param>
/// <param name="url">The URL.</param>
public RssItem(string title, string summary, string publishedDate, string url)
{
Title = title;
Summary = summary;
PublishedDate = publishedDate;
Url = url;
// Get plain text from html
PlainSummary = HttpUtility.HtmlDecode(Regex.Replace(summary, "<[^>]+?>", ""));
}
public string Title { get; set; }
public string Summary { get; set; }
public string PublishedDate { get; set; }
public string Url { get; set; }
public string PlainSummary { get; set; }
}
}
the binding C# to the page to display the feeds
public partial class FeedPage : PhoneApplicationPage
{
private const string WindowsPhoneBlogPosts = "http://feeds.bbci.co.uk/news/rss.xml";
public FeedPage()
{
InitializeComponent();
RssService.GetRssItems(WindowsPhoneBlogPosts, (items) => { listbox.ItemsSource = items; }, (exception) => { MessageBox.Show(exception.Message); }, null);
}
}
Unless the server where the feed is hosted provides an API to limit the number of returned items (for example, this practice is used for the Xbox Marketplace), you will be downloading the entire feed, even if you decide to only show a part of it.

Categories

Resources