package org.briarproject.android; import static android.text.InputType.TYPE_CLASS_TEXT; import static android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD; import static android.view.Gravity.CENTER; import static android.view.Gravity.CENTER_HORIZONTAL; import static android.view.View.GONE; import static android.view.View.VISIBLE; import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static android.view.inputmethod.InputMethodManager.HIDE_IMPLICIT_ONLY; import static android.widget.LinearLayout.VERTICAL; import static org.briarproject.android.TestingConstants.PREVENT_SCREENSHOTS; import static org.briarproject.android.util.CommonLayoutParams.MATCH_MATCH; import static org.briarproject.android.util.CommonLayoutParams.WRAP_WRAP; import java.io.File; import java.util.concurrent.Executor; import javax.inject.Inject; import org.briarproject.R; import org.briarproject.android.util.FixedVerticalSpace; import org.briarproject.android.util.LayoutUtils; import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.crypto.CryptoExecutor; import org.briarproject.api.crypto.SecretKey; import org.briarproject.api.db.DatabaseConfig; import org.briarproject.util.StringUtils; import roboguice.activity.RoboActivity; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.text.Editable; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; public class PasswordActivity extends RoboActivity { @Inject @CryptoExecutor private Executor cryptoExecutor; private TextView enterPassword = null; private EditText passwordEntry = null; private Button signInButton = null; private ProgressBar progress = null; // Fields that are accessed from background threads must be volatile @Inject private volatile CryptoComponent crypto; @Inject private volatile DatabaseConfig databaseConfig; @Override public void onCreate(Bundle state) { super.onCreate(state); if (PREVENT_SCREENSHOTS) getWindow().addFlags(FLAG_SECURE); SharedPreferences prefs = getSharedPreferences("db", MODE_PRIVATE); String hex = prefs.getString("key", null); if (hex == null || !databaseConfig.databaseExists()) { // Storage has been deleted - clean up and return to setup prefs.edit().clear().commit(); delete(databaseConfig.getDatabaseDirectory()); setResult(RESULT_CANCELED); startActivity(new Intent(this, SetupActivity.class)); finish(); return; } final byte[] encrypted = StringUtils.fromHexString(hex); LinearLayout layout = new LinearLayout(this); layout.setLayoutParams(MATCH_MATCH); layout.setOrientation(VERTICAL); layout.setGravity(CENTER_HORIZONTAL); int pad = LayoutUtils.getPadding(this); layout.setPadding(pad, pad, pad, pad); enterPassword = new TextView(this); enterPassword.setGravity(CENTER); enterPassword.setTextSize(18); enterPassword.setText(R.string.enter_password); layout.addView(enterPassword); passwordEntry = new EditText(this); passwordEntry.setId(1); passwordEntry.setMaxLines(1); int inputType = TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD; passwordEntry.setInputType(inputType); passwordEntry.setOnEditorActionListener(new OnEditorActionListener() { public boolean onEditorAction(TextView v, int action, KeyEvent e) { validatePassword(encrypted, passwordEntry.getText()); return true; } }); layout.addView(passwordEntry); // Adjusting the padding of buttons and EditTexts has the wrong results layout.addView(new FixedVerticalSpace(this)); signInButton = new Button(this); signInButton.setLayoutParams(WRAP_WRAP); signInButton.setText(R.string.sign_in_button); signInButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { validatePassword(encrypted, passwordEntry.getText()); } }); layout.addView(signInButton); progress = new ProgressBar(this); progress.setLayoutParams(WRAP_WRAP); progress.setIndeterminate(true); progress.setVisibility(GONE); layout.addView(progress); setContentView(layout); } private void delete(File f) { if (f.isFile()) f.delete(); else if (f.isDirectory()) for (File child : f.listFiles()) delete(child); } private void validatePassword(final byte[] encrypted, Editable e) { if (progress == null) return; // Not created yet // Hide the soft keyboard Object o = getSystemService(INPUT_METHOD_SERVICE); ((InputMethodManager) o).toggleSoftInput(HIDE_IMPLICIT_ONLY, 0); // Replace the button with a progress bar signInButton.setVisibility(GONE); progress.setVisibility(VISIBLE); // Decrypt the database key in a background thread final String password = e.toString(); cryptoExecutor.execute(new Runnable() { public void run() { byte[] key = crypto.decryptWithPassword(encrypted, password); if (key == null) { tryAgain(); } else { databaseConfig.setEncryptionKey(new SecretKey(key)); setResultAndFinish(); } } }); } private void tryAgain() { runOnUiThread(new Runnable() { public void run() { enterPassword.setText(R.string.try_again); passwordEntry.setText(""); signInButton.setVisibility(VISIBLE); progress.setVisibility(GONE); } }); } private void setResultAndFinish() { runOnUiThread(new Runnable() { public void run() { setResult(RESULT_OK); finish(); } }); } }