WinRT

Na co dzień miliony osób korzysta z facebook. Facebook też jest ważnym aspektem jeśli chodzi o marketing samej aplikacji.

Nic więc dziwnego, że aplikacje dają możliwość pochwalenia się swoimi wyczynami na facebook-u.

Dzisiaj opiszę jak używając wbudowanej funkcjonalności, jak i specjalnego SDK osiągnąć ten cel w WinRT. Aplikacja WinRT zaloguje nas do Facebook-a, a potem na naszym koncie Facebook-owym napisze wiadomość.

Zanim rozpoczniesz pisanie aplikacji bądź ściągniesz mojego gotowca musisz stworzyć aplikację Facebook-ową, która będzie wykonywała za nas (na tym poziomie) odpowiednie operacje.

Wejdź na stronę http://developers.facebook.com/ i stwórz swoją aplikację, wchodząc na zakładkę “Apps” ,a potem klikając na przycisk “Utwórz aplikację”.

Facebook-ap_thumb3

Nazwij swoją aplikację i ją stwórz , a potem zwróć uwagę na jej ID. Będzie ona potrzebna do naszej aplikacji.

image_thumb5

UI aplikacji zawiera proste TextBox-y, które możesz uzupełnić swoim ID. Ja swoją aplikację testową, dla bezpieczeństwa skasuję, więc się nie zdziw jeśli ona nie zadziała.

Kod XAML znajduje się poniżej.

<Page
    x:Class="WinRTFacebookDemo.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:WinRTFacebookDemo"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <Grid Margin="100">
            <Grid.RowDefinitions>
                <RowDefinition Height="25*"/>
                <RowDefinition Height="117*"/>
            </Grid.RowDefinitions>
            <TextBlock Text="FacebookTest" FontSize="65" />
            <Rectangle Height="5" Fill="White" Margin="0,76,0,19" />
            <StackPanel Grid.Row="1" Width="500" HorizontalAlignment="Left"  >
                <TextBlock Text="Facebook Api ID" />
                <TextBox x:Name="Tx_FacebookAppId" Margin="0,0,0,20" Text="189152441188090"  />
                <TextBlock Text="Facebook Api ID" />
                <TextBox x:Name="Tx_Redirect" Margin="0,0,0,20" 
                         Text="https://www.facebook.com/connect/login_success.html" />
                <StackPanel Orientation="Horizontal" >
                    <Button x:Name="btnFacebookLogin" Content="Login by WinRT Code" Margin="0,0,0,20"
                            Click="btnFacebookLogin_Click" />
                    <Button x:Name="btnFacebookSDK_Login" Content="Login by SDK Facebook" Margin="0,0,0,20"
                            Click="btnFacebookSDK_Login_Click" />
                </StackPanel>
                <StackPanel x:Name="StackSendMessage" Visibility="Collapsed" >
                    <TextBlock Text="Twoja wiadomość" />
                    <TextBox x:Name="Tx_Facebook_Message"  Height="207" />
                    <Button x:Name="btnSend" Content="Wyślij" Margin="0,20,0,0" Width="490" 
                            VerticalAlignment="Bottom" Click="btnSend_Click" />
                </StackPanel>
            </StackPanel>
            <WebView Name="WebView1" Grid.Row="1" Loaded="WebView1_Loaded"  Visibility="Collapsed" />
        </Grid>
    </Grid>
</Page>

Po naciśnięciu przycisku zaloguj,  aplikacja wyświetli dodatkowy textbox , na którym napiszemy  wiadomość i ją wyślemy do Facebooka.

image_thumb2[1]

Jak widać aplikacja posiada dwa przyciski logujące, a to dlatego, że możemy zalogować się do Facebooka dwoma sposobami.

Logowanie do Facebook, z przyczyn bezpieczeństwa nie może odbyć się bezpośrednio przez twoją aplikację. Gdyby tak było to każdy mógłby ukraść twoje hasła.

Obecnie wiele serwisów takich jak Twitter, czy Facebook używa OAuth.

OAuth nie wymaga przetrzymywania hasła i loginu wewnątrz aplikacji. Zamiast tego, gdy użytkownik się zaloguje, otrzymujemy token dowodzący ,że logowanie się powiodło. Token przestanie być aktywny jeśli użytkownik się wyloguję bądź nie zaznaczy opcji “zapamiętaj mnie”.

WinRT oferuje nam ciekawą klasę WebAuthentiactionBroker, która pozwoli na zalogowanie się do Facebooka  bez użycia dodatkowych SDK.

WebAuthentiactionBroker

WebAuthenticationBroker zawiera API, które za nas w swojej wbudowanej przeglądarce wyświetli stronę Facebookową i prześle nam rezultat,  gdy użytkownik się zaloguje bądź użytkownik zrezygnuje z logowania,

WebAuthenticationBroker służy do logowania OAuth nie tylko do aplikacji Facebook-owych. W parametrach podajemy adresy logowania, do których ma być przekierowany użytkownik. W żaden sposób nie możemy tego zmienić, tak działa API Facebook-a. Musimy także podać adres, na który użytkownik zostanie przekierowany, po udanym zalogowaniu.

