Merge pull request #57 from zeapo/feature/refactor-git

Refactoring of the git process
This commit is contained in:
Mohamed Zenadi 2015-01-04 21:49:56 +01:00
commit df8994d84f
17 changed files with 759 additions and 372 deletions

View file

@ -32,6 +32,7 @@ dependencies {
compile project(':libraries:openpgp-api-lib')
compile 'org.eclipse.jgit:org.eclipse.jgit:3.6.0.201411121045-m1'
compile 'org.apache.commons:commons-io:1.3.2'
compile 'com.jayway.android.robotium:robotium-solo:5.2.1'
}
tasks.findAll { // make all tasks whose name starts with 'assemble'...
it.name.startsWith 'assemble'

View file

@ -1,13 +0,0 @@
package com.zeapo.pwdstore;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View file

@ -0,0 +1,111 @@
package com.zeapo.pwdstore;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import com.robotium.solo.Solo;
import com.zeapo.pwdstore.git.GitActivity;
import com.zeapo.pwdstore.utils.PasswordRepository;
import org.apache.commons.io.FileUtils;
import java.io.File;
public class GitActivityClone extends ActivityInstrumentationTestCase2<GitActivity> {
private static final String TAG = "GitActTest";
private Activity gitActivity;
private Instrumentation mInstrumentation;
private SharedPreferences settings;
private Spinner protocolSpinner;
private Spinner connectionModeSpinner;
private EditText uri;
private EditText server_url;
private EditText server_port;
private EditText server_path;
private EditText server_user;
public GitActivityClone() {
super(GitActivity.class);
}
protected void setUp() throws Exception {
super.setUp();
mInstrumentation = getInstrumentation();
Intent intent = new Intent();
intent.putExtra("Operation", GitActivity.REQUEST_CLONE);
setActivityIntent(intent);
gitActivity = getActivity(); // get a references to the app under test
assertNotNull(gitActivity);
settings = PreferenceManager.getDefaultSharedPreferences(gitActivity.getApplicationContext());
uri = (EditText) gitActivity.findViewById(R.id.clone_uri);
server_url = ((EditText) gitActivity.findViewById(R.id.server_url));
server_port = ((EditText) gitActivity.findViewById(R.id.server_port));
server_path = ((EditText) gitActivity.findViewById(R.id.server_path));
server_user = ((EditText) gitActivity.findViewById(R.id.server_user));
protocolSpinner = (Spinner) gitActivity.findViewById(R.id.clone_protocol);
connectionModeSpinner = (Spinner) gitActivity.findViewById(R.id.connection_mode);
assertNotNull(uri);
assertNotNull(server_url);
assertNotNull(server_port);
assertNotNull(server_path);
assertNotNull(server_user);
assertNotNull(protocolSpinner);
assertNotNull(connectionModeSpinner);
assertEquals(protocolSpinner.getSelectedItem(), settings.getString("git_remote_protocol", "ssh://"));
assertEquals(connectionModeSpinner.getSelectedItem(), settings.getString("git_remote_auth", "ssh-key"));
}
public void testCloneSshUser() throws Exception {
final Solo solo = new Solo(getInstrumentation(), getActivity());
FileUtils.deleteDirectory(new File(gitActivity.getFilesDir() + gitActivity.getResources().getString(R.string.store_git)));
// create the repository static variable in PasswordRepository
PasswordRepository.getRepository(new File(gitActivity.getFilesDir() + gitActivity.getResources().getString(R.string.store_git)));
gitActivity.runOnUiThread(new Runnable() {
public void run() {
protocolSpinner.setSelection(0); // ssh://
}
});
mInstrumentation.waitForIdleSync();
solo.clearEditText(server_user);
solo.enterText(server_user, "testpwd");
solo.clearEditText(server_path);
solo.enterText(server_path, "repo-test");
solo.clearEditText(server_url);
solo.enterText(server_url, "192.168.1.28");
mInstrumentation.waitForIdleSync();
gitActivity.runOnUiThread(new Runnable() {
public void run() {
connectionModeSpinner.setSelection(1); // user/pwd
((Button) gitActivity.findViewById(R.id.clone_button)).performClick();
}
});
mInstrumentation.waitForIdleSync();
assertTrue("Could not find the dialog!", solo.searchText(gitActivity.getResources().getString(R.string.passphrase_dialog_title)));
solo.enterText(solo.getEditText("Password"), "test");
solo.clickOnButton(gitActivity.getResources().getString(R.string.dialog_ok));
mInstrumentation.waitForIdleSync();
}
}

View file

@ -0,0 +1,74 @@
package com.zeapo.pwdstore;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.test.ActivityInstrumentationTestCase2;
import android.test.ActivityTestCase;
import android.test.InstrumentationTestCase;
import android.util.Log;
import android.widget.EditText;
import android.widget.Spinner;
import com.zeapo.pwdstore.git.GitActivity;
public class GitActivityTest extends ActivityInstrumentationTestCase2<GitActivity> {
private Activity gitActivity;
private Instrumentation mInstrumentation;
private SharedPreferences settings;
private Spinner protocolSpinner;
private Spinner connectionModeSpinner;
private EditText uri;
private EditText server_url;
private EditText server_port;
private EditText server_path;
private EditText server_user;
public GitActivityTest() {
super(GitActivity.class);
}
protected void setUp() throws Exception {
super.setUp();
mInstrumentation = getInstrumentation();
Intent intent = new Intent();
intent.putExtra("Operation", GitActivity.EDIT_SERVER);
setActivityIntent(intent);
gitActivity = getActivity(); // get a references to the app under test
assertNotNull(gitActivity);
settings = PreferenceManager.getDefaultSharedPreferences(gitActivity.getApplicationContext());
uri = (EditText) gitActivity.findViewById(R.id.clone_uri);
server_url = ((EditText) gitActivity.findViewById(R.id.server_url));
server_port = ((EditText) gitActivity.findViewById(R.id.server_port));
server_path = ((EditText) gitActivity.findViewById(R.id.server_path));
server_user = ((EditText) gitActivity.findViewById(R.id.server_user));
protocolSpinner = (Spinner) gitActivity.findViewById(R.id.clone_protocol);
connectionModeSpinner = (Spinner) gitActivity.findViewById(R.id.connection_mode);
assertEquals(protocolSpinner.getSelectedItem(), settings.getString("git_remote_protocol", "ssh://"));
assertEquals(connectionModeSpinner.getSelectedItem(), settings.getString("git_remote_auth", "ssh-key"));
}
/**
* If we change from ssh protocol to https we automatically switch to username/password auth
* @throws Exception
*/
public void testSpinnerChange() throws Exception{
gitActivity.runOnUiThread(new Runnable() {
public void run() {
protocolSpinner.requestFocus();
protocolSpinner.setSelection(1); // 1 < is https://
}
});
mInstrumentation.waitForIdleSync();
assertEquals(connectionModeSpinner.getSelectedItem(), "username/password"); // 1 < is username/password
}
}

View file

@ -11,7 +11,7 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".git.GitHandler"
<activity android:name=".git.GitActivity"
android:parentActivityName=".PasswordStore">
<meta-data android:name="android.PARENT_ACTIVITY1"
android:value="com.zeapo.pwdstore.PasswordStore" />

View file

@ -18,8 +18,8 @@ import android.view.MenuItem;
import android.view.View;
import com.zeapo.pwdstore.crypto.PgpHandler;
import com.zeapo.pwdstore.git.GitActivity;
import com.zeapo.pwdstore.git.GitAsyncTask;
import com.zeapo.pwdstore.git.GitHandler;
import com.zeapo.pwdstore.utils.PasswordItem;
import com.zeapo.pwdstore.utils.PasswordRecyclerAdapter;
import com.zeapo.pwdstore.utils.PasswordRepository;
@ -151,9 +151,9 @@ public class PasswordStore extends ActionBarActivity {
break;
}
intent = new Intent(this, GitHandler.class);
intent.putExtra("Operation", GitHandler.REQUEST_PUSH);
startActivityForResult(intent, GitHandler.REQUEST_PUSH);
intent = new Intent(this, GitActivity.class);
intent.putExtra("Operation", GitActivity.REQUEST_PUSH);
startActivityForResult(intent, GitActivity.REQUEST_PUSH);
this.leftActivity = true;
return true;
@ -163,9 +163,9 @@ public class PasswordStore extends ActionBarActivity {
break;
}
intent = new Intent(this, GitHandler.class);
intent.putExtra("Operation", GitHandler.REQUEST_PULL);
startActivityForResult(intent, GitHandler.REQUEST_PULL);
intent = new Intent(this, GitActivity.class);
intent.putExtra("Operation", GitActivity.REQUEST_PULL);
startActivityForResult(intent, GitActivity.REQUEST_PULL);
this.leftActivity = true;
return true;
@ -186,9 +186,9 @@ public class PasswordStore extends ActionBarActivity {
}
public void getClone(View view){
Intent intent = new Intent(this, GitHandler.class);
intent.putExtra("Operation", GitHandler.REQUEST_CLONE);
startActivityForResult(intent, GitHandler.REQUEST_CLONE);
Intent intent = new Intent(this, GitActivity.class);
intent.putExtra("Operation", GitActivity.REQUEST_CLONE);
startActivityForResult(intent, GitActivity.REQUEST_CLONE);
}
private void createRepository() {
@ -228,7 +228,7 @@ public class PasswordStore extends ActionBarActivity {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Intent intent = new Intent(activity, UserPreference.class);
startActivityForResult(intent, GitHandler.REQUEST_INIT);
startActivityForResult(intent, GitActivity.REQUEST_INIT);
}
})
.setNegativeButton(this.getResources().getString(R.string.dialog_negative), new DialogInterface.OnClickListener() {
@ -433,7 +433,7 @@ public class PasswordStore extends ActionBarActivity {
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (resultCode == RESULT_OK) {
if (requestCode == GitHandler.REQUEST_CLONE)
if (requestCode == GitActivity.REQUEST_CLONE)
checkLocalRepository();
switch (requestCode) {
@ -446,10 +446,10 @@ public class PasswordStore extends ActionBarActivity {
);
refreshListAdapter();
break;
case GitHandler.REQUEST_INIT:
case GitActivity.REQUEST_INIT:
initRepository(getCurrentFocus());
break;
case GitHandler.REQUEST_PULL:
case GitActivity.REQUEST_PULL:
updateListAdapter();
break;
}

View file

@ -10,9 +10,10 @@ import android.preference.PreferenceFragment;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.MenuItem;
import android.widget.Toast;
import com.zeapo.pwdstore.crypto.PgpHandler;
import com.zeapo.pwdstore.git.GitHandler;
import com.zeapo.pwdstore.git.GitActivity;
import com.zeapo.pwdstore.utils.PasswordRepository;
import org.apache.commons.io.FileUtils;
@ -105,8 +106,8 @@ public class UserPreference extends ActionBarActivity implements Preference.OnPr
break;
case "git_server_info":
{
Intent intent = new Intent(this, GitHandler.class);
intent.putExtra("Operation", GitHandler.EDIT_SERVER);
Intent intent = new Intent(this, GitActivity.class);
intent.putExtra("Operation", GitActivity.EDIT_SERVER);
startActivityForResult(intent, EDIT_GIT_INFO);
}
break;
@ -153,7 +154,7 @@ public class UserPreference extends ActionBarActivity implements Preference.OnPr
{
try {
copySshKey(data.getData());
Log.i("PREF", "Got key");
Toast.makeText(this, this.getResources().getString(R.string.ssh_key_success_dialog_title), Toast.LENGTH_LONG).show();
setResult(RESULT_OK);
finish();
} catch (IOException e)

View file

@ -0,0 +1,67 @@
package com.zeapo.pwdstore.git;
import android.app.Activity;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.Git;
import java.io.File;
public class CloneOperation extends GitOperation {
private static final String TAG = "CLONEOPT";
/**
* Creates a new clone operation
* @param fileDir the git working tree directory
* @param callingActivity the calling activity
*/
public CloneOperation(File fileDir, Activity callingActivity) {
super(fileDir, callingActivity);
}
/**
* Sets the command using the repository uri
* @param uri the uri of the repository
* @return the current object
*/
public CloneOperation setCommand(String uri) {
this.command = Git.cloneRepository().
setCloneAllBranches(true).
setDirectory(repository.getWorkTree()).
setURI(uri);
return this;
}
/**
* sets the authentication for user/pwd scheme
* @param username the username
* @param password the password
* @return the current object
*/
@Override
public CloneOperation setAuthentication(String username, String password) {
super.setAuthentication(username, password);
return this;
}
/**
* sets the authentication for the ssh-key scheme
* @param sshKey the ssh-key file
* @param username the username
* @param passphrase the passphrase
* @return the current object
*/
@Override
public CloneOperation setAuthentication(File sshKey, String username, String passphrase) {
super.setAuthentication(sshKey, username, passphrase);
return this;
}
@Override
public void execute() throws Exception {
if (this.provider != null) {
((CloneCommand) this.command).setCredentialsProvider(this.provider);
}
new GitAsyncTask(callingActivity, true, false, CloneCommand.class).execute(this.command);
}
}

View file

@ -10,22 +10,17 @@ import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.ActionBarActivity;
import android.text.Editable;
import android.text.InputType;
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.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo;
import com.zeapo.pwdstore.R;
import com.zeapo.pwdstore.UserPreference;
import com.zeapo.pwdstore.utils.PasswordRepository;
@ -34,29 +29,17 @@ import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.Git;
import org.apache.commons.io.FileUtils;
import org.eclipse.jgit.api.GitCommand;
import org.eclipse.jgit.api.PullCommand;
import org.eclipse.jgit.api.PushCommand;
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
import org.eclipse.jgit.transport.CredentialItem;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.CredentialsProviderUserInfo;
import org.eclipse.jgit.transport.JschConfigSessionFactory;
import org.eclipse.jgit.transport.OpenSshConfig;
import org.eclipse.jgit.transport.SshSessionFactory;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.eclipse.jgit.util.FS;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
// TODO move the messages to strings.xml
public class GitHandler extends ActionBarActivity {
public class GitActivity extends ActionBarActivity {
private static final String TAG = "GitAct";
private Activity activity;
private Context context;
@ -77,8 +60,6 @@ public class GitHandler extends ActionBarActivity {
public static final int REQUEST_INIT = 104;
public static final int EDIT_SERVER = 105;
private static final int GET_SSH_KEY_FROM_CLONE = 201;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -98,11 +79,11 @@ public class GitHandler extends ActionBarActivity {
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);
@ -131,9 +112,9 @@ public class GitHandler extends ActionBarActivity {
new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
protocol = ((Spinner)findViewById(R.id.clone_protocol)).getSelectedItem().toString();
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.clone_uri)).setHint("user@hostname:path");
((EditText) findViewById(R.id.server_port)).setHint(R.string.default_ssh_port);
@ -141,7 +122,7 @@ public class GitHandler extends ActionBarActivity {
connection_mode_spinner.setSelection(0);
connection_mode_spinner.setEnabled(true);
} else {
((EditText)findViewById(R.id.clone_uri)).setHint("hostname/path");
((EditText) findViewById(R.id.clone_uri)).setHint("hostname/path");
((EditText) findViewById(R.id.server_port)).setHint(R.string.default_https_port);
@ -158,13 +139,24 @@ public class GitHandler extends ActionBarActivity {
}
);
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));
final EditText server_uri = ((EditText) findViewById(R.id.clone_uri));
View.OnFocusChangeListener updateListener = new View.OnFocusChangeListener() {
@Override
@ -180,7 +172,8 @@ public class GitHandler extends ActionBarActivity {
server_url.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { }
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
@ -189,11 +182,13 @@ public class GitHandler extends ActionBarActivity {
}
@Override
public void afterTextChanged(Editable editable) { }
public void afterTextChanged(Editable editable) {
}
});
server_port.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { }
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
@ -202,11 +197,13 @@ public class GitHandler extends ActionBarActivity {
}
@Override
public void afterTextChanged(Editable editable) { }
public void afterTextChanged(Editable editable) {
}
});
server_user.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { }
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
@ -215,11 +212,13 @@ public class GitHandler extends ActionBarActivity {
}
@Override
public void afterTextChanged(Editable editable) { }
public void afterTextChanged(Editable editable) {
}
});
server_path.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { }
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
@ -228,7 +227,8 @@ public class GitHandler extends ActionBarActivity {
}
@Override
public void afterTextChanged(Editable editable) { }
public void afterTextChanged(Editable editable) {
}
});
server_uri.addTextChangedListener(new TextWatcher() {
@ -247,8 +247,7 @@ public class GitHandler extends ActionBarActivity {
}
});
if (operationCode == EDIT_SERVER)
{
if (operationCode == EDIT_SERVER) {
findViewById(R.id.clone_button).setVisibility(View.INVISIBLE);
findViewById(R.id.save_button).setVisibility(View.VISIBLE);
} else {
@ -256,21 +255,24 @@ public class GitHandler extends ActionBarActivity {
findViewById(R.id.save_button).setVisibility(View.INVISIBLE);
}
updateURI();
break;
case REQUEST_PULL:
authenticateAndRun("pullOperation");
pullFromRepository();
break;
case REQUEST_PUSH:
authenticateAndRun("pushOperation");
pushToRepository();
break;
}
}
/** Fills in the server_uri field with the information coming from other fields */
/**
* 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));
@ -278,11 +280,9 @@ public class GitHandler extends ActionBarActivity {
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://":
{
if (uri != null) {
switch (protocol) {
case "ssh://": {
String hostname =
server_user.getText()
+ "@" +
@ -306,8 +306,7 @@ public class GitHandler extends ActionBarActivity {
if (!hostname.equals("@:")) uri.setText(hostname);
}
break;
case "https://":
{
case "https://": {
StringBuilder hostname = new StringBuilder();
hostname.append(server_url.getText().toString().trim());
@ -325,13 +324,15 @@ public class GitHandler extends ActionBarActivity {
}
break;
default:
break;
break;
}
}
}
/** Splits the information in server_uri into the other fields */
/**
* 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));
@ -397,76 +398,35 @@ public class GitHandler extends ActionBarActivity {
return super.onOptionsItemSelected(item);
}
protected class GitConfigSessionFactory extends JschConfigSessionFactory {
/**
* Saves the configuration found in the form
*/
private void saveConfiguration() {
// remember the settings
SharedPreferences.Editor editor = settings.edit();
protected void configure(OpenSshConfig.Host hc, Session session) {
session.setConfig("StrictHostKeyChecking", "no");
}
@Override
protected JSch
getJSch(final OpenSshConfig.Host hc, FS fs) throws JSchException {
JSch jsch = super.getJSch(hc, fs);
jsch.removeAllIdentity();
return jsch;
}
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();
}
protected class SshConfigSessionFactory extends GitConfigSessionFactory {
private String sshKey;
private String passphrase;
public SshConfigSessionFactory(String sshKey, String passphrase) {
this.sshKey = sshKey;
this.passphrase = passphrase;
}
@Override
protected JSch
getJSch(final OpenSshConfig.Host hc, FS fs) throws JSchException {
JSch jsch = super.getJSch(hc, fs);
jsch.removeAllIdentity();
jsch.addIdentity(sshKey);
return jsch;
}
@Override
protected void configure(OpenSshConfig.Host hc, Session session) {
session.setConfig("StrictHostKeyChecking", "no");
CredentialsProvider provider = new CredentialsProvider() {
@Override
public boolean isInteractive() {
return false;
}
@Override
public boolean supports(CredentialItem... items) {
return true;
}
@Override
public boolean get(URIish uri, CredentialItem... items) throws UnsupportedCredentialItem {
for (CredentialItem item : items) {
if (item instanceof CredentialItem.Username) {
((CredentialItem.Username) item).setValue(settings.getString("git_remote_username", "git"));
continue;
}
if (item instanceof CredentialItem.StringType) {
((CredentialItem.StringType) item).setValue(passphrase);
}
}
return true;
}
};
UserInfo userInfo = new CredentialsProviderUserInfo(session, provider);
session.setUserInfo(userInfo);
}
/**
* 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) {
@ -505,8 +465,7 @@ public class GitHandler extends ActionBarActivity {
username = hostname.split("@")[0];
}
if (localDir.exists()) {
if (localDir.exists() && localDir.listFiles().length != 0) {
new AlertDialog.Builder(this).
setTitle(R.string.dialog_delete_title).
setMessage(R.string.dialog_delete_msg).
@ -516,7 +475,15 @@ public class GitHandler extends ActionBarActivity {
public void onClick(DialogInterface dialog, int id) {
try {
FileUtils.deleteDirectory(localDir);
authenticateAndRun("cloneOperation");
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();
@ -538,8 +505,12 @@ public class GitHandler extends ActionBarActivity {
).
show();
} else {
saveConfiguration();
try {
authenticateAndRun("cloneOperation");
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
@ -548,53 +519,28 @@ public class GitHandler extends ActionBarActivity {
}
}
/**
* Pull the latest changes from the remote repository and merges them locally
*/
public void pullFromRepository() {
syncRepository(REQUEST_PULL);
}
/**
* Save the repository information to the shared preferences settings
* @param view
* Pushes the latest changes from the local repository to the remote one
*/
public void saveConfiguration(View view) {
// 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();
PasswordRepository.addRemote("origin", ((EditText) findViewById(R.id.clone_uri)).getText().toString(), true);
public void pushToRepository() {
syncRepository(REQUEST_PUSH);
}
public void cloneOperation(UsernamePasswordCredentialsProvider provider) {
// 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();
CloneCommand cmd = Git.cloneRepository().
setCredentialsProvider(provider).
setCloneAllBranches(true).
setDirectory(localDir).
setURI(hostname);
new GitAsyncTask(activity, true, false, CloneCommand.class).execute(cmd);
}
public void pullOperation(UsernamePasswordCredentialsProvider provider) {
/**
* 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() )
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() {
@ -616,185 +562,23 @@ public class GitHandler extends ActionBarActivity {
else {
// check that the remote origin is here, else add it
PasswordRepository.addRemote("origin", settings.getString("git_remote_username", "user")
+ "@" +
settings.getString("git_remote_server", "server.com").trim()
+ ":" +
settings.getString("git_remote_location", "path/to/repository"), false);
PasswordRepository.addRemote("origin", hostname, false);
GitOperation op;
GitCommand cmd;
if (provider != null)
cmd = new Git(PasswordRepository.getRepository(new File("")))
.pull()
.setRebase(true)
.setRemote("origin")
.setCredentialsProvider(provider);
else
cmd = new Git(PasswordRepository.getRepository(new File("")))
.pull()
.setRebase(true)
.setRemote("origin");
new GitAsyncTask(activity, true, false, PullCommand.class).execute(cmd);
}
}
public void pushOperation(UsernamePasswordCredentialsProvider provider) {
if (settings.getString("git_remote_username", "user").isEmpty() ||
settings.getString("git_remote_server", "server.com").trim().isEmpty() ||
settings.getString("git_remote_location", "path/to/repository").isEmpty() )
new AlertDialog.Builder(this)
.setMessage("You have to set the information about the server before synchronizing with the server")
.setPositiveButton("On my way!", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Intent intent = new Intent(activity, UserPreference.class);
startActivityForResult(intent, REQUEST_PUSH);
}
})
.setNegativeButton("Nah... later", 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", settings.getString("git_remote_username", "user")
+ "@" +
settings.getString("git_remote_server", "server.com").trim()
+ ":" +
settings.getString("git_remote_location", "path/to/repository"), false);
GitCommand cmd;
if (provider != null)
cmd = new Git(PasswordRepository.getRepository(new File("")))
.push()
.setPushAll()
.setRemote("origin")
.setCredentialsProvider(provider);
else
cmd = new Git(PasswordRepository.getRepository(new File("")))
.push()
.setPushAll()
.setRemote("origin");
new GitAsyncTask(activity, true, false, PushCommand.class).execute(cmd);
}
}
/** Finds the method and provides it with authentication paramters via invokeWithAuthentication */
private void authenticateAndRun(String operation) {
try {
invokeWithAuthentication(this, GitHandler.class.getMethod(operation, UsernamePasswordCredentialsProvider.class));
} catch (Exception e) {
e.printStackTrace();
}
}
/** Calls a method encapsulating a GitCommand and providing it with authentication parameters
*
* @param activity
* @param method
*/
private void invokeWithAuthentication(final GitHandler activity, final Method method) {
if (connectionMode.equalsIgnoreCase("ssh-key")) {
final File sshKey = new File(getFilesDir() + "/.ssh_key");
if (!sshKey.exists()) {
new AlertDialog.Builder(this)
.setMessage(activity.getResources().getString(R.string.ssh_preferences_dialog_text))
.setTitle(activity.getResources().getString(R.string.ssh_preferences_dialog_title))
.setPositiveButton(activity.getResources().getString(R.string.dialog_ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
try {
Intent intent = new Intent(getApplicationContext(), UserPreference.class);
intent.putExtra("operation", "get_ssh_key");
startActivityForResult(intent, GET_SSH_KEY_FROM_CLONE);
} catch (Exception e) {
System.out.println("Exception caught :(");
e.printStackTrace();
}
}
}).setNegativeButton(activity.getResources().getString(R.string.dialog_cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
// Do nothing...
}
}).show();
if (operation == REQUEST_PULL) {
op = new PullOperation(localDir, activity).setCommand();
} else if (operation == REQUEST_PUSH) {
op = new PushOperation(localDir, activity).setCommand();
} else {
final EditText passphrase = new EditText(activity);
passphrase.setHint("Passphrase");
passphrase.setWidth(LinearLayout.LayoutParams.MATCH_PARENT);
passphrase.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
new AlertDialog.Builder(activity)
.setTitle(activity.getResources().getString(R.string.passphrase_dialog_title))
.setMessage(activity.getResources().getString(R.string.passphrase_dialog_text))
.setView(passphrase)
.setPositiveButton(activity.getResources().getString(R.string.dialog_ok), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
SshSessionFactory.setInstance(new GitConfigSessionFactory());
try {
JschConfigSessionFactory sessionFactory = new SshConfigSessionFactory(sshKey.getAbsolutePath(), passphrase.getText().toString());
SshSessionFactory.setInstance(sessionFactory);
try {
method.invoke(activity, (UsernamePasswordCredentialsProvider) null);
} catch (Exception e){
e.printStackTrace();
}
} catch (Exception e){
e.printStackTrace();
}
}
}).setNegativeButton(activity.getResources().getString(R.string.dialog_cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Do nothing.
}
}).show();
Log.e(TAG, "Sync operation not recognized : " + operation);
return;
}
} else {
final EditText password = new EditText(activity);
password.setHint("Password");
password.setWidth(LinearLayout.LayoutParams.MATCH_PARENT);
password.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
new AlertDialog.Builder(activity)
.setTitle(activity.getResources().getString(R.string.passphrase_dialog_title))
.setMessage(activity.getResources().getString(R.string.password_dialog_text))
.setView(password)
.setPositiveButton(activity.getResources().getString(R.string.dialog_ok), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
SshSessionFactory.setInstance(new GitConfigSessionFactory());
try {
method.invoke(activity,
new UsernamePasswordCredentialsProvider(
settings.getString("git_remote_username", "git"),
password.getText().toString())
);
} catch (Exception e){
e.printStackTrace();
}
}
}).setNegativeButton(activity.getResources().getString(R.string.dialog_cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Do nothing.
}
}).show();
try {
op.executeAfterAuthentication(connectionMode, settings.getString("git_remote_username", "git"), new File(getFilesDir() + "/.ssh_key"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
@ -807,16 +591,31 @@ public class GitHandler extends ActionBarActivity {
}
if (resultCode == RESULT_OK) {
GitOperation op;
switch (requestCode) {
case REQUEST_PULL:
authenticateAndRun("pullOperation");
op = new PullOperation(localDir, activity).setCommand();
break;
case REQUEST_PUSH:
authenticateAndRun("pushOperation");
op = new PushOperation(localDir, activity).setCommand();
break;
case GET_SSH_KEY_FROM_CLONE:
authenticateAndRun("cloneOperation");
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();
}
}

View file

@ -0,0 +1,170 @@
package com.zeapo.pwdstore.git;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.support.annotation.Nullable;
import android.text.InputType;
import android.util.Log;
import android.widget.EditText;
import android.widget.LinearLayout;
import com.zeapo.pwdstore.R;
import com.zeapo.pwdstore.UserPreference;
import com.zeapo.pwdstore.git.config.GitConfigSessionFactory;
import com.zeapo.pwdstore.git.config.SshConfigSessionFactory;
import com.zeapo.pwdstore.utils.PasswordRepository;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.GitCommand;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.JschConfigSessionFactory;
import org.eclipse.jgit.transport.SshSessionFactory;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import java.io.File;
public abstract class GitOperation {
private static final String TAG = "GitOpt";
public static final int GET_SSH_KEY_FROM_CLONE = 201;
protected final Repository repository;
protected final Activity callingActivity;
protected UsernamePasswordCredentialsProvider provider;
protected GitCommand command;
/**
* Creates a new git operation
*
* @param fileDir the git working tree directory
* @param callingActivity the calling activity
*/
public GitOperation(File fileDir, Activity callingActivity) {
this.repository = PasswordRepository.getRepository(fileDir);
this.callingActivity = callingActivity;
}
/**
* Sets the authentication using user/pwd scheme
*
* @param username the username
* @param password the password
* @return the current object
*/
public GitOperation setAuthentication(String username, String password) {
SshSessionFactory.setInstance(new GitConfigSessionFactory());
this.provider = new UsernamePasswordCredentialsProvider(username, password);
return this;
}
/**
* Sets the authentication using ssh-key scheme
*
* @param sshKey the ssh-key file
* @param username the username
* @param passphrase the passphrase
* @return the current object
*/
public GitOperation setAuthentication(File sshKey, String username, String passphrase) {
JschConfigSessionFactory sessionFactory = new SshConfigSessionFactory(sshKey.getAbsolutePath(), username, passphrase);
SshSessionFactory.setInstance(sessionFactory);
this.provider = null;
return this;
}
/**
* Executes the GitCommand in an async task
*
* @throws Exception
*/
public abstract void execute() throws Exception;
/**
* Executes the GitCommand in an async task after creating the authentication
*
* @param connectionMode the server-connection mode
* @param username the username
* @param sshKey the ssh-key file
* @throws Exception
*/
public void executeAfterAuthentication(String connectionMode, final String username, @Nullable final File sshKey) throws Exception {
if (connectionMode.equalsIgnoreCase("ssh-key")) {
if (sshKey == null || !sshKey.exists()) {
new AlertDialog.Builder(callingActivity)
.setMessage(callingActivity.getResources().getString(R.string.ssh_preferences_dialog_text))
.setTitle(callingActivity.getResources().getString(R.string.ssh_preferences_dialog_title))
.setPositiveButton(callingActivity.getResources().getString(R.string.dialog_ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
try {
// Ask the UserPreference to provide us with the ssh-key
// onResult has to be handled by the callingActivity
Intent intent = new Intent(callingActivity.getApplicationContext(), UserPreference.class);
intent.putExtra("operation", "get_ssh_key");
callingActivity.startActivityForResult(intent, GET_SSH_KEY_FROM_CLONE);
} catch (Exception e) {
System.out.println("Exception caught :(");
e.printStackTrace();
}
}
}).setNegativeButton(callingActivity.getResources().getString(R.string.dialog_cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
// Do nothing...
}
}).show();
} else {
final EditText passphrase = new EditText(callingActivity);
passphrase.setHint("Passphrase");
passphrase.setWidth(LinearLayout.LayoutParams.MATCH_PARENT);
passphrase.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
new AlertDialog.Builder(callingActivity)
.setTitle(callingActivity.getResources().getString(R.string.passphrase_dialog_title))
.setMessage(callingActivity.getResources().getString(R.string.passphrase_dialog_text))
.setView(passphrase)
.setPositiveButton(callingActivity.getResources().getString(R.string.dialog_ok), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
try {
// Authenticate using the ssh-key and then execute the command
setAuthentication(sshKey, username, passphrase.getText().toString()).execute();
} catch (Exception e) {
e.printStackTrace();
}
}
}).setNegativeButton(callingActivity.getResources().getString(R.string.dialog_cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Do nothing.
}
}).show();
}
} else {
final EditText password = new EditText(callingActivity);
password.setHint("Password");
password.setWidth(LinearLayout.LayoutParams.MATCH_PARENT);
password.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
new AlertDialog.Builder(callingActivity)
.setTitle(callingActivity.getResources().getString(R.string.passphrase_dialog_title))
.setMessage(callingActivity.getResources().getString(R.string.password_dialog_text))
.setView(password)
.setPositiveButton(callingActivity.getResources().getString(R.string.dialog_ok), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// authenticate using the user/pwd and then execute the command
try {
setAuthentication(username, password.getText().toString()).execute();
} catch (Exception e) {
e.printStackTrace();
}
}
}).setNegativeButton(callingActivity.getResources().getString(R.string.dialog_cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Do nothing.
}
}).show();
}
}
}

