2013-06-24 2 views
14

Я разрабатываю приложение для Android, которое должно интегрироваться с Facebook. Я ознакомился с официальным гидом и несколькими другими гидами, и я думаю, что все правильно реализовано с помощью фреймворка Facebook.Фрагмент Facebook-Facebook закрывает приложение, используя Facebook SDK 3.0.1

Однако SDK успешно завершает вход в систему только в первый раз. Если я выйду из системы и повторю попытку, приложение просто закрывается без каких-либо исключений. То же самое происходит, если я убью свое приложение и запустил его из списка приложений.

Я могу воспроизвести его как в эмуляторе, так и на реальном устройстве (Nexus 7).

LoginActivity.java:

package com.everporter.everporter; 

import java.io.BufferedOutputStream; 
import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.net.HttpURLConnection; 
import java.net.URL; 

import org.json.JSONException; 
import org.json.JSONObject; 

import com.everporter.everporter.FBLoginFragment.OnFBAccessTokenPass; 

import android.animation.Animator; 
import android.animation.AnimatorListenerAdapter; 
import android.annotation.TargetApi; 
import android.app.AlertDialog; 
import android.content.Context; 
import android.content.DialogInterface; 
import android.content.Intent; 
import android.content.SharedPreferences; 
import android.os.AsyncTask; 
import android.os.Build; 
import android.os.Bundle; 
import android.support.v4.app.FragmentActivity; 
import android.telephony.PhoneNumberUtils; 
import android.text.TextUtils; 
import android.util.Log; 
import android.view.KeyEvent; 
import android.view.Menu; 
import android.view.View; 
import android.view.inputmethod.EditorInfo; 
import android.widget.EditText; 
import android.widget.TextView; 