string facebookUrl = "https://www.facebook.com/dialog/oauth?client_id=" + Uri.EscapeDataString(Tx_FacebookAppId.Text)
    + "&redirect_uri=" + Uri.EscapeDataString(Tx_Redirect.Text) + "&scope=read_stream&display=popup&response_type=token";

Uri StartUri = new Uri(facebookUrl);
Uri EndUri = new Uri(Tx_Redirect.Text);

WebAuthenticationResult webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(
                                        WebAuthenticationOptions.None,
                                        StartUri,
                                        EndUri);

W konstruktorze WebAuthenticationBroker podajemy także opcje, które określają jak strona ma być generowana dla użytkownika. Najlepiej zostawić je na NONE.

Po operacji użytkownika dostaniemy obiekt klasy “WebAuthenticationResult” . Z tego obiektu możemy się dowiedzieć jak przebiegł proces autentykacji. Jeśli właściwość Status jest ustawiona Succes , to wiemy, że wszystko przebiegło zgodnie z założeniem.

Token odbieramy z właściwości “ResponseData” . Niestety zawiera on cały zwrócony adres URL, więc musimy go odpowiednio wyciąć aby uzyskać  token.

Cały kod logujący znajduje się poniżej. Jak widać zawiera także obsługę błędów.

MessageDialog md;
private string accessURLtoken;