View file

@ -0,0 +1,41 @@
package com.zeapo.pwdstore.git;
import android.app.Activity;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.PullCommand;
import java.io.File;
public class PullOperation extends GitOperation {
/**
* Creates a new git operation
*
* @param fileDir the git working tree directory
* @param callingActivity the calling activity
*/
public PullOperation(File fileDir, Activity callingActivity) {
super(fileDir, callingActivity);
}
/**
* Sets the command
* @return the current object
*/
public PullOperation setCommand() {
this.command = new Git(repository)
.pull()
.setRebase(true)
.setRemote("origin");
return this;
}
@Override
public void execute() throws Exception {
if (this.provider != null) {
((PullCommand) this.command).setCredentialsProvider(this.provider);
}
new GitAsyncTask(callingActivity, true, false, PullCommand.class).execute(this.command);
}
}

View file

@ -0,0 +1,41 @@
package com.zeapo.pwdstore.git;
import android.app.Activity;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.PushCommand;
import java.io.File;
public class PushOperation extends GitOperation {
/**
* Creates a new git operation
*
* @param fileDir the git working tree directory
* @param callingActivity the calling activity
*/
public PushOperation(File fileDir, Activity callingActivity) {
super(fileDir, callingActivity);
}
/**
* Sets the command
* @return the current object
*/
public PushOperation setCommand() {
this.command = new Git(repository)
.push()
.setPushAll()
.setRemote("origin");
return this;
}
@Override
public void execute() throws Exception {
if (this.provider != null) {
((PushCommand) this.command).setCredentialsProvider(this.provider);
}
new GitAsyncTask(callingActivity, true, false, PushCommand.class).execute(this.command);
}
}

