Method is called multiple times but called only once - android

I implemented Facebook SDK in my Android application. I am using the login function of the SDK. What i am trying to do is the following :
When MainActivity starts, check if there is an active session
If there isn't, give the user the chance to login into facebook
If there is, user has to be redirected to an other Activity (let's call this : HomeActivity).
When my application starts, it starts the MainActivity. My onCreate() method looks like this :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
this.app = (ApplicationClass)getApplicationContext();
app.setUiHelper(new UiLifecycleHelper(this, callback));
uiHelper = app.getUiHelper();
uiHelper.onCreate(savedInstanceState);
if(this.app.getLoggedIn()) {
setContentView(R.layout.login);
startLoggingIn(); // <-- Makes login button invisible and starts spinner
login();
} else {
setContentView(R.layout.login);
}
}
ApplicationClass is a class where i set all kind of information that can be used throughout the application lifecycle. It doesn't really matter but i thought i include it in the example.
So when MainActivity starts, it should call login() method, which looks like this :
public void login() {
logging_in = true;
Log.i(TAG, "Loggin in");
synchronized (this) {
// start Facebook Login
Session.openActiveSession(this, true, new Session.StatusCallback() {
// callback when session changes state
#Override
public void call(Session session, SessionState state, Exception exception) {
if (Session.getActiveSession().isOpened()) {
// make request to the /me API
Request.executeMeRequestAsync(session, new Request.GraphUserCallback() {
// callback after Graph API response with user object
#Override
public void onCompleted(GraphUser user, Response response) {
if (user != null) { // User is logged in
app.setUser(user);
app.setLoggedIn(true);
// Redirect user to the HomeActivity
Intent intent = new Intent(getBaseContext(), HomeActivity.class);
startActivity(intent);
}
}
});
}
}
});
logging_in = false;
}
}
And finally, my callback for Facebook session looks like this :
private void onSessionStateChange(Session session, SessionState state, Exception exception) {
if (state.isOpened()) {
synchronized (this) {
if( ! logging_in ) {
login();
}
}
} else if (state.isClosed()) {
setContentView(R.layout.login);
}
}
It does it all well, except the login() method gets called about many times. The output in my log is :
Why does it get called so many times? It obviously make sure the HomeActivity intent is started multiple times. I can't figure out why.
Solution
Changed my Session.StateCallback a little bit. It now looks like this :
private void onSessionStateChange(Session session, SessionState state, Exception exception) {
if (state.isOpened()) {
if(state == SessionState.OPENED_TOKEN_UPDATED) {
Log.i(TAG, "OPENED_TOKEN_UPDATED");
login();
} else if (state == SessionState.OPENED) {
if(app.getUser() != null) {
// Redirect user to the HomeActivity
Intent intent = new Intent(getBaseContext(), HomeActivity.class);
startActivity(intent);
} else {
login();
}
}
} else if (state.isClosed()) {
if(state == SessionState.CLOSED_LOGIN_FAILED) {
Log.i(TAG, "CLOSED_LOGIN_FAILED");
setContentView(R.layout.login);
} else if(state == SessionState.CLOSED) {
Log.i(TAG, "CLOSED");
//setContentView(R.layout.login);
}
}
}

Its most likely because you're calling login from onSessionState changed.
When you login what do think is going to happen to the session, it's going to change right.
And when it changes it calls onSessionState change and you call login.
I would expect that to loop infinitely but it seems Facebook have done some handling for this.

I also faced the same problem .. I just changed my activity result code . And it worked ..

Related

Pause splash screen until user responds to permission request in android app

