Currently I'm writing an adapter class to provide a convenient way for communication with the facebook API.
The way I thought about using it is to run the authentication when the app is starting up, downloading user's private picture, and later in the app publishing updates on users facebook wall using an AsyncFacebookRunner.
However flipping through the documentation it seems for every authorize() implementation the first parameter have to be an activity.
void authorize(Activity activity, final DialogListener listener):
And here I begin to wonder.
Thinking about activities and life cycles what will happen when the activity I threw in will be destroyed? Wouldn't the reference for this object Facebook.mAuthActivity become invalid as well.
I see the logout() method "only" asks for a context.
String logout(Context context) throws ...:
context - The Android context in which the logout should be called: it should be the same context in which the login occurred in order to clear any stored cookies
From what I see I can not guarantee the "login-activity" will still be present as app's uptime increases - actually the opposite is more likely.
Are there any special situations I should consider to prevent the app form total crashing in a later state?
You can try use my FBHelper class.
public class FBHelper {
private SharedPreferences mPrefs;
private Context context;
private final String ACCES_TOKEN = "access_token";
private final String ACCES_EXPIRES = "access_expires";
private Facebook facebook;
private FBHelperCallbacks callback;
public FBHelper(Context context, Facebook facebook)
{
this.context = context;
this.facebook = facebook;
}
public void setSignInFinishListener(FBHelperCallbacks callback)
{
this.callback = callback;
}
public void FacebookSingleSignIn() {
mPrefs = ((Activity)context).getPreferences(Context.MODE_PRIVATE);
String access_token = mPrefs.getString(ACCES_TOKEN, null);
long expires = mPrefs.getLong(ACCES_EXPIRES, 0);
if(access_token != null) {
facebook.setAccessToken(access_token);
}
if(expires != 0) {
facebook.setAccessExpires(expires);
}
/*
* Only call authorize if the access_token has expired.
*/
if(!facebook.isSessionValid()) {
Log.i("Facebook","Facebook session is not valid based on acces token... authorizing again");
facebook.authorize((Activity)context, new String[] {"user_about_me"},new DialogListener() {
#Override
public void onFacebookError(FacebookError e) {
e.printStackTrace();
callback.onError(e.toString());
}
#Override
public void onError(DialogError e) {
Log.i("Facebook","onError inner");
callback.onError(e.toString());
}
#Override
public void onComplete(Bundle values) {
SharedPreferences.Editor editor = mPrefs.edit();
editor.putString(ACCES_TOKEN, facebook.getAccessToken());
Log.i("Facebook","Saving acces token:"+facebook.getAccessToken());
editor.putLong(ACCES_EXPIRES, facebook.getAccessExpires());
editor.commit();
callback.onSignedInFinished(facebook.getAccessToken());
}
#Override
public void onCancel() {
callback.onError("onCancel");
}
});
}
else
{
Log.i("Facebook","Accces token read form preferencesno no need to authorize");
callback.onSignedInFinished(facebook.getAccessToken());
}
}
public String LogOut()
{
try {
//set ACCES_TOKEN to null
mPrefs = ((Activity)context).getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = mPrefs.edit();
editor.putString(ACCES_TOKEN, null);
editor.putLong(ACCES_EXPIRES, 0);
editor.commit();
return facebook.logout(context);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "Error";
}
public static abstract class FBHelperCallbacks{
public abstract void onSignedInFinished(String accesToken);
public abstract void onError(String message);
}
}
This is how you use this class.
public class LogInActivity extends Activity {
private static final String TAG = "LogInActivity";
public static final int REQUEST_CODE = 1;
private Context context;
private Facebook facebook;
private FBHelper fbhelper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_log_in);
this.context = this;
Handler pauser = new Handler();
pauser.postDelayed (new Runnable() {
public void run() {
facebook = new Facebook(context.getString(R.string.FACEBOOK_APP_ID));
fbhelper = new FBHelper(context, facebook);
if (aHelper.isLogedIn())
{
//log out
fbhelper.LogOut();
}
else
{
//facebook login
fbhelper.setSignInFinishListener(fbcallback);
fbhelper.FacebookSingleSignIn();
}
}
}, 100);
}
FBHelperCallbacks fbcallback = new FBHelperCallbacks() {
#Override
public void onSignedInFinished(String accesToken) {
Log.d(TAG,"log in finish");
}
#Override
public void onError(String message) {
setResult(RESULT_CANCELED);
finish();
}
};
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
facebook.authorizeCallback(requestCode, resultCode, data);
}
}
aHelper is object that hold some application specific data. Basically you should decide here if you want to log in or log out.
using facebook API for the android is easy and in your case you don't need to save the Facebook instance the only thing you need is to save the authKey of the facebook on the first login then you can use it anywhere.
this means that you can create more than one instance of the facebook object in mutiple activities based on the authKey.
Otherwise you need to put this facebook object in a singleton handler to save it among the application :
class x {
private Facebook obj;
private static x instance;
private x (){
}
public static x getX(){
if(instance == null){
instance = new x();
}
return instance;
}
public void setIt(Facebook obj){
this.obj = obj;
}
public Facebook getIt(){
return obj;
}
}
but this way is not the best way to implement the code you need to create a Facebook instance for each activity using the authKy.
Related
I tried implementing a signIn method from the OneDrive API, but I am not sure I correctly understood the workflow.
Basically, on first launch of the app, I want to have both the login window and the "authorise the app to..." window". But then, when the user launches the app again, I would like to be directly connected to the app, without any window.
Instead, with the following code, I keep having the second window (where the user decides to accept the app)
#Override
public void signIn() {
//personal code
linkingStarted = true;
signInStatus = SignInStatus.SIGNING_IN;
activity.setUpWait(R.layout.popup_waitgif_white);
//end of personal code
mAuthClient = AuthClientFactory.getAuthClient(activity.getApplication());
if (mAuthClient.getSession().isExpired() && Util.isConnectedToInternet(activity)) {
activity.alertOnUIThread("Login again");
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
mAuthClient.login(activity, SCOPES, mAuthListener);
}
});
} else if (!Util.isConnectedToInternet(activity)) {
activity.alertOnUIThread(activity.getString(R.string.alert_verifyconnection));
} else {
activity.alertOnUIThread("Resigned In OneDrive");
signInStatus = SignInStatus.SIGNED_IN;
mAuthClient.initialize(SCOPES, new AuthListener() {
#Override
public void onAuthComplete(final AuthStatus status, final AuthSession session, final Object userState) {
if (status == AuthStatus.CONNECTED) {
authToken = session.getAccessToken();
oneDriveService = getOneDriveService();
signInStatus = SignInStatus.SIGNED_IN;
} else {
authenticationFailure();
Log.v(TAG, "Problem connecting");
}
}
#Override
public void onAuthError(final AuthException exception, final Object userState) {
//mAuthClient.login(activity, SCOPES, mAuthListener);
}
}, null, authToken);
}
}
and the AuthClientFactory is just this:
public class AuthClientFactory {
private static AuthClient authClient;
private static final String CLIENT_ID = "00000000XXXXX";
public static AuthClient getAuthClient(Context context) {
if (authClient == null)
authClient = new AuthClient(context, OneDriveOAuthConfig.getInstance(), CLIENT_ID);
return authClient;
}
}
You would have an easier time with the OneDrive SDK for Android, as authentication is a much simpler process.
final MSAAuthenticator msaAuthenticator = new MSAAuthenticator() {
#Override
public String getClientId() {
return "<msa-client-id>";
}
#Override
public String[] getScopes() {
return new String[] { "onedrive.appfolder", "wl.offline_access"};
}
}
final IClientConfig oneDriveConfig = new DefaultClientConfig.createWithAuthenticator(msaAuthenticator);
final IOneDriveClient oneDriveClient = new OneDriveClient
.Builder()
.fromConfig(oneDriveConfig)
.loginAndBuildClient(getActivity());
That will take care of the user authentication flow and then give you a service object that makes interacting with OneDrive straight-forward. See the full example application.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I having some issue in creating a dialog of type group in quickblox chat service on android. I am able to get all the user from the server side of quickblox, able to build dialog and adding those users i got in it but when it comes to call groupChatManager.createDialog(dialog, new QBEntityCallbackImpl(). I am getting a null pointer exception on that. I found later that i have to initialise chatService in class that extends application i did that but still same error. here is my code
enter code here
My applicationSingeltonClass
public class ApplicationSingleton extends Application
{
private QBUser currentUser;
//quickBlox
private static final String APP_ID = "key";
private static final String AUTH_KEY = "authkey";
private static final String AUTH_SECRET = "authsec";
private static ApplicationSingleton instance;
private Map<Integer, QBUser> dialogsUsers = new HashMap<Integer, QBUser>();
private QBRoomChat currentRoom;
#Override
public void onCreate()
{
super.onCreate();
initApplication();
initImageLoader(getApplicationContext());
}
public static ApplicationSingleton getInstance()
{
return instance;
}
private void initImageLoader(Context context)
{
// This configuration tuning is custom. You can tune every option, you may tune some of them,
// or you can create default configuration by
// ImageLoaderConfiguration.createDefault(this);
// method.
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
.threadPriority(Thread.NORM_PRIORITY - 2)
.denyCacheImageMultipleSizesInMemory()
.discCacheFileNameGenerator(new Md5FileNameGenerator())
.tasksProcessingOrder(QueueProcessingType.LIFO)
.enableLogging() // Not necessary in common
.build();
// Initialize ImageLoader with configuration.
ImageLoader.getInstance().init(config);
}
public QBUser getCurrentUser()
{
return currentUser;
}
public void setCurrentUser(QBUser currentUser)
{
this.currentUser = currentUser;
}
public Map<Integer, QBUser> getDialogsUsers()
{
return dialogsUsers;
}
public void setDialogsUsers(List<QBUser> setUsers)
{
dialogsUsers.clear();
for (QBUser user : setUsers)
{
dialogsUsers.put(user.getId(), user);
}
}
public void addDialogsUsers(List<QBUser> newUsers)
{
for (QBUser user : newUsers)
{
dialogsUsers.put(user.getId(), user);
}
}
public Integer getOpponentIDForPrivateDialog(QBDialog dialog)
{
Integer opponentID = -1;
for(Integer userID : dialog.getOccupants())
{
if(userID != getCurrentUser().getId())
{
opponentID = userID;
break;
}
}
return opponentID;
}
private void initApplication()
{
instance = this;
QBChatService.setDebugEnabled(true);
QBSettings.getInstance().fastConfigInit(APP_ID, AUTH_KEY,AUTH_SECRET);
}
}
here is my other class were i create a dialog
enter code here
protected void onPostExecute(final Boolean success)
{
if(status.equals("accepted"))
{
pagedRequestBuilder.setPage(1);
pagedRequestBuilder.setPerPage(10);
final ArrayList<String> usersLogins = new ArrayList<String>();
usersLogins.add(userID);
usersLogins.add(herocivID);
final ArrayList<Integer> occupantIdsList = new ArrayList<Integer>();
occupantIdsList.add(civID);
occupantIdsList.add(heroCivID);
QBUsers.getUsersByFacebookId(usersLogins, pagedRequestBuilder, new QBEntityCallbackImpl<ArrayList<QBUser>>() {
#Override
public void onSuccess(ArrayList<QBUser> users, Bundle params)
{
if (!QBChatService.isInitialized())
{
QBChatService.init(getApplicationContext());
chatService = QBChatService.getInstance();
chatService.addConnectionListener(chatConnectionListener);
}
QBDialog dialog = new QBDialog();
dialog.setName("chat with mostafa wo may");
dialog.setType(QBDialogType.GROUP);
dialog.setOccupantsIds(occupantIdsList);
QBGroupChatManager groupChatManager = chatService.getInstance().getGroupChatManager();
groupChatManager.createDialog(dialog, new QBEntityCallbackImpl<QBDialog>()
{
#Override
public void onSuccess(QBDialog dialog, Bundle args)
{
Log.i("", "dialog: " + dialog);
}
#Override
public void onError(List<String> errors) {
Toast.makeText(getBaseContext(), "Something went wrong", Toast.LENGTH_SHORT).show();
}
});
}
#Override
public void onError(List<String> errors)
{
Toast.makeText(getBaseContext(), "Something went wrong", Toast.LENGTH_SHORT).show();
}
});
Toast.makeText(getBaseContext(), "you accepted the request", Toast.LENGTH_SHORT).show();
}
else if(status.equals("rejected"))
{
Toast.makeText(getBaseContext(), "you rejected the request", Toast.LENGTH_SHORT).show();
}
}
i need to know were exactly i have to configure the chat service to prevent the null pointer while creating a dialog.
Any help would be appreciated thank you.
You're right,
chatService.getInstance().getGroupChatManager();
is null if you're not logged in ti Chat
so you have to login to chat first and then call this method
I managed to solve this issue.
First you need to check roomChatManager if its equal to null you have to ask the user to login in.
Second in the login wether your using social provider or by mail you have to login to chatService ( ApplicationSingleton.getInstance().chatService.login(userResult);)
ApplicationSingleton is a java class were it extends Application in it you initialise the ChatService.
Thats it now you can create your dialog successfully.
I'm looking to pass a Facebook session across activities. I saw the example from Facebook's SDK and someone mentioned that the "Simple" example has a way to do this: https://github.com/facebook/facebook-android-sdk/blob/master/examples/simple/src/com/facebook/android/SessionStore.java
But how does this work? In my MainActivity, I have this:
mPrefs = getPreferences(MODE_PRIVATE);
String accessToken = mPrefs.getString("access_token", null);
long expires = mPrefs.getLong("access_expires", 0);
if (accessToken != null) {
//We have a valid session! Yay!
facebook.setAccessToken(accessToken);
}
if (expires != 0) {
//Since we're not expired, we can set the expiration time.
facebook.setAccessExpires(expires);
}
//Are we good to go? If not, call the authentication menu.
if (!facebook.isSessionValid()) {
facebook.authorize(this, new String[] { "email", "publish_stream" }, new DialogListener() {
#Override
public void onComplete(Bundle values) {
}
#Override
public void onFacebookError(FacebookError error) {
}
#Override
public void onError(DialogError e) {
}
#Override
public void onCancel() {
}
});
}
But how do I pass this along to my PhotoActivity activity? Is there an example of this being implemented?
Using SharedPreferences to pass data across activities is not good idea. SharedPreferences used to store some data into memory across application restart or device re-boot.
Instead you have two options:
Declare a static variable to hold facebook session, which is simplest method, but I wont recommend to use Static Fields as far there is no other way.
Make an class implementing parcelable, and set your facebook object there, see an parcelable implementation as follows:
// simple class that just has one member property as an example
public class MyParcelable implements Parcelable {
private int mData;
/* everything below here is for implementing Parcelable */
// 99.9% of the time you can just ignore this
public int describeContents() {
return 0;
}
// write your object's data to the passed-in Parcel
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}
// this is used to regenerate your object. All Parcelables must have a CREATOR that implements these two methods
public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
// example constructor that takes a Parcel and gives you an object populated with it's values
private MyParcelable(Parcel in) {
mData = in.readInt();
}
}
For FB SDK 3.5, In my FB login activity, I pass the active session object via intent extras because the Session class implements serializable:
private void onSessionStateChange(Session session, SessionState state, Exception exception) {
if (exception instanceof FacebookOperationCanceledException || exception instanceof FacebookAuthorizationException) {
new AlertDialog.Builder(this).setTitle(R.string.cancelled).setMessage(R.string.permission_not_granted).setPositiveButton(R.string.ok, null).show();
} else {
Session session = Session.getActiveSession();
if ((session != null && session.isOpened())) {
// Kill login activity and go back to main
finish();
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
intent.putExtra("fb_session", session);
startActivity(intent);
}
}
}
From my MainActivity onCreate(), I check for intent extra and initiate the session:
Bundle extras = getIntent().getExtras();
if (extras != null) {
Session.setActiveSession((Session) extras.getSerializable("fb_session"));
}
The example pretty much has the whole implementation. You just use SharedPreferences to store your session. When you need it in your PhotoActivity, just look in SharedPreferences again (perhaps via your SessionStore static methods if you followed the same pattern) to get the Facebook Session that you previously stored.
i'm a newbie in android. In my app i create a many-to-many chat, and need to update from server a list of Messages. In order to do so, i created a service that updates every second from the server.
My problem is that i don't know how to pass data back to the application. I know that I should do it using intent and broadcast receiver, but in that I stuck with Bundle object that i have to serialize in order to pass it to the app, and it does not make sense to me, since this operation is not that efficient.
For now i'm using the ref to my application (i think it's not that good but don't know why), and after every update from server in the service i activate the application function, and updates it's fields directly. Moreover i think maybe my code will do some good for beginners as well :)
public class UpdateChatService extends Service {
private static final long DELAY_FOR_CHAT_TASK = 0;
private static final long PERIOD_FOR_CHAT_TASK = 1;
private static final TimeUnit TIME_UNIT_CHAT_TASK = TimeUnit.SECONDS;
//private Task retryTask; TODO: check this out
private ScheduledExecutorService scheduler;
private boolean timerRunning = false;
private long RETRY_TIME = 200000;
private long START_TIME = 5000;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
scheduleChatUpdate();
}
private void scheduleChatUpdate() {
BiggerGameApp app = (BiggerGameApp) getApplication();
this.scheduler = Executors.newScheduledThreadPool(3);
this.scheduler.scheduleAtFixedRate(new UpdateChatTask(app),
DELAY_FOR_CHAT_TASK, PERIOD_FOR_CHAT_TASK,
TIME_UNIT_CHAT_TASK);
timerRunning = true;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!timerRunning) {
scheduleChatUpdate();
}
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
super.onDestroy();
if (scheduler != null) {
scheduler.shutdown();
}
timerRunning = false;
}
}
Here is the code of the asynchronous task the runs in the service.
Please tell me what i'm doing wrong, and how should pass data from the service to the application.
public void run() {
try {
if (this.app.getLastMsgFromServer() == null) {
this.app.setLastMsgFromServer(new Message(new Player(DEFAULT_EMAIL), "", -1));
this.app.getLastMsgFromServer().setMessageId(-1);
}
Gson gson = new GsonBuilder()
.registerTypeAdapter(DateTime.class, new DateTimeTypeConverter())
.create();
ServerHandler serverHandler = new ServerHandler();
String jsonString = gson.toJson(this.app.getLastMsgFromServer());
// Sending player to servlet in server
String resultString = serverHandler.getResultFromServlet(jsonString, "GetListOfMessages");
if (resultString.contains("Error")) {
return;
}
// Parsing answer
JSONObject json = new JSONObject(resultString);
Status status = null;
String statusString = json.getString("status");
if (statusString == null || statusString.length() == 0)
return;
status = Status.valueOf(statusString);
if (Status.SUCCESS.equals(status)) {
ArrayList<Message> tempChat = null;
JSONArray jsonList = json.getJSONArray("data");
MyJsonParser jsonParser = new MyJsonParser();
tempChat = jsonParser.getListOfMessagesFromJson(jsonList.toString());
if (tempChat != null && tempChat.size() != 0) {
// After getting the chat from the server, it saves the last msg
// For next syncing with the server
this.app.setLastMsgFromServer(tempChat.get(LAST_MSG_INDEX));
tempChat.addAll(this.app.getChat());
if (tempChat.size() > SIZE_OF_USER_CHAT) {
tempChat = (ArrayList<Message>) tempChat.subList(0, SIZE_OF_USER_CHAT - 1);
}
this.app.setChat(tempChat);
this.app.updateViews(null);
}
}
return;
Is the Service local only (I'm going to assume "yes")?
Communication with a local-only service can be done by passing an instance of android.os.Binder back, as shown below:
public class UpdateChatService extends Service {
public static final class UpdateChat extends Binder {
UpdateChatService mInstance;
UpdateChat(UpdateChatService instance) {
mInstance = instance;
}
public static UpdateChat asUpdateChat(IBinder binder) {
if (binder instanceof UpdateChat) {
return (UpdateChat) binder;
}
return null;
}
public String pollMessage() {
// Takes a message from the list or returns null
// if the list is empty.
return mInstance.mMessages.poll();
}
public void registerDataSetObserver(DataSetObserver observer) {
mInstance.mObservable.registerObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
mInstance.mObservable.unregisterObserver(observer);
}
}
private ScheduledExecutorService mScheduler;
private LinkedList<String> mMessages;
private DataSetObservable mObservable;
#Override
public IBinder onBind(Intent intent) {
return new UpdateChat(this);
}
#Override
public void onCreate() {
super.onCreate();
mObservable = new DataSetObservable();
mMessages = new LinkedList<String>();
mScheduler = Executors.newScheduledThreadPool(3);
mScheduler.scheduleAtFixedRate(new UpdateChatTask(), 0, 1, TimeUnit.SECONDS);
}
#Override
public void onDestroy() {
super.onDestroy();
mScheduler.shutdownNow();
mObservable.notifyInvalidated();
}
class UpdateChatTask implements Runnable {
int mN = 0;
public void run() {
// This example uses a list to keep all received messages, your requirements may vary.
mMessages.add("Message #" + (++mN));
mObservable.notifyChanged();
}
}
}
This example could be used to feed an Activity (in this case a ListActivity) like this:
public class ChattrActivity extends ListActivity implements ServiceConnection {
LinkedList<String> mMessages;
ArrayAdapter<String> mAdapter;
UpdateChat mUpdateChat;
DataSetObserver mObserver;
Runnable mNotify;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMessages = new LinkedList<String>();
mNotify = new Runnable() {
public void run() {
mAdapter.notifyDataSetChanged();
}
};
mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mMessages);
getListView().setAdapter(mAdapter);
// Bind to the Service if you do not need it to persist when this Activity
// dies - otherwise you must call #startService(..) before!
bindService(new Intent(this, UpdateChatService.class), this, BIND_AUTO_CREATE);
}
/**
* #see android.app.ListActivity#onDestroy()
*/
#Override
protected void onDestroy() {
super.onDestroy();
if (mUpdateChat != null) {
mUpdateChat.unregisterDataSetObserver(mObserver);
unbindService(this);
}
}
public void onServiceConnected(ComponentName name, IBinder service) {
mUpdateChat = UpdateChat.asUpdateChat(service);
mObserver = new DataSetObserver() {
#Override
public void onChanged() {
String message;
while ((message = mUpdateChat.pollMessage()) != null) {
mMessages.add(message);
}
runOnUiThread(mNotify);
}
#Override
public void onInvalidated() {
// Service was killed - restart or handle this error somehow.
}
};
// We use a DataSetObserver to notify us when a message has been "received".
mUpdateChat.registerDataSetObserver(mObserver);
}
public void onServiceDisconnected(ComponentName name) {
mUpdateChat = null;
}
}
If you need to communicate across processes you should look into implementing an AIDL interface - but for "local" versions this pattern works just fine & doesn't involve abusing the global Application instance.
You can use a static memory shared between your service and rest of application (activities). If you do not plan to expose this service to external apps, then sharing static memory is better than serializing/deserializing data via bundles.
Bundles based approach is encouraged for components that are to be exposed to outside world. A typical app usually has just the primary activity exposed in app manifest file.
If your don't pulibc your service , the static memory and the callback function can do.
If not , you can send broadcast.
I write an Android application that integrates facebook, but failing in the login to facebook step. What I'm doing basically is to perform authorization and then ask if the session is valid. The answer is always negative. If I'm trying a simple request like:
String response = facebookClient.request("me");
I am getting this response:
{"error":{"message":"An active access token must be used to query
information about the current user.","type":"OAuthException"}}
Maybe I have the wrong hash key (through I read pretty good threads how to get it right). I'd like to know if this is a way to insure key is matching.
I based my code on this - Android/Java -- Post simple text to Facebook wall?, and add some minor changes. This is the code:
public class FacebookActivity extends Activity implements DialogListener
{
private Facebook facebookClient;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.setContentView(R.layout.main);//my layout xml
}
public void login(View view)
{
facebookClient = new Facebook("my APP ID");
facebookClient.authorize(this, this);
if (facebookClient.isSessionValid() == true)
Log.d("Valid:", "yes");
else
Log.d("Valid:", "no");
}
}
Note that authorize method is asynchronous.
You should implement an onComplete method of DialogListener and make all the work you need (such as graph API me request) there.
Use following code in your app:
public class FacebookLogin {
private AsyncFacebookRunner mAsyncRunner;
private Facebook facebook;
private Context mContext;
private String mFName;
public static final String[] PERMISSIONS = new String[] {"email", "publish_checkins", "publish_stream","offline_access"};
public FacebookLogin(Context mContext) {
this.mContext=mContext;
facebook=new Facebook(YOUR_APP_ID);
mAsyncRunner = new AsyncFacebookRunner(facebook);
}
public void Login() {
facebook.authorize((Activity) mContext,PERMISSIONS,Facebook.FORCE_DIALOG_AUTH,new LoginDialogListener());
}
public void Logout() throws MalformedURLException, IOException {
facebook.logout(mContext);
}
public boolean isValidUser() {
return facebook.isSessionValid();
}
class LoginDialogListener implements DialogListener {
public void onComplete(Bundle values) {
//Save the access token and access expire for future use in shared preferece
String profile=facebook.request("me")
String uid = profile.getString("id");
mFName= profile.optString("first_name");
new Session(facebook, uid, mFName).save(mContext);
}
public void onFacebookError(FacebookError error) {
displayMessage("Opps..! Check for Internet Connection, Authentication with Facebook failed.");
}
public void onError(DialogError error) {
displayMessage("Opps..! Check for Internet Connection, Authentication with Facebook failed.");
}
public void onCancel() {
displayMessage("Authentication with Facebook failed due to Login cancel.");
}
}
}
On Login complete save the facebook access token and access expire in your shared preference and while using again facebook object later set that access token and access expire to facebook object , it will not give the error which occurs in your code.
you can use following class :
public class Session {
private static Session singleton;
private static Facebook fbLoggingIn;
// The Facebook object
private Facebook fb;
// The user id of the logged in user
private String uid;
// The user name of the logged in user
private String name;
/**
* Constructor
*
* #param fb
* #param uid
* #param name
*/
public Session(Facebook fb, String uid, String name) {
this.fb = fb;
this.uid = uid;
this.name = name;
}
/**
* Returns the Facebook object
*/
public Facebook getFb() {
return fb;
}
/**
* Returns the session user's id
*/
public String getUid() {
return uid;
}
/**
* Returns the session user's name
*/
public String getName() {
return name;
}
/**
* Stores the session data on disk.
*
* #param context
* #return
*/
public boolean save(Context context) {
Editor editor =
context.getSharedPreferences(ConstantsFacebook.KEY, Context.MODE_PRIVATE).edit();
editor.putString(ConstantsFacebook.TOKEN, fb.getAccessToken());
editor.putLong(ConstantsFacebook.EXPIRES, fb.getAccessExpires());
editor.putString(ConstantsFacebook.UID, uid);
editor.putString(ConstantsFacebook.NAME, name);
editor.putString(ConstantsFacebook.APP_ID, fb.getAppId());
editor.putBoolean(ConstantsFacebook.LOGIN_FLAG,true);
if (editor.commit()) {
singleton = this;
return true;
}
return false;
}
/**
* Loads the session data from disk.
*
* #param context
* #return
*/
public static Session restore(Context context) {
if (singleton != null) {
if (singleton.getFb().isSessionValid()) {
return singleton;
} else {
return null;
}
}
SharedPreferences prefs =
context.getSharedPreferences(ConstantsFacebook.KEY, Context.MODE_PRIVATE);
String appId = prefs.getString(ConstantsFacebook.APP_ID, null);
if (appId == null) {
return null;
}
Facebook fb = new Facebook(appId);
fb.setAccessToken(prefs.getString(ConstantsFacebook.TOKEN, null));
fb.setAccessExpires(prefs.getLong(ConstantsFacebook.EXPIRES, 0));
String uid = prefs.getString(ConstantsFacebook.UID, null);
String name = prefs.getString(ConstantsFacebook.NAME, null);
if (!fb.isSessionValid() || uid == null || name == null) {
return null;
}
Session session = new Session(fb, uid, name);
singleton = session;
return session;
}
/**
* Clears the saved session data.
*
* #param context
*/
public static void clearSavedSession(Context context) {
Editor editor =
context.getSharedPreferences(ConstantsFacebook.KEY, Context.MODE_PRIVATE).edit();
editor.clear();
editor.commit();
singleton = null;
}
/**
* Freezes a Facebook object while it's waiting for an auth callback.
*/
public static void waitForAuthCallback(Facebook fb) {
fbLoggingIn = fb;
}
/**
* Returns a Facebook object that's been waiting for an auth callback.
*/
public static Facebook wakeupForAuthCallback() {
Facebook fb = fbLoggingIn;
fbLoggingIn = null;
return fb;
}
public static String getUserFristName(Context context) {
SharedPreferences prefs =
context.getSharedPreferences(ConstantsFacebook.KEY, Context.MODE_PRIVATE);
String frist_name = prefs.getString(ConstantsFacebook.NAME, null);
return frist_name;
}
public static boolean checkValidSession(Context context) {
SharedPreferences prefs =
context.getSharedPreferences(ConstantsFacebook.KEY, Context.MODE_PRIVATE);
Boolean login=prefs.getBoolean(ConstantsFacebook.LOGIN_FLAG,false);
return login;
}
}