Digits android authcallback not called on success/failure otp verification - android

I am creating a digitsauthconfig like this:
private DigitsAuthConfig createDigitsAuthConfig() {
return new DigitsAuthConfig.Builder()
.withAuthCallBack(createAuthCallback())
.withPhoneNumber("+91")
.withThemeResId(R.style.CustomDigitsTheme)
.build();
}
Where authcallback is returned by:
private AuthCallback createAuthCallback() {
return new AuthCallback() {
#Override
public void success(DigitsSession session, String phoneNumber) {
doIfSuccessfulOtpVerification();
}
#Override
public void failure(DigitsException exception) {
doIfNotSuccessfulOtpVerification();
}
};
}
I initiate the process using a button with event listener:
digitsButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Digits.authenticate(createDigitsAuthConfig());
}
});
The problem is, once my phone number is verified, it goes back to the activity where the button is displayed and does nothing. Technically, the authcallback is never called, doesn't matter successful or not. But if I click the button again, the authcallback is called without repeating the verification step. So right now I am required to click the button twice.
What is the way around it?

Finally i got solution for that issue, May it will help you too also.
You need to remove the ActiveSession, before calling the setCallback(authCallback) like mentioned as below.It will remove the existing session(if you already entered your phone number and got an OTP) from digits. This session will will not allows you to make another session to generate an OTP. So, we have to remove it. And it will work if there is no any previous sessions.
DigitsAuthButton digitsButton = (DigitsAuthButton) findViewById(R.id.auth_button);
Digits.getSessionManager().clearActiveSession();
digitsButton.setCallback(((WyzConnectApp) getApplication()).getAuthCallback());

Digits changed the way it reference the AuthCallback passed in the Digits#authenticate call.
Now Digits holds a weak reference (to avoid a memory leak), unless you hold a strong reference, that AuthCallback will be garbage collected and the result of the flow will be never propagated.
You need to define the AuthCallback in the Application context and then use this callback in your activity and it should work.
Please check the documentation on how to do this

I know its late but may be helpful for a beginner.To verify that the session is already active, please put this code in your onCreate of that activity in which Digits phone number verification button is initialised:
TwitterAuthConfig authConfig = new TwitterAuthConfig(TWITTER_KEY, TWITTER_SECRET);
Fabric.with(this, new TwitterCore(authConfig), new Digits.Builder().build());
if (Digits.getActiveSession() != null) {
Intent ss = new Intent(CurrentActivity.this, SecondActivity.class);
startActivity(ss);
finish();
}

Related

GDPR Consent form crashing: WindowManager BadTokenException

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.

After phone number validation from Digits by twitter, I am getting two instance of my app in recent activity