/** 
* Activity which displays a login screen to the user, offering registration as 
* well. 
*/ 
public class LoginActivity extends FragmentActivity 
    implements OnFBAccessTokenPass { 

    /** 
    * The default email to populate the email field with. 
    */ 
    public static final String EXTRA_EMAIL = "com.example.android.authenticatordemo.extra.EMAIL"; 
    private static final String REST_METHOD_NAME = "/login/"; 
    private static final String SOCIAL_REST_METHOD_NAME = "/validation/"; 
    private static final String TAG = "UserLoginTask"; 
    private static final String FB_TAG = "UserFacebookLoginTask"; 

    /** 
    * Keep track of the login task to ensure we can cancel it if requested. 
    */ 
    private UserLoginTask mAuthTask = null; 
    private UserFacebookLoginTask mFacebookAuthTask = null; 
    private String mFacebookAccessToken; 
    private String mFacebookUserId; 

    // API URL read from the settings (set in main activity) 
    private String mRestApiUrl; 
    private int mConnectionTimeout; 
    private int mReadTimeout; 
    private SharedPreferences mPrefs; 

    // Error message set by the async task, if the error does happen indeed 
    private String mAsyncTaskErrorMsg; 

    // Values for email and password at the time of the login attempt. 
    private String mEmail; 
    private String mPassword; 
    private String mSessionCookie; // our session 

    // UI references. 
    private EditText mEmailView; 
    private EditText mPasswordView; 
    private View mLoginFormView; 
    private View mLoginStatusView; 
    private TextView mLoginStatusMessageView; 


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

     setContentView(R.layout.activity_login); 

     mPrefs = getSharedPreferences("settings", MODE_PRIVATE); 
     mRestApiUrl = mPrefs.getString("api_url", ""); 
     mConnectionTimeout = mPrefs.getInt("connection_timeout", 3000); 
     mReadTimeout = mPrefs.getInt("read_timeout", 3000); 

     // Set up the login form. 
     mEmail = getIntent().getStringExtra(EXTRA_EMAIL); 
     mEmailView = (EditText) findViewById(R.id.email); 
     mEmailView.setText(mEmail); 

     mPasswordView = (EditText) findViewById(R.id.password); 
     mPasswordView 
       .setOnEditorActionListener(new TextView.OnEditorActionListener() { 
        @Override 
        public boolean onEditorAction(TextView textView, int id, 
          KeyEvent keyEvent) { 
         if (id == R.id.login || id == EditorInfo.IME_NULL) { 
          attemptLogin(); 
          return true; 
         } 
         return false; 
        } 
       }); 

     mLoginFormView = findViewById(R.id.login_form); 
     mLoginStatusView = findViewById(R.id.login_status); 
     mLoginStatusMessageView = (TextView) findViewById(R.id.login_status_message); 

     findViewById(R.id.sign_in_button).setOnClickListener(
       new View.OnClickListener() { 
        @Override 
        public void onClick(View view) { 
         attemptLogin(); 
        } 
       }); 
    } 

    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 
     super.onCreateOptionsMenu(menu); 
     getMenuInflater().inflate(R.menu.login, menu); 
     return true; 
    } 

    /** 
    * Handle Facebook login 
    */ 
    @Override 
    public void onFBAccessTokenPass(String accessToken, String uid) { 
     // we are passed Facebook access token, successful login 
     Log.i(TAG, "Facebook access token: " + accessToken); 
     mFacebookAccessToken = accessToken; 
     mFacebookUserId = uid; 

     // Show a progress spinner, and kick off a background task to 
     // sent the FB token to the server. 
     mLoginStatusMessageView.setText(R.string.login_progress_signing_in); 
     showProgress(true); 
     mFacebookAuthTask = new UserFacebookLoginTask(this); 
     mFacebookAuthTask.execute((Void) null); 

    } 

    /** 
    * Attempts to sign in or register the account specified by the login form. 
    * If there are form errors (invalid email, missing fields, etc.), the 
    * errors are presented and no actual login attempt is made. 
    */ 
    public void attemptLogin() { 
     if (mAuthTask != null) { 
      return; 
     } 

     // Reset errors. 
     mEmailView.setError(null); 
     mPasswordView.setError(null); 
     mAsyncTaskErrorMsg = ""; 

     // Store values at the time of the login attempt. 
     mEmail = mEmailView.getText().toString(); 
     mPassword = mPasswordView.getText().toString(); 

     boolean cancel = false; 
     View focusView = null; 

     // Check for a valid password. 
     if (TextUtils.isEmpty(mPassword)) { 
      mPasswordView.setError(getString(R.string.error_field_required)); 
      focusView = mPasswordView; 
      cancel = true; 
     } else if (mPassword.length() < 4) { 
      mPasswordView.setError(getString(R.string.error_invalid_password)); 
      focusView = mPasswordView; 
      cancel = true; 
     } 

     // Check for a valid email address or phone number. 
     if (TextUtils.isEmpty(mEmail)) { 
      mEmailView.setError(getString(R.string.error_field_required)); 
      focusView = mEmailView; 
      cancel = true; 
     } else if (!android.util.Patterns.EMAIL_ADDRESS.matcher(mEmail).matches() && 
       !PhoneNumberUtils.isGlobalPhoneNumber(mEmail)) { 
      mEmailView.setError(getString(R.string.error_invalid_email)); 
      focusView = mEmailView; 
      cancel = true; 
     } 

     if (cancel) { 
      // There was an error; don't attempt login and focus the first 
      // form field with an error. 
      focusView.requestFocus(); 
     } else { 
      // Show a progress spinner, and kick off a background task to 
      // perform the user login attempt. 
      mLoginStatusMessageView.setText(R.string.login_progress_signing_in); 
      showProgress(true); 
      mAuthTask = new UserLoginTask(this); 
      mAuthTask.execute((Void) null); 
     } 
    } 

    /** 
    * Shows the progress UI and hides the login form. 
    */ 
    @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) 
    private void showProgress(final boolean show) { 
     // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow 
     // for very easy animations. If available, use these APIs to fade-in 
     // the progress spinner. 
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { 
      int shortAnimTime = getResources().getInteger(
        android.R.integer.config_shortAnimTime); 

      mLoginStatusView.setVisibility(View.VISIBLE); 
      mLoginStatusView.animate().setDuration(shortAnimTime) 
        .alpha(show ? 1 : 0) 
        .setListener(new AnimatorListenerAdapter() { 
         @Override 
         public void onAnimationEnd(Animator animation) { 
          mLoginStatusView.setVisibility(show ? View.VISIBLE 
            : View.GONE); 
         } 
        }); 

      mLoginFormView.setVisibility(View.VISIBLE); 
      mLoginFormView.animate().setDuration(shortAnimTime) 
        .alpha(show ? 0 : 1) 
        .setListener(new AnimatorListenerAdapter() { 
         @Override 
         public void onAnimationEnd(Animator animation) { 
          mLoginFormView.setVisibility(show ? View.GONE 
            : View.VISIBLE); 
         } 
        }); 
     } else { 
      // The ViewPropertyAnimator APIs are not available, so simply show 
      // and hide the relevant UI components. 
      mLoginStatusView.setVisibility(show ? View.VISIBLE : View.GONE); 
      mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); 
     } 
    } 

    /** 
    * Represents an asynchronous login/registration task used to authenticate 
    * the user. 
    */ 
    public class UserLoginTask extends AsyncTask<Void, Void, Boolean> { 

     private Context mContext; 

     public UserLoginTask(Context context) { 
      this.mContext = context; 
     } 

     @Override 
     protected Boolean doInBackground(Void... params) { 
      Log.i(TAG, "Begin auth..."); 

      HttpURLConnection conn = null; 
      StringBuilder sb = new StringBuilder(); 
      BufferedOutputStream printout; 
      boolean signedInOk = false; 
      try { 

       URL url = new URL(mRestApiUrl + REST_METHOD_NAME); 
       conn = (HttpURLConnection) url.openConnection(); 
       conn.setDoInput(true); 
       conn.setDoOutput(true); 
       conn.setUseCaches(false); 
       conn.setConnectTimeout(mConnectionTimeout); 
       conn.setReadTimeout(mReadTimeout); 
       conn.setRequestMethod("POST"); 
       conn.setRequestProperty("Content-Type","application/json; charset=utf-8"); 
       conn.connect(); 
       //Create JSONObject here 
       JSONObject jsonParam = new JSONObject(); 
       jsonParam.put("password", mPassword); 
       jsonParam.put("username", mEmail); 
       // Send POST output. 
       printout = new BufferedOutputStream(conn.getOutputStream()); 
       printout.write(jsonParam.toString().getBytes("UTF-8")); 
       printout.flush(); 
       printout.close(); 

       int httpCode = conn.getResponseCode(); 
       if (httpCode == HttpURLConnection.HTTP_OK) { 
        BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8")); 
        String line = null; 
        while ((line = br.readLine()) != null) { 
         sb.append(line + "\n"); 
        } 
        br.close(); 
        Log.i(TAG, sb.toString()); 
        JSONObject jsonResponse = new JSONObject(sb.toString()); 
        int resultCode = jsonResponse.getInt("code"); 
        if (resultCode == 0) { 
         // get the session id 
         String cookie = conn.getHeaderField("Set-Cookie"); 
         if (cookie != null) { 

          Log.i(TAG, cookie); 
          signedInOk = true; 
          mSessionCookie = cookie; 
          Log.i(TAG, "Authenticated"); 
         } 
        } else if (resultCode == 2 || resultCode == 3) { 
         signedInOk = false; // wrong login and password 
        } else { 
         // get the error message 
         if (!jsonResponse.isNull("message")) { 
          mAsyncTaskErrorMsg = jsonResponse.getString("message"); 
         } else { 
          mAsyncTaskErrorMsg = getString(R.string.internal_error); 
         } 
        } 
       } else{ 
        Log.e(TAG, conn.getResponseMessage()); 
       } 
      } catch (JSONException e) { 
       Log.e(TAG, e.getMessage()); 
       mAsyncTaskErrorMsg = e.getMessage(); 
      } catch (IOException e) { 
       Log.e(TAG, e.getMessage()); 
       mAsyncTaskErrorMsg = e.getMessage(); 
      } finally { 
       if (conn != null) 
        conn.disconnect(); 
      } 

      Log.i(TAG, "End auth..."); 
      return signedInOk; 
     } 

     @Override 
     protected void onPostExecute(final Boolean success) { 
      mAuthTask = null; 
      showProgress(false); 

      if (success) { 
       // store the userId 
       SharedPreferences.Editor ed = mPrefs.edit(); 
       ed.putString("session_cookie", mSessionCookie); 
       ed.putLong("session_cookie_time", System.currentTimeMillis()); 
       ed.apply(); 
       // start main 
       Intent intent = new Intent(mContext, MainActivity.class); 
       startActivity(intent); 
       finish(); 
      } else { 
       if (!mAsyncTaskErrorMsg.isEmpty()) { 
        // show the error message 
        new AlertDialog.Builder(mContext) 
        .setTitle("Error occured") 
        .setMessage(mAsyncTaskErrorMsg) 
        .setNeutralButton(android.R.string.ok, 
        new DialogInterface.OnClickListener() { 
         public void onClick(DialogInterface dialog, int id) { 
          dialog.cancel(); 
         } 
        }).show(); 
       } else { 
        mPasswordView 
          .setError(getString(R.string.error_incorrect_password)); 
        mPasswordView.requestFocus(); 
       } 
      } 
     } 

     @Override 
     protected void onCancelled() { 
      mAuthTask = null; 
      showProgress(false); 
     } 
    } 

    /** 
    * Represents an asynchronous login task used to authenticate 
    * the user via Facebook. 
    */ 
    public class UserFacebookLoginTask extends AsyncTask<Void, Void, Boolean> { 

     private Context mContext; 

     public UserFacebookLoginTask(Context context) { 
      this.mContext = context; 
     } 

     @Override 
     protected Boolean doInBackground(Void... params) { 
      Log.i(FB_TAG, "Begin Facebook validation..."); 

      HttpURLConnection conn = null; 
      StringBuilder sb = new StringBuilder(); 
      BufferedOutputStream printout; 
      boolean signedInOk = false; 
      try { 

       URL url = new URL(mRestApiUrl + SOCIAL_REST_METHOD_NAME); 
       conn = (HttpURLConnection) url.openConnection(); 
       conn.setDoInput(true); 
       conn.setDoOutput(true); 
       conn.setUseCaches(false); 
       conn.setConnectTimeout(mConnectionTimeout); 
       conn.setReadTimeout(mReadTimeout); 
       conn.setRequestMethod("POST"); 
       conn.setRequestProperty("Content-Type","application/json; charset=utf-8"); 
       conn.connect(); 
       //Create JSONObject here 
       JSONObject jsonParam = new JSONObject(); 
       jsonParam.put("provider", "facebook"); 
       jsonParam.put("token", mFacebookAccessToken); 
       jsonParam.put("uid", mFacebookUserId); 
       // Send POST output. 
       printout = new BufferedOutputStream(conn.getOutputStream()); 
       printout.write(jsonParam.toString().getBytes("UTF-8")); 
       printout.flush(); 
       printout.close(); 

       int httpCode = conn.getResponseCode(); 
       if (httpCode == HttpURLConnection.HTTP_OK) { 
        BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8")); 
        String line = null; 
        while ((line = br.readLine()) != null) { 
         sb.append(line + "\n"); 
        } 
        br.close(); 
        Log.i(FB_TAG, sb.toString()); 
        JSONObject jsonResponse = new JSONObject(sb.toString()); 
        int resultCode = jsonResponse.getInt("code"); 
        if (resultCode == 0) { 
         // get the session id 
         String cookie = conn.getHeaderField("Set-Cookie"); 
         if (cookie != null) { 

          Log.i(FB_TAG, cookie); 
          signedInOk = true; 
          mSessionCookie = cookie; 
          Log.i(FB_TAG, "Authenticated"); 
         } 
        } else if (resultCode == 2 || resultCode == 3) { 
         signedInOk = false; // no such user in the database 
         mAsyncTaskErrorMsg = ""; 
        } else { 
         // get the error message 
         if (!jsonResponse.isNull("message")) { 
          mAsyncTaskErrorMsg = jsonResponse.getString("message"); 
         } else { 
          mAsyncTaskErrorMsg = getString(R.string.internal_error); 
         } 
        } 
       } else{ 
        Log.e(FB_TAG, conn.getResponseMessage()); 
       } 
      } catch (JSONException e) { 
       Log.e(FB_TAG, e.getMessage()); 
       mAsyncTaskErrorMsg = e.getMessage(); 
      } catch (IOException e) { 
       Log.e(FB_TAG, e.getMessage()); 
       mAsyncTaskErrorMsg = e.getMessage(); 
      } finally { 
       if (conn != null) 
        conn.disconnect(); 
      } 

      Log.i(FB_TAG, "End auth..."); 
      return signedInOk; 
     } 

     @Override 
     protected void onPostExecute(final Boolean success) { 
      mFacebookAuthTask = null; 
      showProgress(false); 

      if (success) { 
       // store the userId 
       SharedPreferences.Editor ed = mPrefs.edit(); 
       ed.putString("session_cookie", mSessionCookie); 
       ed.putLong("session_cookie_time", System.currentTimeMillis()); 
       ed.apply(); 
       // start main 
       Intent intent = new Intent(mContext, MainActivity.class); 
       startActivity(intent); 
       finish(); 
      } else { 
       if (!mAsyncTaskErrorMsg.isEmpty()) { 
        // show the error message 
        new AlertDialog.Builder(mContext) 
        .setTitle("Error occured") 
        .setMessage(mAsyncTaskErrorMsg) 
        .setNeutralButton(android.R.string.ok, 
        new DialogInterface.OnClickListener() { 
         public void onClick(DialogInterface dialog, int id) { 
          dialog.cancel(); 
         } 
        }).show(); 
       } 
      } 
     } 

     @Override 
     protected void onCancelled() { 
      mFacebookAuthTask = null; 
      showProgress(false); 
     } 
    } 
} 