private async void btnFacebookLogin_Click(object sender, RoutedEventArgs e)
{
         
    if (Tx_FacebookAppId.Text == "")
    {
        md = new MessageDialog("Prosze wpis client id", "Błąd");
        await md.ShowAsync();
        return;
    }
    else if (Tx_Redirect.Text == "")
    {
        md = new MessageDialog("Wpisz adres Callback", "Błąd");
        await md.ShowAsync();
        return;
    }

    StringBuilder sb = new StringBuilder();

    try
    {
        string facebookUrl = "https://www.facebook.com/dialog/oauth?client_id=" + Uri.EscapeDataString(Tx_FacebookAppId.Text)
            + "&redirect_uri=" + Uri.EscapeDataString(Tx_Redirect.Text) + "&scope=read_stream&display=popup&response_type=token";

        Uri StartUri = new Uri(facebookUrl);
        Uri EndUri = new Uri(Tx_Redirect.Text);

        WebAuthenticationResult webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(
                                                WebAuthenticationOptions.None,
                                                StartUri,
                                                EndUri);

        if (webAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success)
        {

            sb.AppendLine("Zalogowany do Facebook przez WinRT");
                    
            string response = webAuthenticationResult.ResponseData;
            int index = response.LastIndexOf("#access_token=", System.StringComparison.Ordinal);
            accessURLtoken = response.Substring(index + "#access_token=".Length);


            StackSendMessage.Visibility = Windows.UI.Xaml.Visibility.Visible;
            btnFacebookLogin.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
            btnFacebookSDK_Login.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
        }
        else if (webAuthenticationResult.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
        {
            sb.AppendLine();
            sb.AppendLine("Error: " + "HTTP Error returned by AuthenticateAsync() : ");
            sb.AppendLine(webAuthenticationResult.ResponseErrorDetail.ToString());
        }
        else
        {
            sb.AppendLine();
            sb.AppendLine("Error: " + "Error returned by AuthenticateAsync() : ");
            sb.AppendLine(webAuthenticationResult.ResponseStatus.ToString());
        }
    }
    catch (Exception Error)
    {
        sb.Append(Error.Message);
    }

    md = new MessageDialog(sb.ToString(), "Opis operacji");
    await md.ShowAsync();
}

W trakcie logowania użytkownik zostanie kompletnie odseparowany od naszej aplikacji. Jak widać okno dialogowe pochłania cały obszar.

image_thumb31

Ten sposób logowania wydaje się być jednak wskazany. Drugi sposób logowania, który zaraz pokażę,  być może łamie jakieś wytyczne co, do aplikacji WinRT

Wysłanie wiadomości

Zalogowaliśmy się do Facebook ale co z tego, jeśli nie możemy nic dalej z tym zrobić.

WinRT nie ma wybudowanej API do wysłania komunikatów do REST-owej aplikacji Facebook-owej. Z drugiej strony nie chcemy czytać dokładnie dokumentacji Facebooka i zagłębiać się w to, jak mamy wysłać te komunikaty.

Pisanie swoich klas nie wchodzi tutaj w grę. Istnieje już gotowe SDK stworzone do tego celu.

image_thumb[2]

Aby dodać gotowe SDK do projektu, wystarczy kliknąć  prawym przyciskiem myszki na katalog “References” i z wywołanego meni kontekstowe wybrać “Manage NuGet Packages”.

Z NuGet-a można pobierać przeróżne dodatkowe biblioteki ale teraz interesuje nas biblioteka “Facebook SDK C#”.

image_thumb[16]

Po dodaniu referencji tego SDK możemy przejść do wysłania wiadomości.

private async void btnSend_Click(object sender, RoutedEventArgs e)
{
    Facebook.FacebookClient d = new Facebook.FacebookClient(accessURLtoken);

    dynamic parameters = new ExpandoObject();
    parameters.message = Tx_Facebook_Message.Text;

    dynamic privacy = new ExpandoObject();
    privacy.value = "ALL_FRIENDS";

    parameters.privacy = privacy;

    dynamic result = await d.PostTaskAsync("me/feed", parameters);

    md = new MessageDialog("Wiadomość wysłana pomyślnie", "Sukcess");
    await md.ShowAsync();
}

Konstruktor klasy FacebookClient potrzebuje tokena, który wcześniej został uzyskany. Potem zgodnie z dokumentacją aplikacji REST Facebookwej podajemy odpowiednie parametry do komunikatu. Dynamiczny obiekt  parameters  i jego właściwości zostaną odpowiednio potem serializowany do JSON-a i przesłane do Facebooka.

We właściwości “message” podajemy treść wiadomości ,a potem, chociaż nie jest to wymagane podajemy stopień prywatności tej przesłanej wiadomości. Jak widać jest to kolejny dynamiczny obiekt.

Słowo kluczowe await dużo upraszcza tutaj kod. Wiadomość zostanie wysłana oczywiście asynchronicznie ale nie zablokuje to w żaden sposób wątku UI. Co więcej po wykonaniu części kodu z “await” dalszy kod zostanie wykonany dalej. Ten kod nie ma obsługi błędu (w wypadku nie wysłania wiadomości np. w przypadku utraty połączenia z Internetem.)

Jak widać poniżej wiadomość testowa została dodana do mojego konta Facebook przez aplikację “UnNamed Test Application”.

image_thumb2

Drugi sposób logowania do Facebook

Bez wbudowanej klasy WinRT  logowanie do Facebook jest trochę bardziej skomplikowane ale metodę wykorzystałem przy pisaniu aplikacji na Windows Phone 7.1.

Korzystając z kontrolki przeglądarki, w sumie zasymuluję poprzedni scenariusz. W czasie wczytywania kontrolki przeglądarki nawiguje ją do facebook-wej strony logowania.

Jeśli użytkownik poprawnie się zalogował token zostanie zapisany.

private const string ExtendedPermissions = "user_about_me,read_stream,publish_stream";

private void WebView1_Loaded(object sender, RoutedEventArgs routedEventArgs)
{
    WebView1.LoadCompleted += WebView1_LoadCompleted;

    var loginUrl = GetFacebookLoginUrl(Tx_FacebookAppId.Text, ExtendedPermissions);
    WebView1.Navigate(loginUrl);
}

private void WebView1_LoadCompleted(object sender, NavigationEventArgs e)
{
     FacebookOAuthResult oauthResult;
    if (!_fb.TryParseOAuthCallbackUrl(e.Uri, out oauthResult))
    {
        return;
    }

    if (oauthResult.IsSuccess)
    {
        var accessToken = oauthResult.AccessToken;
        LoginSucceded(accessToken);
    }
    else
    {
        // user cancelled
    }
}

Pamiętasz dwa przyciski logowania. Drugi przycisk tak naprawdę tylko pokazuje schowaną kontrolkę przeglądarki. Możesz tę   kontrolkę umieścić w taki sposób, że nie będzie blokowała reszty UI. Pytanie brzmi, czy jest to zgodne z wytycznymi aplikacji WINRT.

Jak poniżej widać napis “FacebookTest” jest widoczny, w poprzednim przykładzie tak nie było.

aaaxd_thumb[1]

Metoda GetFacebookLoginUrl tworzy tak naprawdę odpowiednie zapytanie HTTP do przeglądarki, która wyświetla nam okno logowania.

private void btnFacebookSDK_Login_Click(object sender, RoutedEventArgs e)
{
    WebView1.Visibility = Windows.UI.Xaml.Visibility.Visible;
}

private Uri GetFacebookLoginUrl(string appId, string extendedPermissions)
{
    dynamic parameters = new ExpandoObject();
    parameters.client_id = appId;
    parameters.redirect_uri = "https://www.facebook.com/connect/login_success.html";
    parameters.response_type = "token";
    parameters.display = "popup";

    if (!string.IsNullOrWhiteSpace(extendedPermissions))
    {
         parameters.scope = extendedPermissions;
    }

    return _fb.GetLoginUrl(parameters);
}

private async void LoginSucceded(string accessToken)
{
    accessURLtoken = accessToken;

    md = new MessageDialog("Zalogowany do Facebook przez SDK", "Sukcess");
    await md.ShowAsync();
    WebView1.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
    btnFacebookLogin.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
    btnFacebookSDK_Login.Visibility = Windows.UI.Xaml.Visibility.Collapsed;

    StackSendMessage.Visibility = Windows.UI.Xaml.Visibility.Visible;
}

Jeśli logowania zakończyło się sukcesem informuje  o tym  użytkownika za pomocą MessageBox-a ….hhmyn przepraszam MessageDialog-a.

Chowa odpowiedzenie kontrolki oraz przypisuje odebrany token w odpowiednie miejsce. To wszystko. Jeśli potrzebujesz więcej informacji proszę skorzystaj z dokumentacji Facebook SDK.

Pobierz Kod