I've been trying to implement the Facebook SDK into my application in order to let users post messages on our fanwall through the app. However, I've been unsuccessful at even logging the user in through the SDK.
In the SDK examples, a simple sample has been given which uses an Activity to try and authorize the user using Single Sign-On. I've tried this example myself, and it works. I'm able to log in, I had to authorize the app to use my Facebook data and I could see the requests being made and received in LogCat.
Now, I've tried adding the same code to my own app. This app is Fragment based using the Compatibility package. There is one central FragmentActivity and the rest of my classes are simple Fragments. When adding the sample code to one of these Fragments, the Facebook app starts up for half a second when trying to authorize, but afterwards closes and nothing has happened. I'm back in my regular Fragment again.
When checking the LogCat after this, nothing suggests that the Facebook app even opened or made any requests other than the fact it shows some printing checks I added and the fact it says it's starting the Facebook intent :
01-12 13:19:40.874: I/System.out(6087): Calling authorize
01-12 13:19:40.874: I/ActivityManager(1380): Starting activity: Intent { cmp=com.facebook.katana/.ProxyAuth (has extras) } from pid 6087
01-12 13:19:40.874: I/System.out(6087): Called authorize
Other than that, nothing gets returned. No Facebook-checks, no statements saying my keys are wrong or anything, just nothing. The Facebook intent was called, but closed almost immediately and nothing else shows that it was even open.
This has been boggling my mind for a few hours now, and I'm starting to think the regular , sample-provided approach just doesn't work in Fragments due to the way Fragments work.
The code I've been using is posted below. The Fragment gets fired by a button which calls a FragmentTransaction. Am I doing something fundamentally wrong here, or does the Facebook SDK really just not work with Fragments? I've tried searching for this problem but I haven't been able to find anyone else with the same sort of situation.
public class FanWallFacebook extends Fragment {
Facebook facebook = new Facebook("294678133912628");
public FanWallFacebook() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.filler, container, false);
}
#Override
public void onStart() {
super.onStart();
System.out.println("Calling authorize");
facebook.authorize(getActivity(), new DialogListener() {
#Override
public void onComplete(Bundle values) {
System.out.println("Completed");
}
#Override
public void onFacebookError(FacebookError error) {
System.out.println("Facebook error: "+error.getMessage());
}
#Override
public void onError(DialogError e) {
System.out.println("General error: "+e.getMessage());
}
#Override
public void onCancel() {
System.out.println("Cancelled");
}
});
System.out.println("Called authorize");
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
facebook.authorizeCallback(requestCode, resultCode, data);
System.out.println("Authorize callback'd");
}
}
Edit
Just tried using a FragmentActivity, and lo and behold, that does work. It successfully logs in. Seems like the SDK really only works with classes that explicitly extend ...Activity. Could anyone give me an idea why that might be the case? I always thought Fragments somewhere down the line extended Activity as well.
I ended up using a FragmentActivity for my Facebook interaction. Not the ideal solution, but it works.
well...
the new Facebook SDK has an Activity that implement FragmentActivity so problem solved.
Related
I am showing Google's GDPR consent form and I am noticing a lot of these reports:
Fatal Exception: android.view.WindowManager$BadTokenException
Unable to add window -- token android.os.BinderProxy#38734f2 is not valid; is your activity running?
com.my.project.MainActivity$4.onConsentFormLoaded
As context I use MainActivity.this:
private void displayConsentForm() {
consentForm = new ConsentForm.Builder(MainActivity.this, GeneralUtils.getAppsPrivacyPolicy())
.withListener(new ConsentFormListener() {
#Override
public void onConsentFormLoaded() {
consentForm.show(); // crashing here for some users
}
#Override
public void onConsentFormOpened() { }
#Override
public void onConsentFormClosed(
ConsentStatus consentStatus, Boolean userPrefersAdFree) {
if(userPrefersAdFree) {
ConsentInformation.getInstance(MainActivity.this)
.setConsentStatus(NON_PERSONALIZED);
} else {
ConsentInformation.getInstance(MainActivity.this)
.setConsentStatus(consentStatus);
}
initAds();
}
#Override
public void onConsentFormError(String errorDescription) {
Log.e("Error",errorDescription);
}
})
.withPersonalizedAdsOption()
.withNonPersonalizedAdsOption()
.withAdFreeOption()
.build();
consentForm.load();
}
Here is additional Firebase crash report:
Why is this happening and how to prevent it? I am not sure what additional check to put before consentForm.show() and I can not reproduce the issue. Maybe it would suffice if I put this check before showing the form:
if(!MainActivity.this.isFinishing() && !MainActivity.this.isDestroyed())
?
The easiest way around this would be to just put a try-catch block around consentForm.show() and catch the BadTokenException.
It's not really clean, but it's likely that this is happening when the Activity finishes (maybe the user closes the app from Recents right as the Dialog is loading).
If this were my project, I'd first try adding that if statement you have (although you don't need the MainActivity.this. part; you can just call isFinishing() and isDestroyed() directly). Since you're referencing an Activity Context, this should take care of it.
However, if it still crashes, you should first look into reproducing it. Try getting to just before displayConsentForm() is called, then closing the app from Recents. Play around with the timing and you'll probably reproduce the crash. If not, then just add the try-catch. The Activity isn't displayed, since it's throwing that error, so the user isn't actually in the app.
I have a pretty straight forward setting, setup
my intent filters for my main activity on the manifest
singleTask mode for all my activities (it just have two)
My app have two entry points: one the intent filter will call my MainActivity which start the branch session return the values on the referringParams and I go to the SecondActivity, everyone is happy
the another entry point is the launcher, when I click open the MainActivity do somethhing different because intent.data is empty and Go to SecondActivity, the problem is as follows, after the app is in the SecondActivity and the app goes background (e.g. touch home button) and then tap on some link the MainActivity is launched intent.data is not empty there's a valid url but when my callback is called I got referringParams empty {}
I dont know what is wrong with this. i have spend some hours without success
#Override
public void onStart() {
super.onStart();
Branch branch = Branch.getInstance();
branch.initSession(new Branch.BranchReferralInitListener(){
#Override
public void onInitFinished(JSONObject referringParams, BranchError error) {
if (error == null) {
// here referringParams is a empty {} object
} else {
Log.i("MyApp", error.getMessage());
}
}
}, this.getIntent().getData(), this);
}
#Override
public void onNewIntent(Intent intent) {
this.setIntent(intent);
}
I am not sure what processing you are doing in your Main Activity. The use case you mentioned, should return the Branch link parameters correctly,
If you have the intent filters and the launchMode of the MainActivity, correctly defined
You are overriding the onNewIntent() method in your MainActivity (which I can see from the code snippet you shared)
I created a sample app, which is uploaded here. If you follow the test case you mentioned with this app(i.e. App is backgrounded with the SecondActivity), clicking on a Branch link returns the link parameters correctly.
I am trying to display the Drop-in UI in my app upon clicking a specific button. I have used the guide from Braintree site but for some reason nothing is happening.
Code below:
OnClick function:
public void onClick(View v){
switch (v.getId()){
case R.id.showUI_button:
onBraintreeSubmit(v);
break;
}
}
Drop-in functions:
public void onBraintreeSubmit(View v) {
PaymentRequest paymentRequest = new PaymentRequest()
.clientToken(token)
.amount("$10.00")
.primaryDescription("Awesome payment")
.secondaryDescription("Using the Client SDK")
.submitButtonText("Pay");
startActivityForResult(paymentRequest.getIntent(this), REQUEST_CODE);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE) {
if (resultCode == BraintreePaymentActivity.RESULT_OK) {
PaymentMethodNonce paymentMethodNonce = data.getParcelableExtra(
BraintreePaymentActivity.EXTRA_PAYMENT_METHOD_NONCE
);
String nonce = paymentMethodNonce.getNonce();
// Send the nonce to your server.
}
}
}
I have checked that the token is returned from the server.
I have also tried by setting the onClick via the xml code of the button and removing the onClick from the java file but the result is the same, no UI shown.
The log has only two lines
performCreate Call Injection Manager
Timeline: Activity_idle id:android.os.BinderProxy#etc
Any ideas? If more info is needed to understand better let me know
Actually I found this there is a "BraintreeFragment" set up part. Braintree documentation needs to be more clear on this I think.
https://developers.braintreepayments.com/guides/client-sdk/setup/android/v2
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
mBraintreeFragment = BraintreeFragment.newInstance(this, mAuthorization);
// mBraintreeFragment is ready to use!
} catch (InvalidArgumentException e) {
// There was an issue with your authorization string.
}
}
The above code should work along with the previous code posted. mAuthorization is the token and needs to be valid to show the payment screen (so the variable "token" in the previous code posted which in my code I just have as private but visible from the whole activity).
Try with the test token that they have on their page and if this works then the main setup is ok.
https://developers.braintreepayments.com/start/hello-client/android/v2
For setting up tokens on your server, they have further documentation so that those test tokens work on the sandbox.
I want to connect my game with google play service. i have read documentation on android developer and try to following type-a-number sample and still can't load leaderboard.
i have import baseGameUtils, but i use andengine so i didn't use extends BaseGameActivity from google.
what i have until now:
- GooglePlayServicesUtil.isGooglePlayServicesAvailable(this) return success
- startActivityForResult(pickAccountIntent, REQUEST_CODE_PICK_ACCOUNT); is working well and i got my account name from onActivityResult(..);
- i already put this on my manifest.
<meta-data android:name="com.google.android.gms.games.APP_ID"
android:value="#string/app_id" />
my questions are
1. can i use google play service without extends BaseGameActivity?
2. if i use gameHelper.beginUserInitiatedSignIn(); after i got my account name, i got this on log cat. (what this connected mean? because i still got error on next question)
08-25 00:09:01.890: D/BaseGameActivity(11222): isGooglePlayServicesAvailable returned 0
08-25 00:09:01.890: D/BaseGameActivity(11222): beginUserInitiatedSignIn: starting new sign-in flow.
08-25 00:09:01.890: D/BaseGameActivity(11222): All clients now connected. Sign-in successful.
08-25 00:09:01.890: D/BaseGameActivity(11222): All requested clients connected. Sign-in succeeded!
3 . how do i use connect()? i have read and tried about gameClient and GameClientBuilder but i have no idea how to use that. when i tried run this code.
startActivityForResult(gameHelper.getGamesClient().getAllLeaderboardsIntent(), RC_UNUSED);
i got this log.
08-25 00:09:05.660: E/AndroidRuntime(11222): java.lang.IllegalStateException: Not connected. Call connect() and wait for onConnected() to be called.
4 . to use leaderboard i know i must use code from google play store such as CgkIx****AIQAA. but i didn't found where i must put this code to load leaderboard.
sorry for long question, but i think if there is a sample that only for connect and either access achievement or leaderboard it will answer all my question. please don't tell me to see type-a-number sample, i did that and i need another sample code.
update, my snipped code
public class MainMenu extends Activity
implements OnClickListener, GooglePlayServicesClient.ConnectionCallbacks, GooglePlayServicesClient.OnConnectionFailedListener, GameHelperListener{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_menu);
gameHelper = new GameHelper(this);
}
#Override
public void onClick(View v) {
if(v.equals(loadData)) {
if(gameHelper.isSignedIn()) {
gameHelper.setup(this, GameHelper.CLIENT_GAMES, Scopes.GAMES);
startActivityForResult(gameHelper.getGamesClient().getAllLeaderboardsIntent(), RC_UNUSED);
}
}
else if(v.equals(loginButton)) {
Intent googlePicker = AccountPicker.newChooseAccountIntent(null,null,new String[]{GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE},true,null,null,null,null) ;
startActivityForResult(googlePicker, REQUEST_CODE_PICK_ACCOUNT);
}
}
#Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
if(requestCode==REQUEST_CODE_RECOVER_PLAY_SERVICES) {
if (resultCode == RESULT_CANCELED) {
Toast.makeText(this, "Google Play Services must be installed.", Toast.LENGTH_SHORT).show();
finish();
}
return;
}
else if(requestCode==REQUEST_CODE_PICK_ACCOUNT) {
if (resultCode == RESULT_OK) {
String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
gameHelper.beginUserInitiatedSignIn();
}
else if (resultCode == RESULT_CANCELED) {
Toast.makeText(this, "This application requires a Google account.", Toast.LENGTH_SHORT).show();
finish();
}
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
// this 2 methods not called, is this also because my code is wrong?
#Override
public void onSignInFailed() {
Log.d("rush", "on sign in failed");
}
#Override
public void onSignInSucceeded() {
Log.d("rush", "on sign in succeed");
}
}
Yes. Take a look at the BaseGameActivity source and see that it largely just wraps GameHelper. You can implement the calls to GameHelper yourself - in fact, you can probably copy some code directly from BaseGameActivity. I'm a bit confused, because it appears that your code is already using GameHelper. It looks like you are mixing GameHelper calls with BaseGameActivity calls. You cannot do this, and it will result in... errors like you are getting.
The LogCat you see means that all of your clients are connected. The default call to GameHelper.setup() just requests the Games client. If you aren't using BaseGameActivity and want different clients, do:
gameHelper = new GameHelper(this);
gameHelper.setup(this, GameHelper.CLIENT_GAMES | GameHelper.CLIENT_PLUS);
beginUserInitiatedSignIn() is an asynchronous method with a callback when it finishes. Are you running it that way? GameHelper.GameHelperListener is the interface to implement. If you are using gameHelper, make sure to register the callback. See the this in the setup call above? That's registering the callback (this is my main activity).
As I said above, it looks like you are mixing GameHelper calls with BaseGameActivity calls. The GameHelper that is connected is the BaseGameActivity.mHelper instance, not any GameHelper you might have instantiated. Make sure that if you are using BaseGameActivity that you are not using GameHelper as well.
If you want to display a single leaderboard, use the GamesClient.getLeaderboardIntent(string, int) or method to get the Intent. The string is the code you have (CgkIx****AIQAA).
startActivityForResult(gameHelper.getGamesClient().getLeaderboardIntent(
leaderboard_id, RC_UNUSED);
Again, make sure you are using the correct getGamesClient() method, depending on if you are using BaseGameActivity or GameHelper directly.
Here is basic information how to use GameHelper without BaseGameActivity:
https://developers.google.com/games/services/android/init#using_gamehelper_without_basegameactivity
I want to extend a common security check to nearly every view of my application. To do this, I have made this class
public class ProtectedActivity extends ActivityBase {
boolean isAuthenticated = false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Thread validationThread = new Thread()
{
#Override
public void run()
{
try
{
isAuthenticated = UserService.validateToken();
}
catch (FTNIServiceException e)
{
//eat it
}
finally
{
if (!isAuthenticated)
{
startActivity(new Intent(ProtectedActivity.this, SignInActivity.class));
finish();
}
}
}
};
validationThread.start();
}
}
The logic is simple. Validate the user against my restful api to make sure they are signed in. If they aren't, show them to the signin page.
This works great, because to add the security check, all I need to do is inherit from my ProtectedActivity.
public class MainMenuActivity extends ProtectedActivity{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
The problem is, however, that I periodically receive View not attached to window manager errors. I understand why this is happening. I am starting a new intent in the parent class, and the child lives on. to attempt to alter it's view even though a new intent has started. What is a better way to handle this so that if a user is not authenticated (such as their session expires serverside), it won't error when sending the user to the sign in screen?
Don't you Thread. Use AsyncTask instead which should handle your references to windows correctly.
On a different note, I would change this to a different implementation. Why don't use the Preferences storage on the phone to store some kind token. If the token is not valid then request a new token and all the stuff you are doing currently. This way is better because you don't want to request a REST call every time.
I imagine something like this (pseudo code)
Check if credentials exist in Preference
if(valid) then do nothing
else use AsyncTask and pop up a loader screen "Waiting..."