/* * Copyright 2013 GitHub Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.mobile.accounts; import static android.content.DialogInterface.OnCancelListener; import static android.view.KeyEvent.ACTION_DOWN; import static android.view.KeyEvent.KEYCODE_ENTER; import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE; import static com.github.mobile.accounts.AccountConstants.*; import static com.github.mobile.accounts.LoginActivity.configureSyncFor; import android.accounts.Account; import android.accounts.AccountManager; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.text.Editable; import android.text.Html; import android.text.TextUtils; import android.text.TextWatcher; import android.text.method.LinkMovementMethod; import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.widget.EditText; import android.widget.TextView; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuItem; import com.github.kevinsawicki.wishlist.ViewFinder; import com.github.mobile.R.id; import com.github.mobile.R.layout; import com.github.mobile.R.menu; import com.github.mobile.R.string; import com.github.mobile.ui.LightProgressDialog; import com.github.mobile.ui.TextWatcherAdapter; import com.github.rtyley.android.sherlock.roboguice.activity.RoboSherlockActivity; import java.io.IOException; import org.eclipse.egit.github.core.User; import org.eclipse.egit.github.core.service.OAuthService; import org.eclipse.egit.github.core.service.UserService; import roboguice.util.RoboAsyncTask; /** * Activity to enter two-factor authentication OTP code */ public class TwoFactorAuthActivity extends RoboSherlockActivity { /** * Create intent to enter two-factor authentication code * * @param username * @param password * @return */ public static Intent createIntent(Context context, String username, String password) { Intent intent = new Intent(context, TwoFactorAuthActivity.class); intent.putExtra(PARAM_USERNAME, username); intent.putExtra(PARAM_PASSWORD, password); return intent; } /** * Exception sent back to calling Activity */ public static final String PARAM_EXCEPTION = "exception"; /** * User name entered in login screen */ public static final String PARAM_USERNAME = "username"; /** * Password entered in login screen */ public static final String PARAM_PASSWORD = "password"; private static final String TAG = "TwoFactorAuthActivity"; private AccountManager accountManager; private EditText otpCodeText; private RoboAsyncTask<User> authenticationTask; private MenuItem loginItem; private String username; private String password; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(layout.login_two_factor_auth); accountManager = AccountManager.get(this); ViewFinder finder = new ViewFinder(this); otpCodeText = finder.find(id.et_otp_code); final Intent intent = getIntent(); username = intent.getStringExtra(PARAM_USERNAME); password = intent.getStringExtra(PARAM_PASSWORD); TextView signupText = finder.find(id.tv_signup); signupText.setMovementMethod(LinkMovementMethod.getInstance()); signupText.setText(Html.fromHtml(getString(string.signup_link_two_factor_auth))); TextWatcher watcher = new TextWatcherAdapter() { @Override public void afterTextChanged(Editable gitDirEditText) { updateEnablement(); } }; otpCodeText.addTextChangedListener(watcher); otpCodeText.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (event != null && ACTION_DOWN == event.getAction() && keyCode == KEYCODE_ENTER && loginEnabled()) { handleLogin(); return true; } else return false; } }); otpCodeText.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (actionId == IME_ACTION_DONE && loginEnabled()) { handleLogin(); return true; } return false; } }); } @Override protected void onResume() { super.onResume(); updateEnablement(); } private boolean loginEnabled() { Editable otpCode = otpCodeText.getText(); return !TextUtils.isEmpty(otpCode) && otpCode.length() == 6; } private void updateEnablement() { if (loginItem != null) loginItem.setEnabled(loginEnabled()); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case id.m_login: handleLogin(); return true; default: return super.onOptionsItemSelected(item); } } @Override public boolean onCreateOptionsMenu(Menu optionsMenu) { getSupportMenuInflater().inflate(menu.login, optionsMenu); loginItem = optionsMenu.findItem(id.m_login); return true; } private void handleLogin() { final String otpCode = otpCodeText.getText().toString(); final AlertDialog dialog = LightProgressDialog.create(this, string.login_activity_authenticating); dialog.setCancelable(true); dialog.setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { if (authenticationTask != null) authenticationTask.cancel(true); } }); dialog.show(); authenticationTask = new RoboAsyncTask<User>(this) { @Override public User call() throws Exception { TwoFactorAuthClient client = new TwoFactorAuthClient(); client.setCredentials(username, password); client.setOtpCode(otpCode); OAuthService service = new OAuthService(client); String authToken = AccountAuthenticator.getAuthorization(service); if (authToken == null) authToken = AccountAuthenticator.createAuthorization(service); client.setOAuth2Token(authToken); User user = new UserService(client).getUser(); Account account = new Account(user.getLogin(), ACCOUNT_TYPE); accountManager.addAccountExplicitly(account, password, null); accountManager.setAuthToken(account, ACCOUNT_TYPE, authToken); configureSyncFor(account); try { new LoginActivity.AccountLoader(TwoFactorAuthActivity.this).call(); } catch (IOException e) { Log.d(TAG, "Exception loading organizations", e); } return user; } @Override protected void onException(Exception e) throws RuntimeException { dialog.dismiss(); Log.d(TAG, "Exception requesting handling two-factor authentication", e); setResult(RESULT_CANCELED, new Intent().putExtra(PARAM_EXCEPTION, e)); finish(); } @Override public void onSuccess(User user) { dialog.dismiss(); setResult(RESULT_OK); finish(); } }; authenticationTask.execute(); } }