in my android application i have a splash screen that lasts 5 seconds during those 5 seconds i ask the user to grant location permission , I want to pause the splash screen activity until the user grants or denies the permission request.If the activity is not paused until the user responds, the next activity will be activated without having the user's response about the permission request here's my code
SpalshScreen activity:
public class SplashScreen extends BaseActivity {
private static int SPLASH_SCREEN_TIME_OUT = 6000;
some properties ....
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);//will hide the title
getSupportActionBar().hide(); //hide the title bar
setContentView(R.layout.activity_splash_screen);
// This is the method that asks the user for permission
locationPermGranted = CheckLocationPermission();
deviceLanguage = Locale.getDefault().getDisplayLanguage();
DeviceLanguageCode = Locale.getDefault().getLanguage();
sharedPreferences = getSharedPreferences(getPackageName() + ".prefs", Context.MODE_PRIVATE);
editor = sharedPreferences.edit();
codeIntent = new Intent(SplashScreen.this, PhoneVerification.class);
myIntent = new Intent(SplashScreen.this, ProfileSettings.class);
loggedIntent = new Intent(SplashScreen.this, Main.class);
notLoggedIntent = new Intent(SplashScreen.this, Login.class);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
if (!locationPermGranted) {
killApp();
}
if (sharedPreferences.getString("userLanguage", DeviceLanguageCode) == null) {
editor.apply();
}
if (sharedPreferences.getBoolean("isRegistered", false)) {
if (sharedPreferences.getBoolean("isPhoneVerified", false)) {
if (sharedPreferences.getBoolean("isLoggedIn", false)) {
// GO TO HOME
startActivity(loggedIntent);
} else {
// GO TO LOGIN
editor.apply();
startActivity(notLoggedIntent);
}
} else {
editor.apply();
startActivity(codeIntent);
}
} else {
//GO TO PROFILE SETTINGS
editor.apply();
startActivity(myIntent);
}
finish();
}
}, SPLASH_SCREEN_TIME_OUT);
}
Check the permission up front and if the permission is already granted, continue your Handler() and if not, then request for the permission and handle the result. Here's how:
Put all your Handler() code in a function so you can call it easily:
private void continueOperation(){
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
if (sharedPreferences.getString("userLanguage", DeviceLanguageCode) == null) {
editor.apply();
}
if (sharedPreferences.getBoolean("isRegistered", false)) {
if (sharedPreferences.getBoolean("isPhoneVerified", false)) {
if (sharedPreferences.getBoolean("isLoggedIn", false)) {
// GO TO HOME
startActivity(loggedIntent);
} else {
// GO TO LOGIN
editor.apply();
startActivity(notLoggedIntent);
}
} else {
editor.apply();
startActivity(codeIntent);
}
} else {
//GO TO PROFILE SETTINGS
editor.apply();
startActivity(myIntent);
}
finish();
}
}, SPLASH_SCREEN_TIME_OUT);
}
Here, you don't have to check for permission because this function will only be called
with granted permission.
Then, put a permission check in onCreate():
if (!locationPermGranted)
killApp();
else
continueOperation();
Now, handle the onRequestPermissionResult() and call continueOperation() if permission is granted.
In case, you don't want the user to wait for 6 seconds if the permission is granted in onRequestPermissionResult() which would obviously be bad UX, you can use the Handler() in the else{} of 2nd point. So, User will only have to wait if the permission was previously granted which is the basic purpose of a SplashScreen.
Surround newHandler().postDelayed method with if-else statement. If the user grants permission then only run the post delay method.

Facebook onCompleted is called twice

I did the following:
#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);
}
uiHelper.onResume();
}
and
private void onSessionStateChange(Session session, SessionState state, Exception exception) {
if (state.isOpened()) {
makeMeRequest(session);
} else if (state.isClosed()) {
}
}
and
private void makeMeRequest(final Session session) {
Request request = Request.newMeRequest(session, new Request.GraphUserCallback() {
#Override
public void onCompleted(GraphUser user, Response response) {
if (session == Session.getActiveSession()) {
if (user != null) {
log.d("creating fragment"); //<<<<<<<<<<<<<<<
//creating a fragment when the request is complete
.....
}
}
if (response.getError() != null) {
}
}
});
request.executeAsync();
}
Please read the comment of the first code clause: as suggested by Facebook, that code works because without it, the fragment is not created in certain scenarios even though I'm logged in, but with this code, onCompleted is called twice and I get an exception. see my logging in the 3rd code clause: log.d("creating fragment") - I see it twice before the exception occurred.
Any idea what am I missing?
p.s.: I have a main activity that calls a fragment where the user can login to facebook
Session session = Session.getActiveSession();
if (session != null && (session.isOpened() || session.isClosed())) {
onSessionStateChange(session, session.getState(), null);
}
don't use above code in onresume(),
Activity normally called first oncreate() and then onresume().so that it's called twice.
please check it.

uploading pictures to facebook using the facebook sdk for android

I am trying to upload a picture to facebook using the sdk for android. When i run the code my app crashes. Can someone help me. I am not too sure how to use the sdk. Is there another way to upload pictures?
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_main);
bm = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
// start Facebook Login
Session.openActiveSession(this, true, new Session.StatusCallback() {
// callback when session changes state
#Override
public void call(Session session, SessionState state, Exception exception) {
if (session.isOpened()) {
// make request to the /me API
Request.newUploadPhotoRequest(session, bm, new Request.Callback() {
#Override
public void onCompleted(Response response)
{
// TODO Auto-generated method stub
if (isFinishing()) {
return;
}
if (response.getError() != null) { // [IF Failed Posting]
Log.d("", "photo upload problem. Error="+response.getError() );
} // [ENDIF Failed Posting]
Object graphResponse = response.getGraphObject().getProperty("id");
if (graphResponse == null || !(graphResponse instanceof String) ||
TextUtils.isEmpty((String) graphResponse)) { // [IF Failed upload/no results]
Log.d("", "failed photo upload/no response");
} else { // [ELSEIF successful upload]
fbPhotoAddress = "https://www.facebook.com/photo.php?fbid=" +graphResponse;
}
}
} ).executeAsync();
}
}
});
}
}

