2013年7月16日 星期二

[Android][Facebook][中文化] Facebook SDK for Android Tutorial - Authenticate with Facebook Login 中文化

Authenticate with Facebook Login

前言: 

標題連結是 fackbook 官方的教學, 十分詳細。 我在邊練習範例的過程中將其中文化, 如有翻譯上的問題,還望指教。練習這個範例前, 別忘記 facebook 的 APP 註冊以及Project 相關的設定。 以下為Project 設定簡述:

1. 在 strings.xml,  加入app_id <string name="app_id">xxxxxxxxxxxxxxxxx</string>
2. 在 AndroidManifest.xml 加入 use permission , meta-data 與一個 facebook 的 activity
     *android.permission.INTERNET
     *加入  'Meta Data'  其 Name :  com.facebook.sdk.ApplicationId, 其 value : @string/app_id
     *加入 'Activity' 其 Name:  com.facebook.LoginActivity
可以參考我上篇文 :  Facebook SDK 安裝以及使用


這個教學將帶你走過, 如何利用Android 的 Facebook SDK ,  在你的app 裡加入Facebook login 。 
一但你完成下列步驟, 你的 app 將可以讓人們使用它來完成Facebook 登入流程, 因而得到授權。

我們將介紹Facebook SDK 的一個核心主題 : Session。 這個 Session 物件是用來授權人們來使用你的 app , 並且管理 Facebook 登入流程和 app 的 session。 任何需要授權的 Facebook API calls 都需要使用到一個 Session 的 instance。  app 可以建立 Session 的 instances 然後可以管理它們, Session class 則是提簡化每次只有一個人登入狀況下的一般的登入場景。


此外,  Facebook SDK 也提供了 UiLifecycleHelper class,  讓你的 activity 或是 fragment 可以使用它 來做 session 的管理。 UiLifecycleHelper class 可以管理授權流程, 然後利用 Session.StatusCallback callback 的 implementation 監視著 session state。在 Session.StatusCallback 這個callback 的 implementation 中, 你會將你的 activity 或是 fragment 傳替給 UiLifecycleHelper 的 instatnce, 因此 在Session.StatusCallback callback 的 implementation 裡 ,可以設定如何對 session state 的改變做出對應的行動。 

LoginButton 是一個Facebook SDK 提供的客製的 Button , 你可以在你的授權 UI 上使用它。 它可以維護 session state 。 使用LoginButton 和  UiLifecycleHelper 可以幫助你快速的將 Facebook 授權流程加入你的 app 裡。

這個教學將帶你走過下列步驟: 
  Step 1:  設定 UI (Set up the UI)
  Step 2:  整合授權邏輯 (Wire up the authentication logic)
  Step 3:  加入登出流程 (Add the logout flow)
  下一步 (Next steps )
  相關範例 (Related samples)

這個範例的 Code來自於 Scrumptious sample Android app
注意: 在你開始範例前, 請先設定好你的App set up your basic Facebook app.

------------------------------------------------------------------------------------------------------------ 步驟 1:  設定 UI (Set up the UI) 

你的 app 的登入/授權流程會有一個MainActivity 和兩個 Fragments。這兩個 fragments 分別處理沒有授權的UI 與有授權UI。沒有授權式 UI 包含一個app 的名稱和一個 login 按鈕。授權的UI 則展示了個人化的畫面和一連串幫助建立個人化頁面的設定。在這個練習中, 你不需要加入完整的授權的 UI,  你只需要加入部份的授權UI,讓你也可以在程式中實現 (implement) 登出的功能。而完整的授權的UI會在另外的個範例裡。

你將會產生一個 fragment 叫做 SplashFragmnet 來代表沒有授權式 UI。並且產生另一個 SelectionFragment fragment 來代表授權後的UI。

步驟 1a: 設置 Login UI

首先 將使用login 流程會用道的圖檔拷貝道你的project裡。  這些圖檔在 facebook sdk 裡所提供的範例Scrumptious 的 res/drawable 裡。 將圖檔烤貝並放在你 project的 res/drawable 裡。如果你的 project 沒有 drawable 這個檔案夾, 你就要先建立一個。 
接下來, 建立一個 fragment class 給沒有授權的 UI使用。將它命名為 SplashFragment 並且讓其成為 fragment class 的子類別 。 為了可以支援舊版的安卓系統, 因此選擇成為android.support.v4.app.Fragment 的 Fragment class 的子類別。
接下來要建立給這個 fragment 使用的layout。 在 res/layout 目錄下建立一個叫 "splash.
xml"的xml 檔。 並請使用LinearLayout 做為 root element。 將然後將一個 app 圖式, app 名稱標籤, app 描述, 還有一個 login 的按鈕的UI物件加入這個 Layout 裡。 這裡的 login 
button 是使用了 Facebook SDK 客製的 LoginButton, 其功能可以記錄追蹤它自己的狀態。 
你的xml 看起來是這樣。


