package com.dozuki.ifixit.ui.auth;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
import android.graphics.Typeface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.dozuki.ifixit.App;
import com.dozuki.ifixit.R;
import com.dozuki.ifixit.model.dozuki.Site;
import com.dozuki.ifixit.model.user.User;
import com.dozuki.ifixit.ui.BaseActivity;
import com.dozuki.ifixit.ui.BaseDialogFragment;
import com.dozuki.ifixit.util.api.Api;
import com.dozuki.ifixit.util.api.ApiCall;
import com.dozuki.ifixit.util.api.ApiError;
import com.dozuki.ifixit.util.api.ApiEvent;
import com.google.android.gms.auth.GoogleAuthException;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.UserRecoverableAuthException;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.SignInButton;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.Scope;
import com.google.android.gms.plus.Plus;
import com.squareup.otto.Subscribe;
import java.io.IOException;
public class LoginFragment extends BaseDialogFragment implements OnClickListener,
GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
private static final int OPEN_ID_REQUEST_CODE = 4;
private Button mLogin;
private Button mRegister;
private SignInButton mGoogleLogin;
//private ImageButton mYahooLogin;
private EditText mEmail;
private EditText mPassword;
private TextView mErrorText;
private ProgressBar mLoadingSpinner;
private ApiCall mCurAPICall;
private boolean mHasRegisterBtn = true;
private boolean mFailedSsoLogin = false;
private GoogleApiClient mGoogleApiClient;
private boolean mGoogleLoginInProgress = false;
private boolean mGoogleLoginClicked = false;
@Subscribe
public void onLogin(ApiEvent.Login event) {
handleLogin(event);
}
@Subscribe
public void onUserInfo(ApiEvent.UserInfo event) {
handleLogin(event);
}
@Subscribe
public void onSiteInfo(ApiEvent.SiteInfo event) {
if (!event.hasError()) {
App.get().setSite(event.getResult());
initGoogleLoginButton();
} else {
Api.getErrorDialog(getActivity(), event).show();
}
}
private void handleLogin(ApiEvent<User> event) {
if (!event.hasError()) {
User user = event.getResult();
((App)getActivity().getApplication()).login(user, getEmail(),
getPassword(), true);
dismiss();
} else {
enable(true);
ApiError error = event.getError();
mLoadingSpinner.setVisibility(View.GONE);
// Show input fields
mEmail.setVisibility(View.VISIBLE);
mPassword.setVisibility(View.VISIBLE);
mErrorText.setVisibility(View.VISIBLE);
mErrorText.setText(error.mMessage);
}
}
/**
* Required for restoring fragments
*/
public LoginFragment() {}
public static LoginFragment newInstance() {
LoginFragment frag = new LoginFragment();
frag.setStyle(DialogFragment.STYLE_NO_TITLE,
android.R.style.Theme_Holo_Light_Dialog);
return frag;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
App.get().setIsLoggingIn(true);
Site site = App.get().getSite();
mHasRegisterBtn = site.mPublicRegistration;
if (site.checkForGoogleLogin()) {
// Get the clientid so we can perform Google login.
if (site.mGoogleOAuth2Clientid == null) {
Api.call(getActivity(), ApiCall.siteInfo());
}
mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(Plus.API)
.addScope(new Scope("email"))
.addScope(new Scope("profile"))
.build();
}
if (!site.mStandardAuth) {
Intent intent = new Intent(getActivity(), OpenIDActivity.class);
intent.putExtra(OpenIDActivity.SINGLE_SIGN_ON, true);
startActivityForResult(intent, OPEN_ID_REQUEST_CODE);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.login_fragment, container, false);
mEmail = (EditText)view.findViewById(R.id.edit_email);
mPassword = (EditText)view.findViewById(R.id.edit_password);
mPassword.setTypeface(Typeface.DEFAULT);
mLogin = (Button)view.findViewById(R.id.signin_button);
mRegister = (Button)view.findViewById(R.id.register_button);
mGoogleLogin = (SignInButton)view.findViewById(R.id.use_google_login_button);
//mYahooLogin = (ImageButton)view.findViewById(R.id.use_yahoo_login_button);
mLogin.setOnClickListener(this);
if (mHasRegisterBtn) {
mRegister.setOnClickListener(this);
mGoogleLogin.setOnClickListener(this);
//mYahooLogin.setOnClickListener(this);
} else {
mRegister.setVisibility(View.GONE);
mGoogleLogin.setVisibility(View.GONE);
//mYahooLogin.setVisibility(View.GONE);
}
initGoogleLoginButton();
mErrorText = (TextView)view.findViewById(R.id.login_error_text);
mErrorText.setVisibility(View.GONE);
mLoadingSpinner = (ProgressBar)view.findViewById(R.id.login_loading_bar);
mLoadingSpinner.setVisibility(View.GONE);
return view;
}
private void initGoogleLoginButton() {
if (App.get().getSite().hasGoogleLogin()) {
// Display the Google Login button because we have a valid clientid.
mGoogleLogin.setVisibility(View.VISIBLE);
}
}
@Override
public void onStart() {
super.onStart();
App.sendScreenView("/login");
}
@Override
public void onStop() {
super.onStop();
if (mGoogleApiClient != null) {
mGoogleApiClient.disconnect();
}
}
@Override
public void onResume() {
super.onResume();
if (mFailedSsoLogin) {
// Dismiss the dialog because SSO login failed.
getDialog().cancel();
}
}
private void login() {
String email = getEmail();
String password = getPassword();
if (email.length() > 0 && password.length() > 0 ) {
// Hide input fields
mEmail.setVisibility(View.GONE);
mPassword.setVisibility(View.GONE);
mLoadingSpinner.setVisibility(View.VISIBLE);
enable(false);
mCurAPICall = ApiCall.login(email, password);
Api.call(getActivity(), mCurAPICall);
} else {
if (email.length() < 1) {
mEmail.requestFocus();
showKeyboard();
} else {
mPassword.requestFocus();
showKeyboard();
}
mErrorText.setText(R.string.empty_field_error);
mErrorText.setVisibility(View.VISIBLE);
}
}
private String getEmail() {
return mEmail.getText().toString();
}
private String getPassword() {
return mPassword.getText().toString();
}
private void showKeyboard() {
InputMethodManager in = (InputMethodManager)getActivity()
.getSystemService(Context.INPUT_METHOD_SERVICE);
in.toggleSoftInput(InputMethodManager.SHOW_FORCED,
InputMethodManager.HIDE_IMPLICIT_ONLY);
}
private void enable(boolean enabled) {
mEmail.setEnabled(enabled);
mPassword.setEnabled(enabled);
mLogin.setEnabled(enabled);
if (mHasRegisterBtn) {
mRegister.setEnabled(enabled);
mGoogleLogin.setEnabled(enabled);
//mYahooLogin.setEnabled(enabled);
}
}
@Override
public void onClick(View v) {
Intent intent;
switch (v.getId()) {
case R.id.use_google_login_button:
if (!mGoogleApiClient.isConnecting()) {
mGoogleLoginClicked = true;
if (mGoogleApiClient.isConnected()) {
mGoogleApiClient.reconnect();
} else {
mGoogleApiClient.connect();
}
}
break;
case R.id.use_yahoo_login_button:
intent = new Intent(getActivity(), OpenIDActivity.class);
intent.putExtra(OpenIDActivity.LOGIN_METHOD, OpenIDActivity.YAHOO_LOGIN);
startActivityForResult(intent, OPEN_ID_REQUEST_CODE);
break;
case R.id.register_button:
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
fragmentManager.beginTransaction()
.remove(this)
.add(RegisterFragment.newInstance(), null)
.commit();
break;
case R.id.signin_button:
InputMethodManager in = (InputMethodManager)getActivity()
.getSystemService(Context.INPUT_METHOD_SERVICE);
in.hideSoftInputFromWindow(mEmail.getApplicationWindowToken(),
InputMethodManager.HIDE_NOT_ALWAYS);
login();
break;
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == OPEN_ID_REQUEST_CODE && resultCode == Activity.RESULT_OK && data != null) {
mLoadingSpinner.setVisibility(View.VISIBLE);
String session = data.getStringExtra(OpenIDActivity.SESSION);
enable(false);
mCurAPICall = ApiCall.userInfo(session);
Api.call(getActivity(), mCurAPICall);
} else if (requestCode == BaseActivity.GOOGLE_SIGN_IN_REQUEST_CODE) {
handleGoogleSignInActivityResult(requestCode, resultCode, data);
}
if (!App.get().getSite().mStandardAuth && resultCode != Activity.RESULT_OK) {
/**
* Single sign on failed. There aren't any login alternatives so we need
* to close the dialog. We can't do that here because onResume hasn't been
* called yet. This sets a flag which is used in onResume to kill the dialog.
*/
mFailedSsoLogin = true;
}
}
@Subscribe
public void onGoogleSignInActivityResult(GoogleSignInActivityResult result) {
handleGoogleSignInActivityResult(result.mRequestCode, result.mResultCode,
result.mData);
}
private void handleGoogleSignInActivityResult(int requestCode, int resultCode,
Intent data) {
mGoogleLoginInProgress = false;
if (resultCode == Activity.RESULT_OK) {
mLoadingSpinner.setVisibility(View.VISIBLE);
enable(false);
// If the client isn't connected we need to do that before doing anything else.
// This will trigger the rest of the process.
if (!mGoogleApiClient.isConnected()) {
mGoogleApiClient.connect();
return;
}
// Otherwise get the account name and request a token that we will use to
// make an auth token.
String accountName = Plus.AccountApi.getAccountName(mGoogleApiClient);
String scopes = getGoogleOAuthScopes();
new RetrieveGoogleOAuthCodeTask().execute(accountName, scopes);
}
}
@Override
public void onCancel(DialogInterface dialog) {
App.get().cancelLogin();
}
@Override
public void onConnected(Bundle connectionHint) {
// We've resolved any connection errors. mGoogleApiClient can be used to
// access Google APIs on behalf of the user.
mGoogleLoginClicked = false;
String accountName = Plus.AccountApi.getAccountName(mGoogleApiClient);
String scopes = getGoogleOAuthScopes();
new RetrieveGoogleOAuthCodeTask().execute(accountName, scopes);
}
@Override
public void onConnectionSuspended(int cause) {
mGoogleApiClient.connect();
}
@Override
public void onConnectionFailed(ConnectionResult result) {
if (!result.hasResolution()) {
GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), getActivity(), 0).show();
return;
}
if (mGoogleLoginClicked && !mGoogleLoginInProgress && result.hasResolution()) {
try {
mGoogleLoginInProgress = true;
result.startResolutionForResult(getActivity(), BaseActivity.GOOGLE_SIGN_IN_REQUEST_CODE);
} catch (IntentSender.SendIntentException e) {
// The intent was canceled before it was sent. Return to the default
// state and attempt to connect to get an updated ConnectionResult.
mGoogleLoginInProgress = false;
mGoogleApiClient.connect();
}
}
}
private String getGoogleOAuthScopes() {
String clientId = App.get().getSite().mGoogleOAuth2Clientid;
return "oauth2:server:client_id:" + clientId + ":api_scope:" +
"https://www.googleapis.com/auth/userinfo.profile " +
"https://www.googleapis.com/auth/userinfo.email";
}
@Subscribe
public void onTokenReceived(GoogleOAuthCodeEvent event) {
mCurAPICall = ApiCall.googleOauthLogin(event.mCode);
Api.call(getActivity(), mCurAPICall);
}
private static class GoogleOAuthCodeEvent {
public String mCode;
public GoogleOAuthCodeEvent(String code) {
mCode = code;
}
}
public static class GoogleSignInActivityResult {
public int mRequestCode;
public int mResultCode;
public Intent mData;
public GoogleSignInActivityResult(int requestCode, int resultCode, Intent data) {
mRequestCode = requestCode;
mResultCode = resultCode;
mData = data;
}
}
private class RetrieveGoogleOAuthCodeTask extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... params) {
String accountName = params[0];
String scopes = params[1];
String token = null;
try {
Context context = getActivity().getApplicationContext();
token = GoogleAuthUtil.getToken(context, accountName, scopes);
// We only use the token once so we need to clear it from the local cache
// so we don't get the same one next time which would then be invalid.
GoogleAuthUtil.clearToken(context, token);
} catch (IOException e) {
Log.e("LoginFragment", e.getMessage());
} catch (UserRecoverableAuthException e) {
startActivityForResult(e.getIntent(), BaseActivity.GOOGLE_SIGN_IN_REQUEST_CODE);
} catch (GoogleAuthException e) {
Log.e("LoginFragment", e.getMessage());
}
return token;
}
@Override
protected void onPostExecute(String token) {
super.onPostExecute(token);
if (token != null) {
App.getBus().post(new GoogleOAuthCodeEvent(token));
}
}
}
}