Facebook Session cancel dialog

I'm developping an app that uses this : https://developers.facebook.com/docs/android/native-login/
I have followed the Facebook dev tutorial, so basically I have the session check, it opens the dialog, and I'm trying to get the Cancel event (when the user cancel the facebook dialog) but I have no method on this.
Maybe you can help.
Thanks
EDIT: Actually, even if I click the cancel button, I still receive the GraphUser correctly. That's weird.
With Android SDK 3.5, I got the cancel event via exception, if the state change callback with instance of FacebookOperationCanceledException or FacebookAuthorizationException, its an cancel event:
private void onSessionStateChange(Session session, SessionState state, Exception exception) {
if (exception instanceof FacebookOperationCanceledException || exception instanceof FacebookAuthorizationException) {
// Cancelled by user, show alert
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())) {
// Session ready
}
}
}
private Session.StatusCallback callback = new Session.StatusCallback() {
#Override
public void call(Session session, SessionState state, Exception exception) {
onSessionStateChange(session, state, exception);
}
};
It works just great
Add a Session.StatusCallback to your open request where you can check the SessionState.
new Session.StatusCallback() {
#Override
public void call(Session session, SessionState state, Exception exception) {
//check state
}
};
Check out this question.

Android IAB - Activity destroyed after successful purchase

