aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/main/java/com/zeapo/pwdstore/git/GitActivity.java
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/com/zeapo/pwdstore/git/GitActivity.java')
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/GitActivity.java624
1 files changed, 624 insertions, 0 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/GitActivity.java b/app/src/main/java/com/zeapo/pwdstore/git/GitActivity.java
new file mode 100644
index 00000000..637d2052
--- /dev/null
+++ b/app/src/main/java/com/zeapo/pwdstore/git/GitActivity.java
@@ -0,0 +1,624 @@
+package com.zeapo.pwdstore.git;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.support.v7.app.ActionBarActivity;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import com.zeapo.pwdstore.R;
+import com.zeapo.pwdstore.UserPreference;
+import com.zeapo.pwdstore.utils.PasswordRepository;
+
+import org.eclipse.jgit.api.CloneCommand;
+import org.eclipse.jgit.api.Git;
+
+import org.apache.commons.io.FileUtils;
+import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+// TODO move the messages to strings.xml
+
+public class GitActivity extends ActionBarActivity {
+ private static final String TAG = "GitAct";
+
+ private Activity activity;
+ private Context context;
+
+ private String protocol;
+ private String connectionMode;
+
+ private File localDir;
+ private String hostname;
+ private String username;
+ private String port;
+
+ private SharedPreferences settings;
+
+ public static final int REQUEST_PULL = 101;
+ public static final int REQUEST_PUSH = 102;
+ public static final int REQUEST_CLONE = 103;
+ public static final int REQUEST_INIT = 104;
+ public static final int EDIT_SERVER = 105;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ context = getApplicationContext();
+ activity = this;
+
+ settings = PreferenceManager.getDefaultSharedPreferences(this.context);
+
+ protocol = settings.getString("git_remote_protocol", "ssh://");
+ connectionMode = settings.getString("git_remote_auth", "ssh-key");
+ int operationCode = getIntent().getExtras().getInt("Operation");
+
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ switch (operationCode) {
+ case REQUEST_CLONE:
+ case EDIT_SERVER:
+ setContentView(R.layout.activity_git_clone);
+ setTitle(R.string.title_activity_git_clone);
+
+ final Spinner protcol_spinner = (Spinner) findViewById(R.id.clone_protocol);
+ final Spinner connection_mode_spinner = (Spinner) findViewById(R.id.connection_mode);
+
+ // init the spinner for connection modes
+ final ArrayAdapter<CharSequence> connection_mode_adapter = ArrayAdapter.createFromResource(this,
+ R.array.connection_modes, android.R.layout.simple_spinner_item);
+ connection_mode_adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ connection_mode_spinner.setAdapter(connection_mode_adapter);
+ connection_mode_spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
+ String selection = ((Spinner) findViewById(R.id.connection_mode)).getSelectedItem().toString();
+ connectionMode = selection;
+ settings.edit().putString("git_remote_auth", selection).apply();
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> adapterView) {
+
+ }
+ });
+
+ // init the spinner for protocols
+ ArrayAdapter<CharSequence> protocol_adapter = ArrayAdapter.createFromResource(this,
+ R.array.clone_protocols, android.R.layout.simple_spinner_item);
+ protocol_adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ protcol_spinner.setAdapter(protocol_adapter);
+ protcol_spinner.setOnItemSelectedListener(
+ new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
+ protocol = ((Spinner) findViewById(R.id.clone_protocol)).getSelectedItem().toString();
+ if (protocol.equals("ssh://")) {
+ ((EditText) findViewById(R.id.clone_uri)).setHint("user@hostname:path");
+
+ ((EditText) findViewById(R.id.server_port)).setHint(R.string.default_ssh_port);
+
+ // select ssh-key auth mode as default and enable the spinner in case it was disabled
+ connection_mode_spinner.setSelection(0);
+ connection_mode_spinner.setEnabled(true);
+ } else {
+ ((EditText) findViewById(R.id.clone_uri)).setHint("hostname/path");
+
+ ((EditText) findViewById(R.id.server_port)).setHint(R.string.default_https_port);
+
+ // select user/pwd auth-mode and disable the spinner
+ connection_mode_spinner.setSelection(1);
+ connection_mode_spinner.setEnabled(false);
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> adapterView) {
+
+ }
+ }
+ );
+
+ if (protocol.equals("ssh://")) {
+ protcol_spinner.setSelection(0);
+ } else {
+ protcol_spinner.setSelection(1);
+ }
+
+ if (connectionMode.equals("ssh-key")) {
+ connection_mode_spinner.setSelection(0);
+ } else {
+ connection_mode_spinner.setSelection(1);
+ }
+
+ // init the server information
+ final EditText server_url = ((EditText) findViewById(R.id.server_url));
+ final EditText server_port = ((EditText) findViewById(R.id.server_port));
+ final EditText server_path = ((EditText) findViewById(R.id.server_path));
+ final EditText server_user = ((EditText) findViewById(R.id.server_user));
+ final EditText server_uri = ((EditText) findViewById(R.id.clone_uri));
+
+ View.OnFocusChangeListener updateListener = new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View view, boolean b) {
+ updateURI();
+ }
+ };
+
+ server_url.setText(settings.getString("git_remote_server", ""));
+ server_port.setText(settings.getString("git_remote_port", ""));
+ server_user.setText(settings.getString("git_remote_username", ""));
+ server_path.setText(settings.getString("git_remote_location", ""));
+
+ server_url.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
+ if (server_url.isFocused())
+ updateURI();
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ }
+ });
+ server_port.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
+ if (server_port.isFocused())
+ updateURI();
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ }
+ });
+ server_user.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
+ if (server_user.isFocused())
+ updateURI();
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ }
+ });
+ server_path.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
+ if (server_path.isFocused())
+ updateURI();
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ }
+ });
+
+ server_uri.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
+ if (server_uri.isFocused())
+ splitURI();
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ }
+ });
+
+ if (operationCode == EDIT_SERVER) {
+ findViewById(R.id.clone_button).setVisibility(View.INVISIBLE);
+ findViewById(R.id.save_button).setVisibility(View.VISIBLE);
+ } else {
+ findViewById(R.id.clone_button).setVisibility(View.VISIBLE);
+ findViewById(R.id.save_button).setVisibility(View.INVISIBLE);
+ }
+
+ updateURI();
+
+ break;
+ case REQUEST_PULL:
+ pullFromRepository();
+ break;
+
+ case REQUEST_PUSH:
+ pushToRepository();
+ break;
+ }
+
+
+ }
+
+ /**
+ * Fills in the server_uri field with the information coming from other fields
+ */
+ private void updateURI() {
+ EditText uri = (EditText) findViewById(R.id.clone_uri);
+ EditText server_url = ((EditText) findViewById(R.id.server_url));
+ EditText server_port = ((EditText) findViewById(R.id.server_port));
+ EditText server_path = ((EditText) findViewById(R.id.server_path));
+ EditText server_user = ((EditText) findViewById(R.id.server_user));
+
+ if (uri != null) {
+ switch (protocol) {
+ case "ssh://": {
+ String hostname =
+ server_user.getText()
+ + "@" +
+ server_url.getText().toString().trim()
+ + ":";
+ if (server_port.getText().toString().equals("22")) {
+ hostname += server_path.getText().toString();
+
+ ((TextView) findViewById(R.id.warn_url)).setVisibility(View.GONE);
+ } else {
+ TextView warn_url = (TextView) findViewById(R.id.warn_url);
+ if (!server_path.getText().toString().matches("/.*") && !server_port.getText().toString().isEmpty()) {
+ warn_url.setText(R.string.warn_malformed_url_port);
+ warn_url.setVisibility(View.VISIBLE);
+ } else {
+ warn_url.setVisibility(View.GONE);
+ }
+ hostname += server_port.getText().toString() + server_path.getText().toString();
+ }
+
+ if (!hostname.equals("@:")) uri.setText(hostname);
+ }
+ break;
+ case "https://": {
+ StringBuilder hostname = new StringBuilder();
+ hostname.append(server_url.getText().toString().trim());
+
+ if (server_port.getText().toString().equals("443")) {
+ hostname.append(server_path.getText().toString());
+
+ ((TextView) findViewById(R.id.warn_url)).setVisibility(View.GONE);
+ } else {
+ hostname.append("/");
+ hostname.append(server_port.getText().toString())
+ .append(server_path.getText().toString());
+ }
+
+ if (!hostname.toString().equals("@/")) uri.setText(hostname);
+ }
+ break;
+ default:
+ break;
+ }
+
+ }
+ }
+
+ /**
+ * Splits the information in server_uri into the other fields
+ */
+ private void splitURI() {
+ EditText server_uri = (EditText) findViewById(R.id.clone_uri);
+ EditText server_url = ((EditText) findViewById(R.id.server_url));
+ EditText server_port = ((EditText) findViewById(R.id.server_port));
+ EditText server_path = ((EditText) findViewById(R.id.server_path));
+ EditText server_user = ((EditText) findViewById(R.id.server_user));
+
+ String uri = server_uri.getText().toString();
+ Pattern pattern = Pattern.compile("(.+)@([\\w\\d\\.]+):([\\d]+)*(.*)");
+ Matcher matcher = pattern.matcher(uri);
+ if (matcher.find()) {
+ int count = matcher.groupCount();
+ if (count > 1) {
+ server_user.setText(matcher.group(1));
+ server_url.setText(matcher.group(2));
+ }
+ if (count == 4) {
+ server_port.setText(matcher.group(3));
+ server_path.setText(matcher.group(4));
+
+ TextView warn_url = (TextView) findViewById(R.id.warn_url);
+ if (!server_path.getText().toString().matches("/.*") && !server_port.getText().toString().isEmpty()) {
+ warn_url.setText(R.string.warn_malformed_url_port);
+ warn_url.setVisibility(View.VISIBLE);
+ } else {
+ warn_url.setVisibility(View.GONE);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateURI();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.git_clone, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+ switch (id) {
+ case R.id.user_pref:
+ try {
+ Intent intent = new Intent(this, UserPreference.class);
+ startActivity(intent);
+ } catch (Exception e) {
+ System.out.println("Exception caught :(");
+ e.printStackTrace();
+ }
+ return true;
+
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * Saves the configuration found in the form
+ */
+ private void saveConfiguration() {
+ // remember the settings
+ SharedPreferences.Editor editor = settings.edit();
+
+ editor.putString("git_remote_server", ((EditText) findViewById(R.id.server_url)).getText().toString());
+ editor.putString("git_remote_location", ((EditText) findViewById(R.id.server_path)).getText().toString());
+ editor.putString("git_remote_username", ((EditText) findViewById(R.id.server_user)).getText().toString());
+ editor.putString("git_remote_protocol", protocol);
+ editor.putString("git_remote_auth", connectionMode);
+ editor.putString("git_remote_port", port);
+ editor.commit();
+ }
+
+ /**
+ * Save the repository information to the shared preferences settings
+ *
+ * @param view
+ */
+ public void saveConfiguration(View view) {
+ saveConfiguration();
+ PasswordRepository.addRemote("origin", ((EditText) findViewById(R.id.clone_uri)).getText().toString(), true);
+ }
+
+ /**
+ * Clones the repository, the directory exists, deletes it
+ *
+ * @param view
+ */
+ public void cloneRepository(View view) {
+ localDir = new File(getApplicationContext().getFilesDir().getAbsoluteFile() + "/store");
+
+ hostname = ((EditText) findViewById(R.id.clone_uri)).getText().toString();
+ port = ((EditText) findViewById(R.id.server_port)).getText().toString();
+ // don't ask the user, take off the protocol that he puts in
+ hostname = hostname.replaceFirst("^.+://", "");
+ ((TextView) findViewById(R.id.clone_uri)).setText(hostname);
+
+ // now cheat a little and prepend the real protocol
+ // jGit does not accept a ssh:// but requires https://
+ if (!protocol.equals("ssh://")) {
+ hostname = protocol + hostname;
+ } else {
+
+ // if the port is explicitly given, jgit requires the ssh://
+ if (!port.isEmpty())
+ hostname = protocol + hostname;
+
+ // did he forget the username?
+ if (!hostname.matches("^.+@.+")) {
+ new AlertDialog.Builder(this).
+ setMessage(activity.getResources().getString(R.string.forget_username_dialog_text)).
+ setPositiveButton(activity.getResources().getString(R.string.dialog_oops), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+
+ }
+ }).
+ show();
+ return;
+ }
+
+ username = hostname.split("@")[0];
+ }
+
+ if (localDir.exists() && localDir.listFiles().length != 0) {
+ new AlertDialog.Builder(this).
+ setTitle(R.string.dialog_delete_title).
+ setMessage(R.string.dialog_delete_msg).
+ setCancelable(false).
+ setPositiveButton(R.string.dialog_delete,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ try {
+ FileUtils.deleteDirectory(localDir);
+ try {
+ new CloneOperation(localDir, activity)
+ .setCommand(hostname)
+ .executeAfterAuthentication(connectionMode, settings.getString("git_remote_username", "git"), new File(getFilesDir() + "/.ssh_key"));
+ } catch (Exception e) {
+ //This is what happens when jgit fails :(
+ //TODO Handle the diffent cases of exceptions
+ e.printStackTrace();
+ }
+ } catch (IOException e) {
+ //TODO Handle the exception correctly if we are unable to delete the directory...
+ e.printStackTrace();
+ } catch (Exception e) {
+ //This is what happens when jgit fails :(
+ //TODO Handle the diffent cases of exceptions
+ }
+
+ dialog.cancel();
+ }
+ }
+ ).
+ setNegativeButton(R.string.dialog_do_not_delete,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ }
+ ).
+ show();
+ } else {
+ saveConfiguration();
+
+ try {
+ new CloneOperation(localDir, activity)
+ .setCommand(hostname)
+ .executeAfterAuthentication(connectionMode, settings.getString("git_remote_username", "git"), new File(getFilesDir() + "/.ssh_key"));
+ } catch (Exception e) {
+ //This is what happens when jgit fails :(
+ //TODO Handle the diffent cases of exceptions
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Pull the latest changes from the remote repository and merges them locally
+ */
+ public void pullFromRepository() {
+ syncRepository(REQUEST_PULL);
+ }
+
+ /**
+ * Pushes the latest changes from the local repository to the remote one
+ */
+ public void pushToRepository() {
+ syncRepository(REQUEST_PUSH);
+ }
+
+ /**
+ * Syncs the local repository with the remote one (either pull or push)
+ * @param operation the operation to execute can be REQUEST_PULL or REQUEST_PUSH
+ */
+ private void syncRepository(int operation) {
+ if (settings.getString("git_remote_username", "").isEmpty() ||
+ settings.getString("git_remote_server", "").isEmpty() ||
+ settings.getString("git_remote_location", "").isEmpty())
+ new AlertDialog.Builder(this)
+ .setMessage(activity.getResources().getString(R.string.set_information_dialog_text))
+ .setPositiveButton(activity.getResources().getString(R.string.dialog_positive), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ Intent intent = new Intent(activity, UserPreference.class);
+ startActivityForResult(intent, REQUEST_PULL);
+ }
+ })
+ .setNegativeButton(activity.getResources().getString(R.string.dialog_negative), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ // do nothing :(
+ setResult(RESULT_OK);
+ finish();
+ }
+ })
+ .show();
+
+ else {
+ // check that the remote origin is here, else add it
+ PasswordRepository.addRemote("origin", hostname, false);
+ GitOperation op;
+
+ if (operation == REQUEST_PULL) {
+ op = new PullOperation(localDir, activity).setCommand();
+ } else if (operation == REQUEST_PUSH) {
+ op = new PushOperation(localDir, activity).setCommand();
+ } else {
+ Log.e(TAG, "Sync operation not recognized : " + operation);
+ return;
+ }
+
+ try {
+ op.executeAfterAuthentication(connectionMode, settings.getString("git_remote_username", "git"), new File(getFilesDir() + "/.ssh_key"));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ protected void onActivityResult(int requestCode, int resultCode,
+ Intent data) {
+ if (resultCode == RESULT_CANCELED) {
+ setResult(RESULT_CANCELED);
+ finish();
+ return;
+ }
+
+ if (resultCode == RESULT_OK) {
+ GitOperation op;
+
+ switch (requestCode) {
+ case REQUEST_PULL:
+ op = new PullOperation(localDir, activity).setCommand();
+ break;
+
+ case REQUEST_PUSH:
+ op = new PushOperation(localDir, activity).setCommand();
+ break;
+
+ case GitOperation.GET_SSH_KEY_FROM_CLONE:
+ op = new CloneOperation(localDir, activity).setCommand(hostname);
+ break;
+ default:
+ Log.e(TAG, "Operation not recognized : " + resultCode);
+ setResult(RESULT_CANCELED);
+ finish();
+ return;
+ }
+
+ try {
+ op.executeAfterAuthentication(connectionMode, settings.getString("git_remote_username", "git"), new File(getFilesDir() + "/.ssh_key"));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+ }
+
+}