View file

@ -0,0 +1,24 @@
package com.zeapo.pwdstore.git.config;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import org.eclipse.jgit.transport.JschConfigSessionFactory;
import org.eclipse.jgit.transport.OpenSshConfig;
import org.eclipse.jgit.util.FS;
public class GitConfigSessionFactory extends JschConfigSessionFactory {
protected void configure(OpenSshConfig.Host hc, Session session) {
session.setConfig("StrictHostKeyChecking", "no");
}
@Override
protected JSch
getJSch(final OpenSshConfig.Host hc, FS fs) throws JSchException {
JSch jsch = super.getJSch(hc, fs);
jsch.removeAllIdentity();
return jsch;
}
}

View file

@ -0,0 +1,70 @@
package com.zeapo.pwdstore.git.config;
import android.content.SharedPreferences;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo;
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
import org.eclipse.jgit.transport.CredentialItem;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.CredentialsProviderUserInfo;
import org.eclipse.jgit.transport.OpenSshConfig;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.util.FS;
public class SshConfigSessionFactory extends GitConfigSessionFactory {
private String sshKey;
private String passphrase;
private String username;
public SshConfigSessionFactory(String sshKey, String username, String passphrase) {
this.sshKey = sshKey;
this.passphrase = passphrase;
this.username = username;
}
@Override
protected JSch
getJSch(final OpenSshConfig.Host hc, FS fs) throws JSchException {
JSch jsch = super.getJSch(hc, fs);
jsch.removeAllIdentity();
jsch.addIdentity(sshKey);
return jsch;
}
@Override
protected void configure(OpenSshConfig.Host hc, Session session) {
session.setConfig("StrictHostKeyChecking", "no");
CredentialsProvider provider = new CredentialsProvider() {
@Override
public boolean isInteractive() {
return false;
}
@Override
public boolean supports(CredentialItem... items) {
return true;
}
@Override
public boolean get(URIish uri, CredentialItem... items) throws UnsupportedCredentialItem {
for (CredentialItem item : items) {
if (item instanceof CredentialItem.Username) {
((CredentialItem.Username) item).setValue(username);
continue;
}
if (item instanceof CredentialItem.StringType) {
((CredentialItem.StringType) item).setValue(passphrase);
}
}
return true;
}
};
UserInfo userInfo = new CredentialsProviderUserInfo(session, provider);
session.setUserInfo(userInfo);
}
}