FBLoginFragment.java:

package com.everporter.everporter; 

import java.util.Arrays; 

import android.app.Activity; 
import android.content.Intent; 
import android.os.Bundle; 
import android.support.v4.app.Fragment; 
import android.util.Log; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 

import com.facebook.Request; 
import com.facebook.Response; 
import com.facebook.Session; 
import com.facebook.SessionState; 
import com.facebook.UiLifecycleHelper; 
import com.facebook.model.GraphUser; 
import com.facebook.widget.LoginButton; 

public class FBLoginFragment extends Fragment { 

    private static final String TAG = "FBLoginFragment"; 
    private UiLifecycleHelper mUiHelper; 
    private OnFBAccessTokenPass mTokenPasser; 
    private String mSessionToken; 

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

    private void onSessionStateChange(Session session, SessionState state, Exception exception) { 
     if (state.isOpened()) { 
      Log.i(TAG, "Logged in..."); 
      mSessionToken = session.getAccessToken(); 
      // Request user data and show the results 
      Request.executeMeRequestAsync(session, new Request.GraphUserCallback() { 

       @Override 
       public void onCompleted(GraphUser user, Response response) { 
        if (user != null) { 
         // pass the access token to the activity 
         // with the user id 
         mTokenPasser.onFBAccessTokenPass(mSessionToken, user.getId()); 
        } 
       } 
      }); 
     } else if (state.isClosed()) { 
      Log.i(TAG, "Logged out..."); 
      mSessionToken = ""; 
     } 
    } 