<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:orientation="vertical"
              android:background="#303040" >

    <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp"
            android:gravity="center_horizontal"
            android:orientation="horizontal" >
        <ImageView
                android:id="@+id/splash_icon"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_marginLeft="10dp"
                android:gravity="center"
                android:src="@drawable/icon" />
        <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:gravity="center"
                android:textColor="#AFDEFE"
                android:textSize="28sp"
                android:typeface="serif"
                android:textStyle="italic"
                android:text="@string/app_name" />
    </LinearLayout>


    <TextView
            android:id="@+id/profile_name"
            android:layout_width="174dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="35dp"
            android:lines="2"
            android:textSize="17sp"
            android:text="@string/get_started"
            android:layout_gravity="center_horizontal"
            android:gravity="center_horizontal"/>

    <com.facebook.widget.LoginButton
            android:id="@+id/login_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="30dp"
            android:layout_marginBottom="30dp" />

</LinearLayout>


你應該會看到跟strings 參考還未定義有關的有錯誤訊息。 解決這些錯誤訊息, 開啟 res/values/strings.xml 檔然後將下列加入string resources :

<string name="get_started">To get started, log in using Facebook</string>

在strings resources 裡, 你也應該要修改 app_name 的值好讓和整個範例吻合 :

<string name="app_name">Scrumptious</string>

接下來開啟, SplashFragment class 然後 override onCreateView() 方法, 設定view 為剛剛設定的Layout:

@Override
public View onCreateView(LayoutInflater inflater, 
        ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.splash, 
            container, false);
    return view;
}

假如出現錯誤, import 遺失的 libraries。 在 eclipse 上按 -
    Mac: Command-Shift-O
    Windows: Control-Shift-O


步驟 1b: 設置授權 UI

在你的 package 裡建立一個名為 "SelectionFragment" 的class 給授權 UI 使用。將這個class 設為Fragment class 的子類別。 接下來, 在 res/layout 裡, 建立一個叫 "selection.xml" 的 layout 給這個 fragment 使用。 使用 LinearLayout 當做 root element。你將建立基本的UI功能來確認人們已被登入。 在這個 project 的後期, 你將加入包括名稱與一連串像她們喜歡吃啥麼這樣的選項。 將下列 view 加在你的 LinearLayout 容器(containter) 裡:


<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="10dp"
    android:layout_gravity="center"
    android:textColor="#333"
    android:textSize="18sp"
    android:text="Welcome, you are now logged in."  />

乎視關於 hard coded strings 的警告, 把這當作只是暫時的 UI。
接下來打開 SelectionFragment class 然後複寫 onCreateView() 將 view 設為剛剛的設定UI。

@Override
public View onCreateView(LayoutInflater inflater, 
        ViewGroup container, Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);
    View view = inflater.inflate(R.layout.selection, 
            container, false);
    return view;
}


加入下行給Debug 使用

private static final String TAG = "SelectionFragment";

步驟 1c : 設定主要UI

接下來要設定 main UI 來放置上面設定好的兩個UI。修改 res/layout/main.xml 檔, 然後將其改成LinearLayout 做為 parent 元件的樣式, 然後再將 2 個 fragment 加入裡面。你的 Layout 應該像這樣:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <fragment android:name="com.facebook.scrumptious.SelectionFragment"
          android:id="@+id/selectionFragment"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />
    <fragment android:name="com.facebook.scrumptious.SplashFragment"
          android:id="@+id/splashFragment"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />
</LinearLayout>


上面 android:name 的值應該要對應到你的 class 的全名 。使用你的 package 名取代 com.facebook.scrumptious 。

到此 facebook 基本 UI 已經完成。


步驟 2 : 整合授權邏輯

在這個步驟,你將整合授權邏輯,當使者還沒被授權時,顯示 Login UI , 當使用者被授權了則顯示基本的歡迎畫面。 
下面是你將會implement 的邏輯流程: 

* 找到 main activity 設置的 fragments 然後在初始時隱藏他們
* 在 main activity, 設置一個 Session.StatusCallback listener。 建立一個 UiLifecycleHelper instatnce 然後將listener 傳給這個instance 的 constructor。
* 在 listener 的 implementation 裡, 呼叫一個對應 session state改變的方法, 然後顯示出與其相連的fragment。 在 相關的fragment 顯示前, fragment 的 back stack 應該要已經被清除了。對於session state 的改變的回應, 只有在 activity  active 的時候。 
*在一個 fragment 被resumed 時, 檢查 使用者的session state 然後顯示已授權的UI或未授權的 UI 

開始設定 fragment。打開  MainActivity class , 設定 MainActivity class 為  FragmentActivity class 子類別而不是原先的 Activity 類別。