View file

@ -6,7 +6,7 @@
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.zeapo.pwdstore.git.GitHandler"
tools:context="com.zeapo.pwdstore.git.GitActivity"
android:background="@android:color/white">
<LinearLayout
android:layout_width="match_parent"

View file

@ -1,7 +1,7 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:pwstore="http://schemas.android.com/apk/res-auto"
tools:context="com.zeapo.pwdstore.git.GitHandler" >
tools:context="com.zeapo.pwdstore.git.GitActivity" >
<item android:id="@+id/user_pref"
android:title="@string/action_settings"
android:orderInCategory="100"

View file

@ -12,7 +12,7 @@
<string name="dialog_delete_msg">Target directory already exist. Current version support only a single store. Do you want to delete the current password store directory?</string>
<string name="dialog_delete">Delete directory</string>
<string name="dialog_do_not_delete">Cancel</string>
<string name="title_activity_git_clone">Clone repository</string>
<string name="title_activity_git_clone">Repository information</string>
<!-- Password Store -->
<string name="creation_dialog_text">Please clone or create a new repository below before trying to add a password or any synchronization operation.</string>
@ -41,7 +41,7 @@
<!-- Git Async Task -->
<string name="running_dialog_text">Running command...</string>
<string name="jgit_error_dialog_title">Internal exception occurred</string>
<string name="jgit_error_dialog_text">Message from jgit: /n</string>
<string name="jgit_error_dialog_text">Message from jgit: \n</string>
<!-- Git Handler -->
<string name="read_only_dialog_text">You are about to use a read-only repository, you will not be able to push to it</string>
@ -108,6 +108,7 @@
<string name="pref_password_dialog_title">Set the time you want the password to be in clipboard</string>
<string name="pref_copy_title">Automatically Copy Password</string>
<string name="pref_copy_dialog_title">Automatically copy the password to the clipboard after decryption was successful.</string>
<string name="ssh_key_success_dialog_title" translatable="false">SSH-key imported</string>
<string name="ssh_key_error_dialog_title">Error while trying to import the ssh-key</string>
<string name="ssh_key_error_dialog_text">Message : \n</string>
<string name="pref_recursive_filter">Recursive filtering</string>