I am pulling my hair out! At one point in the last week, I had this working.
I have an Android app that I am trying to add in-ap billing to. I followed the sample TrivialDrive, and my code worked a few times. Now it doesn't.
I am creating a simple trivia game that has a number of free questions, and the option to upgrade to get more questions. When the user completes the list of free questions, they are taken to a "Game Over" screen where they can erase their answers and start again, or upgrade.
When I click the "Upgrade" button, I can make a successful purchase, but as soon as the Google "Payment Successful" dialog goes away, my activity is destroyed and I am sent back to my main activity.
When I try to go back and do my purchase again, my code catches the error ("You already own this item") and handles it appropriately. My code explains to the user that they already own the upgrade, and allows them to click a button to continue playing. So it looks like the OnIabPurchaseFinishedListener is firing at this point.
I have updated the Google helper code with the latest files.
Any help or suggestions as to where to look for answers is much appreciated.
Thanks.
This is the relevant code for my activity:
public class GameOverActivity extends BaseActivity
{
private IabHelper mHelper;
private String m_base64EncodedPublicKey;
private static String THE_UPGRADE_SKU = "upgrade52";
public static int BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED = 7;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game_over);
setTitle("Game Over");
Button butPlay = (Button) findViewById(R.id.buttonPlay);
butPlay.setVisibility(View.INVISIBLE);
PrepareIAB();
}
#Override
protected void onResume()
{
super.onResume();
CURRENT_ACTIVITY = ACTIVITY_GAME_OVER;
SetMainText();
}
#Override
protected void onDestroy()
{
super.onDestroy();
try
{
if (mHelper != null)
{
mHelper.dispose();
mHelper = null;
}
}
catch (Exception e)
{
}
}
private void PrepareIAB()
{
m_base64EncodedPublicKey = "MyKey";
// compute your public key and store it in base64EncodedPublicKey
mHelper = new IabHelper(this, m_base64EncodedPublicKey);
mHelper.enableDebugLogging( true, TAG);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener()
{
public void onIabSetupFinished(IabResult result)
{
if (!result.isSuccess())
{
ShowMessage("There was an error connecting to the Google Play Store.");
}
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
try
{
// Pass on the activity result to the helper for handling
if (!mHelper.handleActivityResult(requestCode, resultCode, data))
{
// not handled, so handle it ourselves (here's where you'd
// perform any handling of activity results not related to in-app
// billing...
super.onActivityResult(requestCode, resultCode, data);
}
else
{
// Log.d(TAG, "onActivityResult handled by IABUtil.");
}
}
catch (Exception e)
{
super.onActivityResult(requestCode, resultCode, data);
}
}
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener =
new IabHelper.OnIabPurchaseFinishedListener()
{
public void onIabPurchaseFinished(IabResult result, Purchase purchase)
{
try
{
if (result.isFailure())
{
if (result.mResponse==7)
{
UpgradeComplete();
ShowMessage("Thank you for upgrading.\r\n\r\nThis version has 400 more questions.");
}
else
{
ShowMessage("Error purchasing: " + String.valueOf(result.mResponse));
UpgradeError();
return;
}
}
else if (purchase.getSku().equals(THE_UPGRADE_SKU))
{
UpgradeComplete();
ShowMessage("Thank you for upgrading.\r\n\r\nThis version has 400 more questions.");
}
else
{
ShowMessage("Something else happened. ");
}
}
catch (Exception e)
{
Log.e(TAG, e.getLocalizedMessage());
}
}
};
private void HideUpgrade()
{
try
{
Button btnUpgrade = (Button) findViewById(R.id.buttonUpgrade);
if (btnUpgrade != null)
{
btnUpgrade.setVisibility(View.INVISIBLE);
}
TextView txtMessage = (TextView) findViewById(R.id.txtUpgradeFromGameOver);
if (txtMessage!=null)
{
txtMessage.setVisibility(View.INVISIBLE);
}
}
catch (Exception e)
{
}
}
public void onQuitButtonClick(View view)
{
finish();
}
public void onResetDBButtonClick(View view)
{
ConfirmResetDatabase();
}
private void ConfirmResetDatabase()
{
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
switch (which)
{
case DialogInterface.BUTTON_POSITIVE:
ResetDatabase();
Intent gameActivity = new Intent(getApplicationContext(), GameActivity.class);
gameActivity.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
// startActivityForResult(gameActivity, ACTIVITY_GAME);
startActivity(gameActivity);
break;
case DialogInterface.BUTTON_NEGATIVE:
// No button clicked
break;
}
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Do you want to erase your score and start over?").setPositiveButton("Yes", dialogClickListener).setNegativeButton("No", dialogClickListener).show();
}
public void onUpgradeButtonClick(View view)
{
try
{
if (mHelper != null)
{
mHelper.launchPurchaseFlow(this, THE_UPGRADE_SKU, 10001, mPurchaseFinishedListener, m_TriviaAppInstance.AppInstallID());
}
else
{
ShowMessage("Unable to connect to Google Play Store.");
}
}
catch (Exception e)
{
ShowMessage("Unable to connect to Google Play Store.");
SendErrorMessage(e.getLocalizedMessage());
}
}
private void UpgradeComplete()
{
try
{
HideUpgrade();
Button butPlay = (Button) findViewById(R.id.buttonPlay);
if (butPlay!=null)
{
butPlay.setVisibility(View.VISIBLE);
}
TextView txtReset = (TextView) findViewById(R.id.txtGameOverRestDB);
if (txtReset!=null)
{
txtReset.setVisibility(View.INVISIBLE);
}
Button btnReset = (Button)findViewById(R.id.buttonResetDB);
if (btnReset!=null)
{
btnReset.setVisibility(View.INVISIBLE);
}
m_TriviaAppInstance.SetUpgradedStatus(true);
}
catch (Exception e)
{
}
//
}
private void UpgradeError()
{
try
{
Button butUpgrade;
butUpgrade = (Button) findViewById(R.id.buttonUpgrade);
butUpgrade.setVisibility(View.INVISIBLE);
TextView txtMessage = (TextView) findViewById(R.id.txtUpgradeScreen);
txtMessage.setText(R.string.upgradeScreenTextError);
}
catch (Exception e)
{
}
}
public void onPlayButtonClick(View view)
{
Intent myIntent = new Intent(view.getContext(), GameActivity.class);
myIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivityForResult(myIntent, ACTIVITY_GAME);
}
public void SetMainText()
{
TextView txt = (TextView) findViewById(R.id.txtScoreGlobal);
txt.setText(Integer.toString(m_TriviaAppInstance.getGlobal()) + "%");
SetPlayerScore(1);
if (m_TriviaAppInstance.getUpgradedStatus() == true)
{
HideUpgrade();
}
}
}
FYI: I think I have this figured out - for anyone else that may come across it.
The activity that I was using to launch "In App Billing" was called with a "FLAG_ACTIVITY_NO_HISTORY". I did this because I didn't want the user to be able to click to go back to this "Game Over" activity.
BUT, this causes grief with "In App Billing". So, make sure you don't try to launch "In App Billing" from an activity that has had the "FLAG_ACTIVITY_NO_HISTORY" set.
My original code:
private void GameOver()
{
m_TriviaAppInstance.setGameOver(true);
Intent gameOver = new Intent(getApplicationContext(), GameOverActivity.class);
gameOver.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(gameOver);
}
Updated code:
private void GameOver()
{
m_TriviaAppInstance.setGameOver(true);
Intent gameOver = new Intent(getApplicationContext(), GameOverActivity.class);
startActivity(gameOver);
}
Peace
I'm not high enough to comment, but bless you. I had
android:noHistory="true"
set in AndroidManifest.xml for my activity and was experiencing the same problem.
Took it out and IAB is working. Yay!
Do not forget that your IabHelper.OnIabPurchaseFinishedListener is called on a different thread and before onResume() is called on your Activity!
So your UpgradeComplete() or UpgradeError() can cause a crash on older devices (Crashed every time on my Gingerbread Sony Xperia Mini Pro and worked without any trouble on Samsung Galaxy S4 (Android 4.2.2)
Caused a 3 day delay on my game..

Categories

Resources