    public interface OnFBAccessTokenPass { 
     public void onFBAccessTokenPass(String accessToken, String uid); 
    } 

    @Override 
    public void onAttach(Activity a) { 
     super.onAttach(a); 
     mTokenPasser = (OnFBAccessTokenPass) a; 
    } 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     mUiHelper = new UiLifecycleHelper(getActivity(), callback); 
     mUiHelper.onCreate(savedInstanceState); 
    } 

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

     LoginButton authButton = (LoginButton) view.findViewById(R.id.authButton); 
     authButton.setFragment(this); 
     authButton.setReadPermissions(Arrays.asList("email")); 

     return view; 
    } 

    @Override 
    public void onResume() { 
     super.onResume(); 

     // For scenarios where the main activity is launched and user 
     // session is not null, the session state change notification 
     // may not be triggered. Trigger it if it's open/closed. 
     Session session = Session.getActiveSession(); 
     if (session != null && 
       (session.isOpened() || session.isClosed())) { 
      onSessionStateChange(session, session.getState(), null); 
     } 

     mUiHelper.onResume(); 
    } 

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

    @Override 
    public void onPause() { 
     super.onPause(); 
     mUiHelper.onPause(); 
    } 

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

    @Override 
    public void onSaveInstanceState(Bundle outState) { 
     super.onSaveInstanceState(outState); 
     mUiHelper.onSaveInstanceState(outState); 
    } 
} 

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
    package="com.everporter.everporter" 
    android:versionCode="1" 
    android:versionName="1.0" > 

    <uses-sdk 
     android:minSdkVersion="10" 
     android:targetSdkVersion="17" /> 
    <uses-permission android:name="android.permission.INTERNET"/> 

    <application 
     android:allowBackup="true" 
     android:icon="@drawable/ic_launcher" 
     android:label="@string/app_name" 
     android:theme="@style/AppTheme" > 
     <activity 
      android:name="com.everporter.everporter.MainActivity" 
      android:label="@string/app_name" > 
      <intent-filter> 
       <action android:name="android.intent.action.MAIN" /> 

       <category android:name="android.intent.category.LAUNCHER" /> 
      </intent-filter> 
     </activity> 
     <activity 
      android:name="com.everporter.everporter.LoginActivity" 
      android:label="@string/title_activity_login" 
      android:noHistory="true" 
      android:excludeFromRecents="true" 
      android:windowSoftInputMode="adjustResize|stateVisible" > 
     </activity> 
     <meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/fb_app_id"/> 
     <activity android:name="com.facebook.LoginActivity" android:label="@string/app_name"></activity> 
    </application> 

</manifest> 

ответ

78

Проведя день по этому вопросу, я нашел причину: в моей деятельности был андроид: noHistory = «true» в манифесте, который помешал Facebook SDK начать мою деятельность при открытии сессии. Уфф !!

+4

Вы спасли мой день, серьезно это почти неизлечимая ошибка. Большое спасибо человеку. – manish

+1

Смешно, что андроид ничего не отслеживает! Спасибо, давно застрял в этой проблеме! –

+1

Святое дерьмо, я рад, что тебе удалось исправить это, спасибо, что ответили на свой вопрос. :) – Tom