public class MainActivity extends <del>Activity</del> FragmentActivity {

定義一個 fragments 的私有陣列給 main activity 使用。 也定義 array index 常數來操作這些 fragments
private static final int SPLASH = 0;
private static final int SELECTION = 1;
private static final int FRAGMENT_COUNT = SELECTION +1;

private Fragment[] fragments = new Fragment[FRAGMENT_COUNT];

當 import 時如有出現fragment 的選擇, 請選android.support.v4.app.Fragment。

現在,  onCreate 將 fragments 隱藏起來 : 

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    FragmentManager fm = getSupportFragmentManager();
    fragments[SPLASH] = fm.findFragmentById(R.id.splashFragment);
    fragments[SELECTION] = fm.findFragmentById(R.id.selectionFragment);

    FragmentTransaction transaction = fm.beginTransaction();
    for(int i = 0; i < fragments.length; i++) {
        transaction.hide(fragments[i]);
    }
    transaction.commit();
}

如果import FragmentManager 和 FragmentTransaction 時出現選項, 請選擇
android.support.v4.app.FragmentTransaction 和  android.support.v4.app.FragmentManager.

接下來你將 implement session management 相關的 code 。 首先定義一個當session state 改變時呼叫的方法: 

private void showFragment(int fragmentIndex, boolean addToBackStack) {
    FragmentManager fm = getSupportFragmentManager();
    FragmentTransaction transaction = fm.beginTransaction();
    for (int i = 0; i < fragments.length; i++) {
        if (i == fragmentIndex) {
            transaction.show(fragments[i]);
        } else {
            transaction.hide(fragments[i]);
        }
    }
    if (addToBackStack) {
        transaction.addToBackStack(null);
    }
    transaction.commit();
}

定義一個 private 方法負責顯示要求的某個 fragment 並且將其他的 fragments 隱藏。


接下來定義一個flag, 這個 flag 可以用來判斷一個可見 activity 。這個 flag 用來當做 enable session state 改變的檢查。

private boolean isResumed = false;

接下來, 當 activity 被暫停或又重啟時, 清除或設定這個flag 。

@Override
public void onResume() {
    super.onResume();
    isResumed = true;
}

@Override
public void onPause() {
    super.onPause();
    isResumed = false;
}


接下來, 定義一個在 session state 改變時會被呼叫的 private 方法。這個方法基於使用者的授權狀態, 呼叫顯示相關的fragament 。 這個方法藉由 isResume flag , 使其只會在 activity visible 的情況下處理 UI 的改變。在這方法裡, 在呼叫合適的fragmet 前, Fragment 的 backstack 會先被清除。

private void onSessionStateChange(Session session, SessionState state, Exception exception) {
    // Only make changes if the activity is visible
    if (isResumed) {
        FragmentManager manager = getSupportFragmentManager();
        // Get the number of entries in the back stack
        int backStackSize = manager.getBackStackEntryCount();
        // Clear the back stack
        for (int i = 0; i < backStackSize; i++) {
            manager.popBackStack();
        }
        if (state.isOpened()) {
            // If the session state is open:
            // Show the authenticated fragment
            showFragment(SELECTION, false);
        } else if (state.isClosed()) {
            // If the session state is closed:
            // Show the login fragment
            showFragment(SPLASH, false);
        }
    }
}


如果在import Session 跟 SessionState 時需要選擇, 請選擇com.facebook.Session。


接下來需要處理在 fragment 初被生成時的情況, 然後 未授權跟授權的 UI 需要適當的設置。Override  onResumeFragments() 來處理 session 的改變 :