Hiii, I have looked on Digits documentation over and over for this fix but couldn't find it. Whenever I open Digit mobile validation screen and then close it, I am getting two instance of my app in recent screen.
Here is how I am setting up Digits.
On Application,
Fabric.with(this, new TwitterCore(authConfig), new Digits());
And at activity,
DigitsAuthButton digitsButton = (DigitsAuthButton) findViewById(R.id.btn_phone_auth);
digitsButton.setAuthTheme(R.style.CustomDigitsTheme);
AuthCallback authCallback = new AuthCallback() {
#Override
public void success(DigitsSession session, String phoneNumber) {
//validated number processing
};
digitsButton.setCallback(authCallback);
Need help on this.
This issue is been resolved in Digits v1.10.0.
compile('com.digits.sdk.android:digits:1.10.0#aar') {
transitive = true;
}

Android Espresso 2 end phone call

In my app, I have a button that pops up a dialog "Call xxxx-xxxx" Yes / No. After clicking Yes the number shall be called.
This is the test code:
#Test
public void testPhoneButton() {
clickContactTab();
ViewInteraction phoneButtonInteraction = Espresso.onView(ViewMatchers.withId(R.id.button_phone));
phoneButtonInteraction.perform(ViewActions.scrollTo());
phoneButtonInteraction.perform(ViewActions.click());
Espresso.onView(ViewMatchers.withText(R.string.dialog_phone_title)).inRoot(RootMatchers.isDialog()).check(ViewAssertions.matches(ViewMatchers.isDisplayed()));
Espresso.onView(ViewMatchers.withId(android.R.id.button2)).perform(ViewActions.click());
Intents.assertNoUnverifiedIntents();
phoneButtonInteraction.perform(ViewActions.click());
Espresso.onView(ViewMatchers.withId(android.R.id.button1)).perform(ViewActions.click());
Intents.intended(Matchers.allOf(IntentMatchers.hasAction(Intent.ACTION_CALL), IntentMatchers.hasData(Uri.parse("tel:" + tel))));
}
Everything works fine, but how can I cancel the phone call after the test?
yogurtearls answer works for me, thanks:
#Test
public void testPhoneButton() {
clickContactTab();
ViewInteraction phoneButtonInteraction = Espresso.onView(ViewMatchers.withId(R.id.button_phone));
phoneButtonInteraction.perform(ViewActions.scrollTo());
phoneButtonInteraction.perform(ViewActions.click());
Espresso.onView(ViewMatchers.withText(R.string.dialog_phone_title)).inRoot(RootMatchers.isDialog()).check(ViewAssertions.matches(ViewMatchers.isDisplayed()));
Espresso.onView(ViewMatchers.withId(android.R.id.button2)).perform(ViewActions.click());
Intents.assertNoUnverifiedIntents();
phoneButtonInteraction.perform(ViewActions.click());
Intent stubIntent = new Intent();
Instrumentation.ActivityResult stubResult = new Instrumentation.ActivityResult(Activity.RESULT_OK, stubIntent);
Intents.intending(IntentMatchers.hasAction(Intent.ACTION_CALL)).respondWith(stubResult);
Espresso.onView(ViewMatchers.withId(android.R.id.button1)).perform(ViewActions.click());
Intents.intended(Matchers.allOf(IntentMatchers.hasAction(Intent.ACTION_CALL), IntentMatchers.hasData(Uri.parse("tel:" + tel))));
}
You should use Intent stubbing.
You can avoid actually bringing up the dialer, and instead check that the right intent was sent.
Before you click the yes button, call intendING .

How would you write a test for this method using Mockito?

Please assist in this. I can't seem to create a suitable test for this method:
protected void startInterfacing() {
mLiveAuthClient.login(mView.context(), Arrays.asList(SCOPES), new LiveAuthListener() {
#Override
public void onAuthComplete(final LiveStatus liveStatus, final LiveConnectSession liveConnectSession,
Object o) {
// Login successful and user consented, now retrieve user ID and connect with backend server
getUserIdAndConnectWithBackendServer(liveConnectSession, mLiveAuthClient);
}
#Override
public void onAuthError(LiveAuthException e, Object o) {
// We failed to authenticate with auth service... show error
if (e.getError().equals("access_denied") ||
e.getMessage().equals("The user cancelled the login operation.")) {
// When user cancels in either the login or consent page, we need to log the user out to enable
// the login screen again when trying to connect later on
logUserOut(mLiveAuthClient, false);
} else {
onErrorOccured();
}
}
});
}
I'll explain abit what goes on here:
I'm trying to authenticate my client and log into OneDrive.
The method starts with a call to the Live SDK's login method. That SDK object is given to me from outside this class. So I can basically mock it.
Here's what I'm struggling with:
I do not need to test the call to the login method because it is not mine. I do need to test the call to getUserIdAndConnectWithBackendServer() inside onAuthComplete. But this method requires a liveConnectSession object. How do I provide that? It is given to me on the onAuthComplete method.
How do I mock the calls to onAuthComplete and onAuthError? I read about ArgumentCaptor but when I use that, I need to provide the arguments to those methods when I call the actual method.
For instance, argument.getValue().onAuthComplete() requires me to add arguments to this call. What do I actually provide here?
Here is the next method which is roughly the same but has its own issues:
protected void getUserIdAndConnectWithBackendServer(final LiveConnectSession liveConnectSession, final LiveAuthClient
authClient) {
final LiveConnectClient connectClient = new LiveConnectClient(liveConnectSession);
connectClient.getAsync("me", new LiveOperationListener() {
#Override
public void onComplete(LiveOperation liveOperation) {
// We got a result. Check for errors...
JSONObject result = liveOperation.getResult();
if (result.has(ERROR)) {
JSONObject error = result.optJSONObject(ERROR);
String code = error.optString(CODE);
String message = error.optString(MESSAGE);
onErrorOccured();
} else {
connectWithBackend(result, liveConnectSession, authClient);
}
}
#Override
public void onError(LiveOperationException e, LiveOperation liveOperation) {
// We failed to retrieve user information.... show error
onErrorOccured();
logUserOut(authClient, false);
}
});
}
In here I would like to mock the JSONObject for instance. But how do I call the onComplete method, or the onError method. And what would I provide as the arguments the methods provide me with. LiveOperation for instance?
Thank you!!
The solution I eventually used was to use mockito's doAnswer() structure.
This enabled me to get the callback argument and call one of its methods.
Another solution was to use an ArgumentCator.

Android - security through inheritence

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..."

Categories

Resources