@Override
protected void onResumeFragments() {
    super.onResumeFragments();
    Session session = Session.getActiveSession();

    if (session != null && session.isOpened()) {
        // if the session is already open,
        // try to show the selection fragment
        showFragment(SELECTION, false);
    } else {
        // otherwise present the splash screen
        // and ask the person to login.
        showFragment(SPLASH, false);
    

現在使用 UiLifecycleHelper 來追蹤 session 以及 觸發 session state 改變的 listener。 首先, 建立一個私有變數給  UiLifecycleHelper 物件, 以及一個監聽 session session 改變的 listener : 

private UiLifecycleHelper uiHelper;
private Session.StatusCallback callback = 
    new Session.StatusCallback() {
    @Override
    public void call(Session session, 
            SessionState state, Exception exception) {
        onSessionStateChange(session, state, exception);
    }
};

Session.StatusCallback implementation overrides call() 方法並且呼叫了 你先前定義好的 onSessionStateChange()方法。
接下來在 onCreate() 裡, 建立一個 UiLifecycleHelper 的 instance 並把它傳到 listner 裡:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    uiHelper = new UiLifecycleHelper(this, callback);
    ...


接下來, 呼叫 UiLifecycleHelper activity lifecycle 方法。這些方法式追蹤 session 所必要的。 

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    uiHelper = new UiLifecycleHelper(this, callback);
    uiHelper.onCreate(savedInstanceState);
    ....
}

@Override
public void onResume() {
    super.onResume();
    uiHelper.onResume();
    isResumed = true;
}

@Override
public void onPause() {
    super.onPause();
    uiHelper.onPause();
    isResumed = false;
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    uiHelper.onActivityResult(requestCode, resultCode, data);
}

@Override
public void onDestroy() {
    super.onDestroy();
    uiHelper.onDestroy();
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    uiHelper.onSaveInstanceState(outState);
}


現在 build / run 你的 app , 確保沒有錯誤。 你應該看到一個登入畫面。 當你按下登入按鈕時, 你應該要看到核許(Permission) 的對話框。 一但你授權這個 app, 你應該可以看到一個歡迎畫面。



--------------------------------------------------------------------------------------------------

步驟 3 : 加入登出流程


在這個步驟, 你將會給使用你的 app 的人可以登出並且清除他們的 Facebook session 資訊。
你將使用 UserSettingsFragment class, 這個 class 會提供讓人們登出的 UI。這個 fragment 會顯示 login 或是 logout 按鈕決定在這個 app 的 session state 。 假如, 使用這個 app 的人被授權了, 它就會顯示這個人的檔案圖像以及名稱。

在這個範例, 當人們被授權而且按下裝置的 menu 按鈕時, 你將會設定你的 main activity 來放置這個 fragment 。 你將加入只在SelectonFragment activity 時, 展示出這個 menu 選項的code 。 SelectionFragment 會被顯示出來當使用者登入時。

下面是你將 implement 的功能 :

*將 UserSettingsFragment class 加到 main layout.
*加入額外的常數給 settings fragment 使用.
*在onCreate()裡隱藏 settings fragment
*Override onPrepareOptionsMenu() 方法以達成,基於授權狀態或是 SelectionFragment 的顯示與否, 來顯示或隱藏 menu 。
*Override onOptionsItemSelected() 方法以達成, 當settings menu 選像被選擇時, 顯示 UserSettingsFragment fragment。 

 打開你的 main layout 檔, res/layout/main.xml , 加入 UserSettingsFragment fragment:
....
    <fragment android:name="com.facebook.widget.UserSettingsFragment"
          android:id="@+id/userSettingsFragment"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />
</LinearLayout>


打開你的 main activity class 加入一個新的私有常數給 settings fragment:

private static final int SETTINGS = 2;

接下來, 修改 fragment count 變數, 因為現在已經外加了 setting fragment :

<del>private static final int FRAGMENT_COUNT = SELECTION +1;</del>
private static final int FRAGMENT_COUNT = SETTINGS +1;

接下來加入一個私有變數代表 menu item 給 setting fragment。你將使用這個來觸發 UserSettingsFragment 的顯示

private MenuItem settings;

接下來, 修改 onCreate() 將 UserSettingsFragment  物件加入 fragment array:

....
fragments[SELECTION] = fm.findFragmentById(R.id.selectionFragment);
fragments[SETTINGS] = fm.findFragmentById(R.id.userSettingsFragment);

在接下來, 在 res/values/string.xml 加入一個新的 string  給你的 menu option 使用

<string name="settings">Settings</string>

現在複寫 onPrepareOptionsMenu() 方法, 準備 options menu 的顯示:

@Override
public boolean onPrepareOptionsMenu(Menu menu) {
    // only add the menu when the selection fragment is showing
    if (fragments[SELECTION].isVisible()) {
        if (menu.size() == 0) {
            settings = menu.add(R.string.settings);
        }
        return true;
    } else {
        menu.clear();
        settings = null;
    }
    return false;
}


最後, 覆寫 onOptionsItemSelected 方法來顯示當 setting menu 被按壓後的 UserSettingsFragment顯示:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (item.equals(settings)) {
        showFragment(SETTINGS, true);
        return true;
    }
    return false;
}

注意: 假如你前面有定義 onCreateOptionsMenu() 方法的話, 你不需要這個方法,去除這個方法。這個方法可能在你創建 project 時被自動加入。

Build 和 run project, 確認沒有任何的錯誤。一但授權, 你可以按壓下 menu option,  然後應該會出現 Settings 的 menu 選項。 按下 Settings ,  你的名子與profile 圖片就會出現,  然後還有一個 logout 按鈕 。按下 logout button 應將你帶回 login 畫面。


---------------------------------------------------------------------------------------------------------

下一步

學習如何個人化你的app 給登入的使用者

相關範例

*SessionLoginSample
*HelloFacebookSample


1 則留言:

  1. 你好 我照著做
    登入玩 確認授權後
    還是直接回到登入前的頁面

    請問可以分享你的完整code嗎 謝謝

    回覆刪除