Add Spotless to regulate codestyle (#550)

* Add Spotless to regulate codestyle

* treewide: Run spotless

* Add spotlessCheck to CI test

Signed-off-by: Harsh Shandilya <msfjarvis@gmail.com>
This commit is contained in:
Harsh Shandilya 2019-10-02 18:04:18 +05:30 committed by GitHub
parent 9a1a54a6fc
commit f1f59dc1ed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 907 additions and 682 deletions

View file

@ -8,4 +8,4 @@ jobs:
- name: setup-android
uses: msfjarvis/setup-android@0.2
with:
gradleTasks: build test lintDebug -Dpre-dex=false
gradleTasks: spotlessCheck build test lintDebug -Dpre-dex=false

View file

@ -40,7 +40,7 @@ class LaunchActivity : AppCompatActivity() {
decryptIntent.putExtra("LAST_CHANGED_TIMESTAMP", intent.getLongExtra("LAST_CHANGED_TIMESTAMP", 0L))
decryptIntent.putExtra("OPERATION", "DECRYPT")
startActivity(decryptIntent)
}else {
} else {
startActivity(Intent(this, PasswordStore::class.java))
}
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)

View file

@ -6,7 +6,6 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
@ -14,22 +13,21 @@ import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.zeapo.pwdstore.utils.PasswordItem;
import com.zeapo.pwdstore.utils.PasswordRecyclerAdapter;
import com.zeapo.pwdstore.utils.PasswordRepository;
import java.io.File;
import java.util.ArrayList;
import java.util.Stack;
/**
* A fragment representing a list of Items.
* <p/>
* Large screen devices (such as tablets) are supported by replacing the ListView
* with a GridView.
* <p/>
*
* <p>Large screen devices (such as tablets) are supported by replacing the ListView with a
* GridView.
*
* <p>
*/
public class PasswordFragment extends Fragment {
@ -43,11 +41,10 @@ public class PasswordFragment extends Fragment {
private SharedPreferences settings;
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
* Mandatory empty constructor for the fragment manager to instantiate the fragment (e.g. upon
* screen orientation changes).
*/
public PasswordFragment() {
}
public PasswordFragment() {}
@Override
public void onCreate(Bundle savedInstanceState) {
@ -58,13 +55,19 @@ public class PasswordFragment extends Fragment {
passListStack = new Stack<>();
scrollPosition = new Stack<>();
pathStack = new Stack<>();
recyclerAdapter = new PasswordRecyclerAdapter((PasswordStore) requireActivity(), mListener,
PasswordRepository.getPasswords(new File(path), PasswordRepository.getRepositoryDirectory(requireContext()), getSortOrder()));
recyclerAdapter =
new PasswordRecyclerAdapter(
(PasswordStore) requireActivity(),
mListener,
PasswordRepository.getPasswords(
new File(path),
PasswordRepository.getRepositoryDirectory(requireContext()),
getSortOrder()));
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
public View onCreateView(
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.password_recycler_view, container, false);
// use a linear layout manager
@ -74,7 +77,8 @@ public class PasswordFragment extends Fragment {
recyclerView.setLayoutManager(mLayoutManager);
// use divider
recyclerView.addItemDecoration(new DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL));
recyclerView.addItemDecoration(
new DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL));
// Set the adapter
recyclerView.setAdapter(recyclerAdapter);
@ -90,55 +94,77 @@ public class PasswordFragment extends Fragment {
public void onAttach(final Context context) {
super.onAttach(context);
try {
mListener = item -> {
if (item.getType() == PasswordItem.TYPE_CATEGORY) {
// push the current password list (non filtered plz!)
passListStack.push(pathStack.isEmpty() ?
PasswordRepository.getPasswords(PasswordRepository.getRepositoryDirectory(context), getSortOrder()) :
PasswordRepository.getPasswords(pathStack.peek(), PasswordRepository.getRepositoryDirectory(context), getSortOrder()));
//push the category were we're going
pathStack.push(item.getFile());
scrollPosition.push(recyclerView.getVerticalScrollbarPosition());
mListener =
item -> {
if (item.getType() == PasswordItem.TYPE_CATEGORY) {
// push the current password list (non filtered plz!)
passListStack.push(
pathStack.isEmpty()
? PasswordRepository.getPasswords(
PasswordRepository.getRepositoryDirectory(
context),
getSortOrder())
: PasswordRepository.getPasswords(
pathStack.peek(),
PasswordRepository.getRepositoryDirectory(
context),
getSortOrder()));
// push the category were we're going
pathStack.push(item.getFile());
scrollPosition.push(recyclerView.getVerticalScrollbarPosition());
recyclerView.scrollToPosition(0);
recyclerAdapter.clear();
recyclerAdapter.addAll(PasswordRepository.getPasswords(item.getFile(), PasswordRepository.getRepositoryDirectory(context), getSortOrder()));
recyclerView.scrollToPosition(0);
recyclerAdapter.clear();
recyclerAdapter.addAll(
PasswordRepository.getPasswords(
item.getFile(),
PasswordRepository.getRepositoryDirectory(context),
getSortOrder()));
((AppCompatActivity) requireActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} else {
if (getArguments().getBoolean("matchWith", false)) {
((PasswordStore) requireActivity()).matchPasswordWithApp(item);
} else {
((PasswordStore) requireActivity()).decryptPassword(item);
}
}
};
((AppCompatActivity) requireActivity())
.getSupportActionBar()
.setDisplayHomeAsUpEnabled(true);
} else {
if (getArguments().getBoolean("matchWith", false)) {
((PasswordStore) requireActivity()).matchPasswordWithApp(item);
} else {
((PasswordStore) requireActivity()).decryptPassword(item);
}
}
};
} catch (ClassCastException e) {
throw new ClassCastException(context + " must implement OnFragmentInteractionListener");
}
}
/**
* clears the adapter content and sets it back to the root view
*/
/** clears the adapter content and sets it back to the root view */
public void updateAdapter() {
passListStack.clear();
pathStack.clear();
scrollPosition.clear();
recyclerAdapter.clear();
recyclerAdapter.addAll(PasswordRepository.getPasswords(PasswordRepository.getRepositoryDirectory(requireContext()), getSortOrder()));
recyclerAdapter.addAll(
PasswordRepository.getPasswords(
PasswordRepository.getRepositoryDirectory(requireContext()),
getSortOrder()));
((AppCompatActivity) requireActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(false);
((AppCompatActivity) requireActivity())
.getSupportActionBar()
.setDisplayHomeAsUpEnabled(false);
}
/**
* refreshes the adapter with the latest opened category
*/
/** refreshes the adapter with the latest opened category */
public void refreshAdapter() {
recyclerAdapter.clear();
recyclerAdapter.addAll(pathStack.isEmpty() ?
PasswordRepository.getPasswords(PasswordRepository.getRepositoryDirectory(requireContext()), getSortOrder()) :
PasswordRepository.getPasswords(pathStack.peek(), PasswordRepository.getRepositoryDirectory(requireContext()), getSortOrder()));
recyclerAdapter.addAll(
pathStack.isEmpty()
? PasswordRepository.getPasswords(
PasswordRepository.getRepositoryDirectory(requireContext()),
getSortOrder())
: PasswordRepository.getPasswords(
pathStack.peek(),
PasswordRepository.getRepositoryDirectory(requireContext()),
getSortOrder()));
}
/**
@ -158,13 +184,19 @@ public class PasswordFragment extends Fragment {
* recursively filters a directory and extract all the matching items
*
* @param filter the filter to apply
* @param dir the directory to filter
* @param dir the directory to filter
*/
private void recursiveFilter(String filter, File dir) {
// on the root the pathStack is empty
ArrayList<PasswordItem> passwordItems = dir == null ?
PasswordRepository.getPasswords(PasswordRepository.getRepositoryDirectory(requireContext()), getSortOrder()) :
PasswordRepository.getPasswords(dir, PasswordRepository.getRepositoryDirectory(requireContext()), getSortOrder());
ArrayList<PasswordItem> passwordItems =
dir == null
? PasswordRepository.getPasswords(
PasswordRepository.getRepositoryDirectory(requireContext()),
getSortOrder())
: PasswordRepository.getPasswords(
dir,
PasswordRepository.getRepositoryDirectory(requireContext()),
getSortOrder());
boolean rec = settings.getBoolean("filter_recursively", true);
for (PasswordItem item : passwordItems) {
@ -181,12 +213,9 @@ public class PasswordFragment extends Fragment {
}
}
/**
* Goes back one level back in the path
*/
/** Goes back one level back in the path */
public void popBack() {
if (passListStack.isEmpty())
return;
if (passListStack.isEmpty()) return;
recyclerView.scrollToPosition(scrollPosition.pop());
recyclerAdapter.clear();
@ -200,10 +229,8 @@ public class PasswordFragment extends Fragment {
* @return the current directory
*/
public File getCurrentDir() {
if (pathStack.isEmpty())
return PasswordRepository.getRepositoryDirectory(requireContext());
else
return pathStack.peek();
if (pathStack.isEmpty()) return PasswordRepository.getRepositoryDirectory(requireContext());
else return pathStack.peek();
}
public boolean isNotEmpty() {

View file

@ -13,27 +13,19 @@ import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.AppCompatEditText;
import androidx.appcompat.widget.AppCompatTextView;
import androidx.fragment.app.DialogFragment;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.zeapo.pwdstore.pwgen.PasswordGenerator;
import java.util.ArrayList;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
/**
* A placeholder fragment containing a simple view.
*/
/** A placeholder fragment containing a simple view. */
public class PasswordGeneratorDialogFragment extends DialogFragment {
public PasswordGeneratorDialogFragment() {
}
public PasswordGeneratorDialogFragment() {}
@NotNull
@SuppressLint("SetTextI18n")
@ -42,13 +34,17 @@ public class PasswordGeneratorDialogFragment extends DialogFragment {
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireContext());
final Activity callingActivity = requireActivity();
LayoutInflater inflater = callingActivity.getLayoutInflater();
@SuppressLint("InflateParams") final View view = inflater.inflate(R.layout.fragment_pwgen, null);
Typeface monoTypeface = Typeface.createFromAsset(callingActivity.getAssets(), "fonts/sourcecodepro.ttf");
@SuppressLint("InflateParams")
final View view = inflater.inflate(R.layout.fragment_pwgen, null);
Typeface monoTypeface =
Typeface.createFromAsset(callingActivity.getAssets(), "fonts/sourcecodepro.ttf");
builder.setView(view);
SharedPreferences prefs
= requireActivity().getApplicationContext().getSharedPreferences("PasswordGenerator", Context.MODE_PRIVATE);
SharedPreferences prefs =
requireActivity()
.getApplicationContext()
.getSharedPreferences("PasswordGenerator", Context.MODE_PRIVATE);
CheckBox checkBox = view.findViewById(R.id.numerals);
checkBox.setChecked(!prefs.getBoolean("0", false));
@ -74,38 +70,53 @@ public class PasswordGeneratorDialogFragment extends DialogFragment {
AppCompatTextView passwordText = view.findViewById(R.id.passwordText);
passwordText.setTypeface(monoTypeface);
builder.setPositiveButton(getResources().getString(R.string.dialog_ok), (dialog, which) -> {
EditText edit = callingActivity.findViewById(R.id.crypto_password_edit);
edit.setText(passwordText.getText());
});
builder.setPositiveButton(
getResources().getString(R.string.dialog_ok),
(dialog, which) -> {
EditText edit = callingActivity.findViewById(R.id.crypto_password_edit);
edit.setText(passwordText.getText());
});
builder.setNegativeButton(getResources().getString(R.string.dialog_cancel), (dialog, which) -> {
});
builder.setNegativeButton(
getResources().getString(R.string.dialog_cancel), (dialog, which) -> {});
builder.setNeutralButton(getResources().getString(R.string.pwgen_generate), null);
final AlertDialog ad = builder.setTitle(this.getResources().getString(R.string.pwgen_title)).create();
ad.setOnShowListener(dialog -> {
setPreferences();
try {
passwordText.setText(PasswordGenerator.generate(requireActivity().getApplicationContext()).get(0));
} catch (PasswordGenerator.PasswordGeneratorExeption e) {
Toast.makeText(requireActivity(), e.getMessage(), Toast.LENGTH_SHORT).show();
passwordText.setText("");
}
final AlertDialog ad =
builder.setTitle(this.getResources().getString(R.string.pwgen_title)).create();
ad.setOnShowListener(
dialog -> {
setPreferences();
try {
passwordText.setText(
PasswordGenerator.generate(
requireActivity().getApplicationContext())
.get(0));
} catch (PasswordGenerator.PasswordGeneratorExeption e) {
Toast.makeText(requireActivity(), e.getMessage(), Toast.LENGTH_SHORT)
.show();
passwordText.setText("");
}
Button b = ad.getButton(AlertDialog.BUTTON_NEUTRAL);
b.setOnClickListener(v -> {
setPreferences();
try {
passwordText.setText(PasswordGenerator.generate(callingActivity.getApplicationContext()).get(0));
} catch (PasswordGenerator.PasswordGeneratorExeption e) {
Toast.makeText(requireActivity(), e.getMessage(), Toast.LENGTH_SHORT).show();
passwordText.setText("");
}
});
});
Button b = ad.getButton(AlertDialog.BUTTON_NEUTRAL);
b.setOnClickListener(
v -> {
setPreferences();
try {
passwordText.setText(
PasswordGenerator.generate(
callingActivity.getApplicationContext())
.get(0));
} catch (PasswordGenerator.PasswordGeneratorExeption e) {
Toast.makeText(
requireActivity(),
e.getMessage(),
Toast.LENGTH_SHORT)
.show();
passwordText.setText("");
}
});
});
return ad;
}
@ -133,10 +144,10 @@ public class PasswordGeneratorDialogFragment extends DialogFragment {
EditText editText = getDialog().findViewById(R.id.lengthNumber);
try {
int length = Integer.valueOf(editText.getText().toString());
PasswordGenerator.setPrefs(requireActivity().getApplicationContext(), preferences, length);
PasswordGenerator.setPrefs(
requireActivity().getApplicationContext(), preferences, length);
} catch (NumberFormatException e) {
PasswordGenerator.setPrefs(requireActivity().getApplicationContext(), preferences);
}
}
}

View file

@ -18,7 +18,6 @@ import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatTextView;
@ -28,7 +27,6 @@ import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.preference.PreferenceManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.snackbar.Snackbar;
import com.zeapo.pwdstore.crypto.PgpActivity;
@ -38,14 +36,6 @@ import com.zeapo.pwdstore.git.GitOperation;
import com.zeapo.pwdstore.utils.PasswordItem;
import com.zeapo.pwdstore.utils.PasswordRecyclerAdapter;
import com.zeapo.pwdstore.utils.PasswordRepository;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
@ -53,6 +43,12 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
public class PasswordStore extends AppCompatActivity {
@ -65,10 +61,10 @@ public class PasswordStore extends AppCompatActivity {
public static final int REQUEST_CODE_EDIT = 9916;
public static final int REQUEST_CODE_SELECT_FOLDER = 9917;
private static final String TAG = PasswordStore.class.getName();
private final static int CLONE_REPO_BUTTON = 401;
private final static int NEW_REPO_BUTTON = 402;
private final static int HOME = 403;
private final static int REQUEST_EXTERNAL_STORAGE = 50;
private static final int CLONE_REPO_BUTTON = 401;
private static final int NEW_REPO_BUTTON = 402;
private static final int HOME = 403;
private static final int REQUEST_EXTERNAL_STORAGE = 50;
private SharedPreferences settings;
private Activity activity;
private PasswordFragment plist;
@ -78,16 +74,16 @@ public class PasswordStore extends AppCompatActivity {
private static boolean isPrintable(char c) {
Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
return (!Character.isISOControl(c)) &&
block != null &&
block != Character.UnicodeBlock.SPECIALS;
return (!Character.isISOControl(c))
&& block != null
&& block != Character.UnicodeBlock.SPECIALS;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// open search view on search key, or Ctr+F
if ((keyCode == KeyEvent.KEYCODE_SEARCH ||
keyCode == KeyEvent.KEYCODE_F && event.isCtrlPressed())
if ((keyCode == KeyEvent.KEYCODE_SEARCH
|| keyCode == KeyEvent.KEYCODE_F && event.isCtrlPressed())
&& !searchItem.isActionViewExpanded()) {
searchItem.expandActionView();
return true;
@ -116,8 +112,11 @@ public class PasswordStore extends AppCompatActivity {
// If user opens app with permission granted then revokes and returns,
// prevent attempt to create password list fragment
if (savedInstanceState != null && (!settings.getBoolean("git_external", false)
|| ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)) {
if (savedInstanceState != null
&& (!settings.getBoolean("git_external", false)
|| ContextCompat.checkSelfPermission(
activity, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED)) {
savedInstanceState = null;
}
super.onCreate(savedInstanceState);
@ -129,27 +128,39 @@ public class PasswordStore extends AppCompatActivity {
super.onResume();
// do not attempt to checkLocalRepository() if no storage permission: immediate crash
if (settings.getBoolean("git_external", false)) {
if (ContextCompat.checkSelfPermission(activity,
Manifest.permission.READ_EXTERNAL_STORAGE)
if (ContextCompat.checkSelfPermission(
activity, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity,
Manifest.permission.READ_EXTERNAL_STORAGE)) {
if (ActivityCompat.shouldShowRequestPermissionRationale(
activity, Manifest.permission.READ_EXTERNAL_STORAGE)) {
// TODO: strings.xml
Snackbar snack = Snackbar.make(findViewById(R.id.main_layout), "The store is on the sdcard but the app does not have permission to access it. Please give permission.",
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.dialog_ok, view -> ActivityCompat.requestPermissions(activity,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
REQUEST_EXTERNAL_STORAGE));
Snackbar snack =
Snackbar.make(
findViewById(R.id.main_layout),
"The store is on the sdcard but the app does not have permission to access it. Please give permission.",
Snackbar.LENGTH_INDEFINITE)
.setAction(
R.string.dialog_ok,
view ->
ActivityCompat.requestPermissions(
activity,
new String[] {
Manifest.permission
.READ_EXTERNAL_STORAGE
},
REQUEST_EXTERNAL_STORAGE));
snack.show();
View view = snack.getView();
AppCompatTextView tv = view.findViewById(com.google.android.material.R.id.snackbar_text);
AppCompatTextView tv =
view.findViewById(com.google.android.material.R.id.snackbar_text);
tv.setTextColor(Color.WHITE);
tv.setMaxLines(10);
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(activity,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
ActivityCompat.requestPermissions(
activity,
new String[] {Manifest.permission.READ_EXTERNAL_STORAGE},
REQUEST_EXTERNAL_STORAGE);
}
} else {
@ -162,11 +173,11 @@ public class PasswordStore extends AppCompatActivity {
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
public void onRequestPermissionsResult(
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
// If request is cancelled, the result arrays are empty.
if (requestCode == REQUEST_EXTERNAL_STORAGE) {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
checkLocalRepository();
}
}
@ -179,33 +190,35 @@ public class PasswordStore extends AppCompatActivity {
searchItem = menu.findItem(R.id.action_search);
searchView = (SearchView) searchItem.getActionView();
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String s) {
return true;
}
searchView.setOnQueryTextListener(
new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String s) {
return true;
}
@Override
public boolean onQueryTextChange(String s) {
filterListAdapter(s);
return true;
}
});
@Override
public boolean onQueryTextChange(String s) {
filterListAdapter(s);
return true;
}
});
// When using the support library, the setOnActionExpandListener() method is
// static and accepts the MenuItem object as an argument
searchItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
refreshListAdapter();
return true;
}
searchItem.setOnActionExpandListener(
new MenuItem.OnActionExpandListener() {
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
refreshListAdapter();
return true;
}
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
return true;
}
});
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
return true;
}
});
return super.onCreateOptionsMenu(menu);
}
@ -217,9 +230,10 @@ public class PasswordStore extends AppCompatActivity {
int id = item.getItemId();
Intent intent;
final MaterialAlertDialogBuilder initBefore = new MaterialAlertDialogBuilder(this)
.setMessage(this.getResources().getString(R.string.creation_dialog_text))
.setPositiveButton(this.getResources().getString(R.string.dialog_ok), null);
final MaterialAlertDialogBuilder initBefore =
new MaterialAlertDialogBuilder(this)
.setMessage(this.getResources().getString(R.string.creation_dialog_text))
.setPositiveButton(this.getResources().getString(R.string.dialog_ok), null);
switch (id) {
case R.id.user_pref:
@ -306,8 +320,7 @@ public class PasswordStore extends AppCompatActivity {
final File localDir = PasswordRepository.getRepositoryDirectory(getApplicationContext());
try {
if (!localDir.mkdir())
throw new IllegalStateException("Failed to create directory!");
if (!localDir.mkdir()) throw new IllegalStateException("Failed to create directory!");
PasswordRepository.createRepository(localDir);
if (new File(localDir.getAbsolutePath() + "/.gpg-id").createNewFile()) {
settings.edit().putBoolean("repository_initialized", true).apply();
@ -329,9 +342,13 @@ public class PasswordStore extends AppCompatActivity {
if (settings.getBoolean("git_external", false) && externalRepoPath != null) {
File dir = new File(externalRepoPath);
if (dir.exists() &&
dir.isDirectory() &&
!PasswordRepository.getPasswords(dir, PasswordRepository.getRepositoryDirectory(this), getSortOrder()).isEmpty()) {
if (dir.exists()
&& dir.isDirectory()
&& !PasswordRepository.getPasswords(
dir,
PasswordRepository.getRepositoryDirectory(this),
getSortOrder())
.isEmpty()) {
PasswordRepository.closeRepository();
checkLocalRepository();
@ -344,11 +361,14 @@ public class PasswordStore extends AppCompatActivity {
if (keyIds.isEmpty())
new MaterialAlertDialogBuilder(this)
.setMessage(this.getResources().getString(R.string.key_dialog_text))
.setPositiveButton(this.getResources().getString(R.string.dialog_positive), (dialogInterface, i) -> {
Intent intent = new Intent(activity, UserPreference.class);
startActivityForResult(intent, GitActivity.REQUEST_INIT);
})
.setNegativeButton(this.getResources().getString(R.string.dialog_negative), null)
.setPositiveButton(
this.getResources().getString(R.string.dialog_positive),
(dialogInterface, i) -> {
Intent intent = new Intent(activity, UserPreference.class);
startActivityForResult(intent, GitActivity.REQUEST_INIT);
})
.setNegativeButton(
this.getResources().getString(R.string.dialog_negative), null)
.show();
createRepository();
@ -361,7 +381,8 @@ public class PasswordStore extends AppCompatActivity {
intent.putExtra("operation", "git_external");
startActivityForResult(intent, HOME);
} else {
checkLocalRepository(PasswordRepository.getRepositoryDirectory(getApplicationContext()));
checkLocalRepository(
PasswordRepository.getRepositoryDirectory(getApplicationContext()));
}
}
@ -371,12 +392,16 @@ public class PasswordStore extends AppCompatActivity {
if (localDir != null && settings.getBoolean("repository_initialized", false)) {
Log.d(TAG, "Check, dir: " + localDir.getAbsolutePath());
// do not push the fragment if we already have it
if (fragmentManager.findFragmentByTag("PasswordsList") == null || settings.getBoolean("repo_changed", false)) {
if (fragmentManager.findFragmentByTag("PasswordsList") == null
|| settings.getBoolean("repo_changed", false)) {
settings.edit().putBoolean("repo_changed", false).apply();
plist = new PasswordFragment();
Bundle args = new Bundle();
args.putString("Path", PasswordRepository.getRepositoryDirectory(getApplicationContext()).getAbsolutePath());
args.putString(
"Path",
PasswordRepository.getRepositoryDirectory(getApplicationContext())
.getAbsolutePath());
// if the activity was started from the autofill settings, the
// intent is to match a clicked pwd with app. pass this to fragment
@ -405,7 +430,6 @@ public class PasswordStore extends AppCompatActivity {
}
}
@Override
public void onBackPressed() {
if ((null != plist) && plist.isNotEmpty()) {
@ -433,16 +457,13 @@ public class PasswordStore extends AppCompatActivity {
}
Git git = new Git(repository);
String relativePath = getRelativePath(fullPath, repoPath.getAbsolutePath())
.substring(1); // Removes leading '/'
String relativePath =
getRelativePath(fullPath, repoPath.getAbsolutePath())
.substring(1); // Removes leading '/'
Iterator<RevCommit> iterator;
try {
iterator = git
.log()
.addPath(relativePath)
.call()
.iterator();
iterator = git.log().addPath(relativePath).call().iterator();
} catch (GitAPIException e) {
Log.e(TAG, "getLastChangedTimestamp: GITAPIException", e);
return -1;
@ -462,23 +483,29 @@ public class PasswordStore extends AppCompatActivity {
for (Intent intent : new Intent[] {decryptIntent, authDecryptIntent}) {
intent.putExtra("NAME", item.toString());
intent.putExtra("FILE_PATH", item.getFile().getAbsolutePath());
intent.putExtra("REPO_PATH", PasswordRepository.getRepositoryDirectory(getApplicationContext()).getAbsolutePath());
intent.putExtra("LAST_CHANGED_TIMESTAMP", getLastChangedTimestamp(item.getFile().getAbsolutePath()));
intent.putExtra(
"REPO_PATH",
PasswordRepository.getRepositoryDirectory(getApplicationContext())
.getAbsolutePath());
intent.putExtra(
"LAST_CHANGED_TIMESTAMP",
getLastChangedTimestamp(item.getFile().getAbsolutePath()));
intent.putExtra("OPERATION", "DECRYPT");
}
// Adds shortcut
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
ShortcutInfo shortcut = new ShortcutInfo.Builder(this, item.getFullPathToParent())
.setShortLabel(item.toString())
.setLongLabel(item.getFullPathToParent() + item.toString())
.setIcon(Icon.createWithResource(this, R.mipmap.ic_launcher))
.setIntent(authDecryptIntent.setAction("DECRYPT_PASS")) // Needs action
.build();
ShortcutInfo shortcut =
new ShortcutInfo.Builder(this, item.getFullPathToParent())
.setShortLabel(item.toString())
.setLongLabel(item.getFullPathToParent() + item.toString())
.setIcon(Icon.createWithResource(this, R.mipmap.ic_launcher))
.setIntent(authDecryptIntent.setAction("DECRYPT_PASS")) // Needs action
.build();
List<ShortcutInfo> shortcuts = shortcutManager.getDynamicShortcuts();
if (shortcuts.size() >= shortcutManager.getMaxShortcutCountPerActivity() &&
shortcuts.size() > 0) {
if (shortcuts.size() >= shortcutManager.getMaxShortcutCountPerActivity()
&& shortcuts.size() > 0) {
shortcuts.remove(shortcuts.size() - 1);
shortcuts.add(0, shortcut);
shortcutManager.setDynamicShortcuts(shortcuts);
@ -494,7 +521,10 @@ public class PasswordStore extends AppCompatActivity {
intent.putExtra("NAME", item.toString());
intent.putExtra("FILE_PATH", item.getFile().getAbsolutePath());
intent.putExtra("PARENT_PATH", getCurrentDir().getAbsolutePath());
intent.putExtra("REPO_PATH", PasswordRepository.getRepositoryDirectory(getApplicationContext()).getAbsolutePath());
intent.putExtra(
"REPO_PATH",
PasswordRepository.getRepositoryDirectory(getApplicationContext())
.getAbsolutePath());
intent.putExtra("OPERATION", "EDIT");
startActivityForResult(intent, REQUEST_CODE_EDIT);
}
@ -503,8 +533,10 @@ public class PasswordStore extends AppCompatActivity {
if (!PasswordRepository.isInitialized()) {
new MaterialAlertDialogBuilder(this)
.setMessage(this.getResources().getString(R.string.creation_dialog_text))
.setPositiveButton(this.getResources().getString(R.string.dialog_ok), (dialogInterface, i) -> {
}).show();
.setPositiveButton(
this.getResources().getString(R.string.dialog_ok),
(dialogInterface, i) -> {})
.show();
return;
}
@ -512,10 +544,13 @@ public class PasswordStore extends AppCompatActivity {
new MaterialAlertDialogBuilder(this)
.setTitle(this.getResources().getString(R.string.no_key_selected_dialog_title))
.setMessage(this.getResources().getString(R.string.no_key_selected_dialog_text))
.setPositiveButton(this.getResources().getString(R.string.dialog_ok), (dialogInterface, i) -> {
Intent intent = new Intent(activity, UserPreference.class);
startActivity(intent);
}).show();
.setPositiveButton(
this.getResources().getString(R.string.dialog_ok),
(dialogInterface, i) -> {
Intent intent = new Intent(activity, UserPreference.class);
startActivity(intent);
})
.show();
return;
}
@ -524,13 +559,17 @@ public class PasswordStore extends AppCompatActivity {
Intent intent = new Intent(this, PgpActivity.class);
intent.putExtra("FILE_PATH", getCurrentDir().getAbsolutePath());
intent.putExtra("REPO_PATH", PasswordRepository.getRepositoryDirectory(getApplicationContext()).getAbsolutePath());
intent.putExtra(
"REPO_PATH",
PasswordRepository.getRepositoryDirectory(getApplicationContext())
.getAbsolutePath());
intent.putExtra("OPERATION", "ENCRYPT");
startActivityForResult(intent, REQUEST_CODE_ENCRYPT);
}
// deletes passwords in order from top to bottom
public void deletePasswords(final PasswordRecyclerAdapter adapter, final Set<Integer> selectedItems) {
public void deletePasswords(
final PasswordRecyclerAdapter adapter, final Set<Integer> selectedItems) {
final Iterator it = selectedItems.iterator();
if (!it.hasNext()) {
return;
@ -538,21 +577,29 @@ public class PasswordStore extends AppCompatActivity {
final int position = (int) it.next();
final PasswordItem item = adapter.getValues().get(position);
new MaterialAlertDialogBuilder(this)
.setMessage(getResources().getString(R.string.delete_dialog_text, item.getLongName()))
.setPositiveButton(getResources().getString(R.string.dialog_yes), (dialogInterface, i) -> {
item.getFile().delete();
adapter.remove(position);
it.remove();
adapter.updateSelectedItems(position, selectedItems);
.setMessage(
getResources().getString(R.string.delete_dialog_text, item.getLongName()))
.setPositiveButton(
getResources().getString(R.string.dialog_yes),
(dialogInterface, i) -> {
item.getFile().delete();
adapter.remove(position);
it.remove();
adapter.updateSelectedItems(position, selectedItems);
commitChange(getResources().getString(R.string.git_commit_remove_text,
item.getLongName()));
deletePasswords(adapter, selectedItems);
})
.setNegativeButton(this.getResources().getString(R.string.dialog_no), (dialogInterface, i) -> {
it.remove();
deletePasswords(adapter, selectedItems);
})
commitChange(
getResources()
.getString(
R.string.git_commit_remove_text,
item.getLongName()));
deletePasswords(adapter, selectedItems);
})
.setNegativeButton(
this.getResources().getString(R.string.dialog_no),
(dialogInterface, i) -> {
it.remove();
deletePasswords(adapter, selectedItems);
})
.show();
}
@ -567,18 +614,14 @@ public class PasswordStore extends AppCompatActivity {
startActivityForResult(intent, REQUEST_CODE_SELECT_FOLDER);
}
/**
* clears adapter's content and updates it with a fresh list of passwords from the root
*/
/** clears adapter's content and updates it with a fresh list of passwords from the root */
public void updateListAdapter() {
if ((null != plist)) {
plist.updateAdapter();
}
}
/**
* Updates the adapter with the current view of passwords
*/
/** Updates the adapter with the current view of passwords */
private void refreshListAdapter() {
if ((null != plist)) {
plist.refreshAdapter();
@ -607,14 +650,12 @@ public class PasswordStore extends AppCompatActivity {
GitAsyncTask tasks = new GitAsyncTask(activity, false, true, this);
tasks.execute(
git.add().addFilepattern("."),
git.commit().setAll(true).setMessage(message)
);
git.commit().setAll(true).setMessage(message));
}
}.execute();
}
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
switch (requestCode) {
case GitActivity.REQUEST_CLONE:
@ -622,26 +663,39 @@ public class PasswordStore extends AppCompatActivity {
settings.edit().putBoolean("repository_initialized", true).apply();
break;
case REQUEST_CODE_DECRYPT_AND_VERIFY:
// if went from decrypt->edit and user saved changes or HOTP counter was incremented, we need to commitChange
// if went from decrypt->edit and user saved changes or HOTP counter was
// incremented, we need to commitChange
if (data != null && data.getBooleanExtra("needCommit", false)) {
if (data.getStringExtra("OPERATION").equals("EDIT")) {
commitChange(this.getResources().getString(R.string.git_commit_edit_text,
data.getExtras().getString("LONG_NAME")));
commitChange(
this.getResources()
.getString(
R.string.git_commit_edit_text,
data.getExtras().getString("LONG_NAME")));
} else {
commitChange(this.getResources().getString(R.string.git_commit_increment_text,
data.getExtras().getString("LONG_NAME")));
commitChange(
this.getResources()
.getString(
R.string.git_commit_increment_text,
data.getExtras().getString("LONG_NAME")));
}
}
refreshListAdapter();
break;
case REQUEST_CODE_ENCRYPT:
commitChange(this.getResources().getString(R.string.git_commit_add_text,
data.getExtras().getString("LONG_NAME")));
commitChange(
this.getResources()
.getString(
R.string.git_commit_add_text,
data.getExtras().getString("LONG_NAME")));
refreshListAdapter();
break;
case REQUEST_CODE_EDIT:
commitChange(this.getResources().getString(R.string.git_commit_edit_text,
data.getExtras().getString("LONG_NAME")));
commitChange(
this.getResources()
.getString(
R.string.git_commit_edit_text,
data.getExtras().getString("LONG_NAME")));
refreshListAdapter();
break;
case GitActivity.REQUEST_INIT:
@ -657,15 +711,20 @@ public class PasswordStore extends AppCompatActivity {
break;
case CLONE_REPO_BUTTON:
// duplicate code
if (settings.getBoolean("git_external", false) && settings.getString("git_external_repo", null) != null) {
if (settings.getBoolean("git_external", false)
&& settings.getString("git_external_repo", null) != null) {
String externalRepoPath = settings.getString("git_external_repo", null);
File dir = externalRepoPath != null ? new File(externalRepoPath) : null;
if (dir != null &&
dir.exists() &&
dir.isDirectory() &&
!FileUtils.listFiles(dir, null, true).isEmpty() &&
!PasswordRepository.getPasswords(dir, PasswordRepository.getRepositoryDirectory(this), getSortOrder()).isEmpty()) {
if (dir != null
&& dir.exists()
&& dir.isDirectory()
&& !FileUtils.listFiles(dir, null, true).isEmpty()
&& !PasswordRepository.getPasswords(
dir,
PasswordRepository.getRepositoryDirectory(this),
getSortOrder())
.isEmpty()) {
PasswordRepository.closeRepository();
checkLocalRepository();
return; // if not empty, just show me the passwords!
@ -676,7 +735,9 @@ public class PasswordStore extends AppCompatActivity {
startActivityForResult(intent, GitActivity.REQUEST_CLONE);
break;
case REQUEST_CODE_SELECT_FOLDER:
Log.d(TAG, "Moving passwords to " + data.getStringExtra("SELECTED_FOLDER_PATH"));
Log.d(
TAG,
"Moving passwords to " + data.getStringExtra("SELECTED_FOLDER_PATH"));
Log.d(TAG, TextUtils.join(", ", data.getStringArrayListExtra("Files")));
File target = new File(data.getStringExtra("SELECTED_FOLDER_PATH"));
if (!target.isDirectory()) {
@ -684,9 +745,9 @@ public class PasswordStore extends AppCompatActivity {
break;
}
String repositoryPath = PasswordRepository
.getRepositoryDirectory(getApplicationContext())
.getAbsolutePath();
String repositoryPath =
PasswordRepository.getRepositoryDirectory(getApplicationContext())
.getAbsolutePath();
// TODO move this to an async task
for (String fileString : data.getStringArrayListExtra("Files")) {
@ -696,23 +757,33 @@ public class PasswordStore extends AppCompatActivity {
continue;
}
File destinationFile = new File(target.getAbsolutePath() + "/" + source.getName());
File destinationFile =
new File(target.getAbsolutePath() + "/" + source.getName());
String basename = FilenameUtils.getBaseName(source.getAbsolutePath());
String sourceLongName = PgpActivity.getLongName(source.getParent(),
repositoryPath, basename);
String sourceLongName =
PgpActivity.getLongName(
source.getParent(), repositoryPath, basename);
String destinationLongName = PgpActivity.getLongName(target.getAbsolutePath(),
repositoryPath, basename);
String destinationLongName =
PgpActivity.getLongName(
target.getAbsolutePath(), repositoryPath, basename);
if (destinationFile.exists()) {
Log.e(TAG, "Trying to move a file that already exists.");
// TODO: Add option to cancel overwrite. Will be easier once this is an async task.
// TODO: Add option to cancel overwrite. Will be easier once this is an
// async task.
new MaterialAlertDialogBuilder(this)
.setTitle(getResources().getString(R.string.password_exists_title))
.setMessage(getResources().getString(R.string.password_exists_message,
destinationLongName, sourceLongName))
.setTitle(
getResources()
.getString(R.string.password_exists_title))
.setMessage(
getResources()
.getString(
R.string.password_exists_message,
destinationLongName,
sourceLongName))
.setPositiveButton("Okay", null)
.show();
}
@ -721,10 +792,12 @@ public class PasswordStore extends AppCompatActivity {
// TODO this should show a warning to the user
Log.e(TAG, "Something went wrong while moving.");
} else {
commitChange(this.getResources()
.getString(R.string.git_commit_move_text,
sourceLongName,
destinationLongName));
commitChange(
this.getResources()
.getString(
R.string.git_commit_move_text,
sourceLongName,
destinationLongName));
}
}
updateListAdapter();
@ -743,63 +816,94 @@ public class PasswordStore extends AppCompatActivity {
new MaterialAlertDialogBuilder(this)
.setTitle(this.getResources().getString(R.string.location_dialog_title))
.setMessage(this.getResources().getString(R.string.location_dialog_text))
.setPositiveButton(this.getResources().getString(R.string.location_hidden), (dialog, whichButton) -> {
settings.edit().putBoolean("git_external", false).apply();
.setPositiveButton(
this.getResources().getString(R.string.location_hidden),
(dialog, whichButton) -> {
settings.edit().putBoolean("git_external", false).apply();
switch (operation) {
case NEW_REPO_BUTTON:
initializeRepositoryInfo();
break;
case CLONE_REPO_BUTTON:
PasswordRepository.initialize(PasswordStore.this);
switch (operation) {
case NEW_REPO_BUTTON:
initializeRepositoryInfo();
break;
case CLONE_REPO_BUTTON:
PasswordRepository.initialize(PasswordStore.this);
Intent intent = new Intent(activity, GitActivity.class);
intent.putExtra("Operation", GitActivity.REQUEST_CLONE);
startActivityForResult(intent, GitActivity.REQUEST_CLONE);
break;
}
})
.setNegativeButton(this.getResources().getString(R.string.location_sdcard), (dialog, whichButton) -> {
settings.edit().putBoolean("git_external", true).apply();
Intent intent = new Intent(activity, GitActivity.class);
intent.putExtra("Operation", GitActivity.REQUEST_CLONE);
startActivityForResult(intent, GitActivity.REQUEST_CLONE);
break;
}
})
.setNegativeButton(
this.getResources().getString(R.string.location_sdcard),
(dialog, whichButton) -> {
settings.edit().putBoolean("git_external", true).apply();
String externalRepo = settings.getString("git_external_repo", null);
String externalRepo = settings.getString("git_external_repo", null);
if (externalRepo == null) {
Intent intent = new Intent(activity, UserPreference.class);
intent.putExtra("operation", "git_external");
startActivityForResult(intent, operation);
} else {
new MaterialAlertDialogBuilder(activity)
.setTitle(getResources().getString(R.string.directory_selected_title))
.setMessage(getResources().getString(R.string.directory_selected_message, externalRepo))
.setPositiveButton(getResources().getString(R.string.use), (dialog1, which) -> {
switch (operation) {
case NEW_REPO_BUTTON:
initializeRepositoryInfo();
break;
case CLONE_REPO_BUTTON:
PasswordRepository.initialize(PasswordStore.this);
if (externalRepo == null) {
Intent intent = new Intent(activity, UserPreference.class);
intent.putExtra("operation", "git_external");
startActivityForResult(intent, operation);
} else {
new MaterialAlertDialogBuilder(activity)
.setTitle(
getResources()
.getString(
R.string.directory_selected_title))
.setMessage(
getResources()
.getString(
R.string.directory_selected_message,
externalRepo))
.setPositiveButton(
getResources().getString(R.string.use),
(dialog1, which) -> {
switch (operation) {
case NEW_REPO_BUTTON:
initializeRepositoryInfo();
break;
case CLONE_REPO_BUTTON:
PasswordRepository.initialize(
PasswordStore.this);
Intent intent = new Intent(activity, GitActivity.class);
intent.putExtra("Operation", GitActivity.REQUEST_CLONE);
startActivityForResult(intent, GitActivity.REQUEST_CLONE);
break;
}
})
.setNegativeButton(getResources().getString(R.string.change), (dialog12, which) -> {
Intent intent = new Intent(activity, UserPreference.class);
intent.putExtra("operation", "git_external");
startActivityForResult(intent, operation);
}).show();
}
})
Intent intent =
new Intent(
activity,
GitActivity.class);
intent.putExtra(
"Operation",
GitActivity.REQUEST_CLONE);
startActivityForResult(
intent,
GitActivity.REQUEST_CLONE);
break;
}
})
.setNegativeButton(
getResources().getString(R.string.change),
(dialog12, which) -> {
Intent intent =
new Intent(
activity, UserPreference.class);
intent.putExtra("operation", "git_external");
startActivityForResult(intent, operation);
})
.show();
}
})
.show();
}
public void matchPasswordWithApp(PasswordItem item) {
String path = item.getFile().getAbsolutePath()
.replace(PasswordRepository.getRepositoryDirectory(getApplicationContext()) + "/", "")
.replace(".gpg", "");
String path =
item.getFile()
.getAbsolutePath()
.replace(
PasswordRepository.getRepositoryDirectory(getApplicationContext())
+ "/",
"")
.replace(".gpg", "");
Intent data = new Intent();
data.putExtra("path", path);
setResult(RESULT_OK, data);

View file

@ -21,7 +21,6 @@ class SelectFolderActivity : AppCompatActivity() {
val fragmentManager = supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
passwordList = SelectFolderFragment()
val args = Bundle()
args.putString("Path", PasswordRepository.getRepositoryDirectory(applicationContext)?.absolutePath)
@ -59,4 +58,4 @@ class SelectFolderActivity : AppCompatActivity() {
setResult(Activity.RESULT_OK, intent)
finish()
}
}
}

View file

@ -5,7 +5,6 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
@ -13,21 +12,20 @@ import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.zeapo.pwdstore.utils.FolderRecyclerAdapter;
import com.zeapo.pwdstore.utils.PasswordItem;
import com.zeapo.pwdstore.utils.PasswordRepository;
import java.io.File;
import java.util.Stack;
/**
* A fragment representing a list of Items.
* <p/>
* Large screen devices (such as tablets) are supported by replacing the ListView
* with a GridView.
* <p/>
*
* <p>Large screen devices (such as tablets) are supported by replacing the ListView with a
* GridView.
*
* <p>
*/
public class SelectFolderFragment extends Fragment {
@ -38,11 +36,10 @@ public class SelectFolderFragment extends Fragment {
private OnFragmentInteractionListener mListener;
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
* Mandatory empty constructor for the fragment manager to instantiate the fragment (e.g. upon
* screen orientation changes).
*/
public SelectFolderFragment() {
}
public SelectFolderFragment() {}
@Override
public void onCreate(Bundle savedInstanceState) {
@ -50,17 +47,18 @@ public class SelectFolderFragment extends Fragment {
String path = getArguments().getString("Path");
pathStack = new Stack<>();
recyclerAdapter = new FolderRecyclerAdapter(mListener,
PasswordRepository.getPasswords(
new File(path),
PasswordRepository.getRepositoryDirectory(requireActivity()), getSortOrder()
)
);
recyclerAdapter =
new FolderRecyclerAdapter(
mListener,
PasswordRepository.getPasswords(
new File(path),
PasswordRepository.getRepositoryDirectory(requireActivity()),
getSortOrder()));
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
public View onCreateView(
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.password_recycler_view, container, false);
// use a linear layout manager
@ -68,7 +66,8 @@ public class SelectFolderFragment extends Fragment {
recyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
// use divider
recyclerView.addItemDecoration(new DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL));
recyclerView.addItemDecoration(
new DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL));
// Set the adapter
recyclerView.setAdapter(recyclerAdapter);
@ -83,21 +82,28 @@ public class SelectFolderFragment extends Fragment {
public void onAttach(final Context context) {
super.onAttach(context);
try {
mListener = item -> {
if (item.getType() == PasswordItem.TYPE_CATEGORY) {
//push the category were we're going
pathStack.push(item.getFile());
mListener =
item -> {
if (item.getType() == PasswordItem.TYPE_CATEGORY) {
// push the category were we're going
pathStack.push(item.getFile());
recyclerView.scrollToPosition(0);
recyclerAdapter.clear();
recyclerAdapter.addAll(PasswordRepository.getPasswords(item.getFile(), PasswordRepository.getRepositoryDirectory(context), getSortOrder()));
recyclerView.scrollToPosition(0);
recyclerAdapter.clear();
recyclerAdapter.addAll(
PasswordRepository.getPasswords(
item.getFile(),
PasswordRepository.getRepositoryDirectory(context),
getSortOrder()));
((AppCompatActivity) requireActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
};
((AppCompatActivity) requireActivity())
.getSupportActionBar()
.setDisplayHomeAsUpEnabled(true);
}
};
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement OnFragmentInteractionListener");
throw new ClassCastException(
context.toString() + " must implement OnFragmentInteractionListener");
}
}
@ -107,14 +113,13 @@ public class SelectFolderFragment extends Fragment {
* @return the current directory
*/
public File getCurrentDir() {
if (pathStack.isEmpty())
return PasswordRepository.getRepositoryDirectory(requireContext());
else
return pathStack.peek();
if (pathStack.isEmpty()) return PasswordRepository.getRepositoryDirectory(requireContext());
else return pathStack.peek();
}
private PasswordRepository.PasswordSortOrder getSortOrder() {
return PasswordRepository.PasswordSortOrder.getSortOrder(PreferenceManager.getDefaultSharedPreferences(requireContext()));
return PasswordRepository.PasswordSortOrder.getSortOrder(
PreferenceManager.getDefaultSharedPreferences(requireContext()));
}
public interface OnFragmentInteractionListener {

View file

@ -21,7 +21,6 @@ import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
@ -30,18 +29,15 @@ import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.PreferenceManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textfield.TextInputEditText;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.KeyPair;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.ref.WeakReference;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.FileUtils;
public class SshKeyGen extends AppCompatActivity {
@ -49,14 +45,15 @@ public class SshKeyGen extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getSupportActionBar() != null)
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
if (getSupportActionBar() != null) getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setTitle("Generate SSH Key");
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.replace(android.R.id.content, new SshKeyGenFragment()).commit();
getSupportFragmentManager()
.beginTransaction()
.replace(android.R.id.content, new SshKeyGenFragment())
.commit();
}
}
@ -64,45 +61,56 @@ public class SshKeyGen extends AppCompatActivity {
// private and public key, then replaces the SshKeyGenFragment with a
// ShowSshKeyFragment which displays the public key.
public void generate(View view) {
String length = Integer.toString((Integer) ((Spinner) findViewById(R.id.length)).getSelectedItem());
String length =
Integer.toString((Integer) ((Spinner) findViewById(R.id.length)).getSelectedItem());
String passphrase = ((EditText) findViewById(R.id.passphrase)).getText().toString();
String comment = ((EditText) findViewById(R.id.comment)).getText().toString();
new KeyGenerateTask(this).execute(length, passphrase, comment);
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
InputMethodManager imm =
(InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
// SSH key generation UI
public static class SshKeyGenFragment extends Fragment {
public SshKeyGenFragment() {
}
public SshKeyGenFragment() {}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View v = inflater.inflate(R.layout.fragment_ssh_keygen, container, false);
Typeface monoTypeface = Typeface.createFromAsset(requireContext().getAssets(), "fonts/sourcecodepro.ttf");
Typeface monoTypeface =
Typeface.createFromAsset(
requireContext().getAssets(), "fonts/sourcecodepro.ttf");
Spinner spinner = v.findViewById(R.id.length);
Integer[] lengths = new Integer[]{2048, 4096};
ArrayAdapter<Integer> adapter = new ArrayAdapter<>(requireContext(),
android.R.layout.simple_spinner_dropdown_item, lengths);
Integer[] lengths = new Integer[] {2048, 4096};
ArrayAdapter<Integer> adapter =
new ArrayAdapter<>(
requireContext(),
android.R.layout.simple_spinner_dropdown_item,
lengths);
spinner.setAdapter(adapter);
((TextInputEditText) v.findViewById(R.id.passphrase)).setTypeface(monoTypeface);
final CheckBox checkbox = v.findViewById(R.id.show_passphrase);
checkbox.setOnCheckedChangeListener((buttonView, isChecked) -> {
final TextInputEditText editText = v.findViewById(R.id.passphrase);
final int selection = editText.getSelectionEnd();
if (isChecked) {
editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
} else {
editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
}
editText.setSelection(selection);
});
checkbox.setOnCheckedChangeListener(
(buttonView, isChecked) -> {
final TextInputEditText editText = v.findViewById(R.id.passphrase);
final int selection = editText.getSelectionEnd();
if (isChecked) {
editText.setInputType(
InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
} else {
editText.setInputType(
InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_VARIATION_PASSWORD);
}
editText.setSelection(selection);
});
return v;
}
@ -110,16 +118,17 @@ public class SshKeyGen extends AppCompatActivity {
// Displays the generated public key .ssh_key.pub
public static class ShowSshKeyFragment extends DialogFragment {
public ShowSshKeyFragment() {
}
public ShowSshKeyFragment() {}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final FragmentActivity activity = requireActivity();
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireContext());
final MaterialAlertDialogBuilder builder =
new MaterialAlertDialogBuilder(requireContext());
LayoutInflater inflater = activity.getLayoutInflater();
@SuppressLint("InflateParams") final View v = inflater.inflate(R.layout.fragment_show_ssh_key, null);
@SuppressLint("InflateParams")
final View v = inflater.inflate(R.layout.fragment_show_ssh_key, null);
builder.setView(v);
AppCompatTextView textView = v.findViewById(R.id.public_key);
@ -131,27 +140,35 @@ public class SshKeyGen extends AppCompatActivity {
e.printStackTrace();
}
builder.setPositiveButton(getResources().getString(R.string.dialog_ok), (dialog, which) -> {
if (activity instanceof SshKeyGen)
activity.finish();
});
builder.setPositiveButton(
getResources().getString(R.string.dialog_ok),
(dialog, which) -> {
if (activity instanceof SshKeyGen) activity.finish();
});
builder.setNegativeButton(getResources().getString(R.string.dialog_cancel), (dialog, which) -> {
});
builder.setNegativeButton(
getResources().getString(R.string.dialog_cancel), (dialog, which) -> {});
builder.setNeutralButton(getResources().getString(R.string.ssh_keygen_copy), null);
final AlertDialog ad = builder.setTitle("Your public key").create();
ad.setOnShowListener(dialog -> {
Button b = ad.getButton(AlertDialog.BUTTON_NEUTRAL);
b.setOnClickListener(v1 -> {
AppCompatTextView textView1 = getDialog().findViewById(R.id.public_key);
ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("public key", textView1.getText().toString());
clipboard.setPrimaryClip(clip);
});
});
ad.setOnShowListener(
dialog -> {
Button b = ad.getButton(AlertDialog.BUTTON_NEUTRAL);
b.setOnClickListener(
v1 -> {
AppCompatTextView textView1 =
getDialog().findViewById(R.id.public_key);
ClipboardManager clipboard =
(ClipboardManager)
activity.getSystemService(
Context.CLIPBOARD_SERVICE);
ClipData clip =
ClipData.newPlainText(
"public key", textView1.getText().toString());
clipboard.setPrimaryClip(clip);
});
});
return ad;
}
}
@ -206,17 +223,26 @@ public class SshKeyGen extends AppCompatActivity {
Toast.makeText(weakReference.get(), "SSH-key generated", Toast.LENGTH_LONG).show();
DialogFragment df = new ShowSshKeyFragment();
df.show(weakReference.get().getSupportFragmentManager(), "public_key");
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(weakReference.get());
SharedPreferences prefs =
PreferenceManager.getDefaultSharedPreferences(weakReference.get());
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean("use_generated_key", true);
editor.apply();
} else {
new MaterialAlertDialogBuilder(weakReference.get())
.setTitle("Error while trying to generate the ssh-key")
.setMessage(weakReference.get().getResources().getString(R.string.ssh_key_error_dialog_text) + e.getMessage())
.setPositiveButton(weakReference.get().getResources().getString(R.string.dialog_ok), (dialogInterface, i) -> {
// pass
}).show();
.setMessage(
weakReference
.get()
.getResources()
.getString(R.string.ssh_key_error_dialog_text)
+ e.getMessage())
.setPositiveButton(
weakReference.get().getResources().getString(R.string.dialog_ok),
(dialogInterface, i) -> {
// pass
})
.show();
}
}
}

View file

@ -9,8 +9,9 @@ import androidx.fragment.app.Fragment
class ToCloneOrNot : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_to_clone_or_not, container, false)

View file

@ -72,7 +72,6 @@ class UserPreference : AppCompatActivity() {
val externalGitRepositoryPreference = findPreference<Preference>("git_external")
val selectExternalGitRepositoryPreference = findPreference<Preference>("pref_select_external")
// Crypto preferences
val keyPreference = findPreference<Preference>("openpgp_key_id_pref")
@ -393,8 +392,6 @@ class UserPreference : AppCompatActivity() {
sshKeyInputStream.close()
sshKeyOutputSteam.close()
} else {
Toast.makeText(this, getString(R.string.ssh_key_does_not_exist), Toast.LENGTH_LONG).show()
}
@ -413,8 +410,9 @@ class UserPreference : AppCompatActivity() {
}
override fun onActivityResult(
requestCode: Int, resultCode: Int,
data: Intent?
requestCode: Int,
resultCode: Int,
data: Intent?
) {
if (resultCode == Activity.RESULT_OK) {
if (data == null) {
@ -452,7 +450,6 @@ class UserPreference : AppCompatActivity() {
}
}
EDIT_GIT_INFO -> {
}
SELECT_GIT_DIRECTORY -> {
val uri = data.data
@ -527,7 +524,6 @@ class UserPreference : AppCompatActivity() {
if (passDir != null) {
copyDirToDir(sourcePassDir, passDir)
}
}
/**

View file

@ -28,7 +28,6 @@ class AutofillActivity : AppCompatActivity() {
} catch (e: IntentSender.SendIntentException) {
Log.e(AutofillService.Constants.TAG, "SendIntentException", e)
}
} else if (extras != null && extras.containsKey("pick")) {
val intent = Intent(applicationContext, PasswordStore::class.java)
intent.putExtra("matchWith", true)
@ -41,10 +40,10 @@ class AutofillActivity : AppCompatActivity() {
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
finish() // go back to the password field app
finish() // go back to the password field app
when (requestCode) {
REQUEST_CODE_DECRYPT_AND_VERIFY -> if (resultCode == RESULT_OK) {
AutofillService.instance?.setResultData(data!!) // report the result to service
AutofillService.instance?.setResultData(data!!) // report the result to service
}
REQUEST_CODE_PICK -> if (resultCode == RESULT_OK) {
AutofillService.instance?.setPickedPassword(data!!.getStringExtra("path"))

View file

@ -26,7 +26,6 @@ import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.utils.resolveAttribute
import com.zeapo.pwdstore.utils.splitLines
class AutofillFragment : DialogFragment() {
private var adapter: ArrayAdapter<String>? = null
private var isWeb: Boolean = false
@ -116,8 +115,8 @@ class AutofillFragment : DialogFragment() {
val editor = prefs.edit()
if (isWeb) {
builder.setNeutralButton(R.string.autofill_apps_delete) { _, _ ->
if (callingActivity.recyclerAdapter != null
&& packageName != null && packageName != "") {
if (callingActivity.recyclerAdapter != null &&
packageName != null && packageName != "") {
editor.remove(packageName)
callingActivity.recyclerAdapter?.removeWebsite(packageName)
editor.apply()

View file

@ -136,7 +136,6 @@ class AutofillPreferenceActivity : AppCompatActivity() {
} catch (e: PackageManager.NameNotFoundException) {
allApps.add(AutofillRecyclerAdapter.AppInfo(key, key, true, null))
}
}
weakReference.get()?.recyclerAdapter = AutofillRecyclerAdapter(allApps, weakReference.get()!!)
return null

View file

@ -18,8 +18,8 @@ import java.util.ArrayList
import java.util.Locale
internal class AutofillRecyclerAdapter(
allApps: List<AppInfo>,
private val activity: AutofillPreferenceActivity
allApps: List<AppInfo>,
private val activity: AutofillPreferenceActivity
) : RecyclerView.Adapter<AutofillRecyclerAdapter.ViewHolder>() {
private val apps: SortedList<AppInfo>
@ -86,8 +86,8 @@ internal class AutofillRecyclerAdapter(
holder.secondary.setText(R.string.autofill_apps_match)
holder.secondary.append(" " + preference!!.splitLines()[0])
if (preference.trim { it <= ' ' }.splitLines().size - 1 > 0) {
holder.secondary.append(" and "
+ (preference.trim { it <= ' ' }.splitLines().size - 1) + " more")
holder.secondary.append(" and " +
(preference.trim { it <= ' ' }.splitLines().size - 1) + " more")
}
}
}
@ -164,6 +164,5 @@ internal class AutofillRecyclerAdapter(
override fun onClick(v: View) {
activity.showDialog(packageName, appName, isWeb)
}
}
}

View file

@ -85,17 +85,17 @@ class AutofillService : AccessibilityService() {
}
// if returning to the source app from a successful AutofillActivity
if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
&& event.packageName != null && event.packageName == packageName
&& resultData != null) {
if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED &&
event.packageName != null && event.packageName == packageName &&
resultData != null) {
bindDecryptAndVerify()
}
// look for webView and trigger accessibility events if window changes
// or if page changes in chrome
if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED || (event.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
&& event.packageName != null
&& (event.packageName == "com.android.chrome" || event.packageName == "com.android.browser"))) {
if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED || (event.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED &&
event.packageName != null &&
(event.packageName == "com.android.chrome" || event.packageName == "com.android.browser"))) {
// there is a chance for getRootInActiveWindow() to return null at any time. save it.
try {
val root = rootInActiveWindow
@ -116,23 +116,20 @@ class AutofillService : AccessibilityService() {
webViewURL = URL("http://" + node.text.toString()).host
} catch (ignored: MalformedURLException) {
}
}
}
}
}
} catch (e: Exception) {
// sadly we were unable to access the data we wanted
return
}
}
// nothing to do if field is keychain app or system ui
if (event.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
|| event.packageName != null && event.packageName == "org.sufficientlysecure.keychain"
|| event.packageName != null && event.packageName == "com.android.systemui") {
if (event.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED ||
event.packageName != null && event.packageName == "org.sufficientlysecure.keychain" ||
event.packageName != null && event.packageName == "com.android.systemui") {
dismissDialog(event)
return
}
@ -281,8 +278,8 @@ class AutofillService : AccessibilityService() {
// means use default, so ignore it.
val value = prefs.getString(key, null)
val keyLowerCase = key.toLowerCase(Locale.ROOT)
if (value != null && value != ""
&& (webViewUrlLowerCase.contains(keyLowerCase) || keyLowerCase.contains(webViewUrlLowerCase))) {
if (value != null && value != "" &&
(webViewUrlLowerCase.contains(keyLowerCase) || keyLowerCase.contains(webViewUrlLowerCase))) {
preference = value
settingsURL = key
}
@ -397,7 +394,7 @@ class AutofillService : AccessibilityService() {
dialog = null
}
builder.setNeutralButton("Settings") { _, _ ->
//TODO make icon? gear?
// TODO make icon? gear?
// the user will have to return to the app themselves.
val intent = Intent(this@AutofillService, AutofillPreferenceActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
@ -426,7 +423,7 @@ class AutofillService : AccessibilityService() {
startActivity(intent)
}
else -> {
lastWhichItem-- // will add one element to items, so lastWhichItem=items.size()+1
lastWhichItem-- // will add one element to items, so lastWhichItem=items.size()+1
val intent = Intent(this@AutofillService, AutofillActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
intent.putExtra("pickMatchWith", true)
@ -512,7 +509,6 @@ class AutofillService : AccessibilityService() {
} catch (e: UnsupportedEncodingException) {
Log.e(Constants.TAG, "UnsupportedEncodingException", e)
}
}
OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED -> {
Log.i("PgpHandler", "RESULT_CODE_USER_INTERACTION_REQUIRED")

View file

@ -404,7 +404,6 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
RESULT_CODE_USER_INTERACTION_REQUIRED -> handleUserInteractionRequest(result, REQUEST_DECRYPT)
RESULT_CODE_ERROR -> handleError(result)
}
}
}
@ -474,7 +473,6 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
}
RESULT_CODE_ERROR -> handleError(result)
}
}
}
@ -669,7 +667,6 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
} else {
showToast(this.resources.getString(R.string.clipboard_password_no_clear_toast_text))
}
}
private fun copyUsernameToClipBoard(username: String) {
@ -697,7 +694,7 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
sendIntent,
resources.getText(R.string.send_plaintext_password_to)
)
)//Always show a picker to give the user a chance to cancel
) // Always show a picker to give the user a chance to cancel
}
private fun setTimer() {
@ -872,4 +869,3 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
}
}
}

View file

@ -10,7 +10,7 @@ import java.io.File
/**
* Creates a new clone operation
*
* @param fileDir the git working tree directory
* @param fileDir the git working tree directory
* @param callingActivity the calling activity
*/
class CloneOperation(fileDir: File, callingActivity: Activity) : GitOperation(fileDir, callingActivity) {
@ -44,8 +44,8 @@ class CloneOperation(fileDir: File, callingActivity: Activity) : GitOperation(fi
/**
* sets the authentication for the ssh-key scheme
*
* @param sshKey the ssh-key file
* @param username the username
* @param sshKey the ssh-key file
* @param username the username
* @param passphrase the passphrase
* @return the current object
*/
@ -62,10 +62,10 @@ class CloneOperation(fileDir: File, callingActivity: Activity) : GitOperation(fi
override fun onError(errorMessage: String) {
MaterialAlertDialogBuilder(callingActivity)
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
.setMessage("Error occured during the clone operation, "
+ callingActivity.resources.getString(R.string.jgit_error_dialog_text)
+ errorMessage
+ "\nPlease check the FAQ for possible reasons why this error might occur.")
.setMessage("Error occured during the clone operation, " +
callingActivity.resources.getString(R.string.jgit_error_dialog_text) +
errorMessage +
"\nPlease check the FAQ for possible reasons why this error might occur.")
.setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> }
.show()
}

View file

@ -73,7 +73,6 @@ open class GitActivity : AppCompatActivity() {
}
override fun onNothingSelected(adapterView: AdapterView<*>) {
}
}
@ -107,7 +106,6 @@ open class GitActivity : AppCompatActivity() {
}
override fun onNothingSelected(adapterView: AdapterView<*>) {
}
}
@ -218,10 +216,10 @@ open class GitActivity : AppCompatActivity() {
if (uri != null) {
when (protocol) {
"ssh://" -> {
var hostname = (serverUser.text.toString()
+ "@" +
serverUrl.text.toString().trim { it <= ' ' }
+ ":")
var hostname = (serverUser.text.toString() +
"@" +
serverUrl.text.toString().trim { it <= ' ' } +
":")
if (serverPort.text.toString() == "22") {
hostname += serverPath.text.toString()
@ -258,7 +256,6 @@ open class GitActivity : AppCompatActivity() {
else -> {
}
}
}
}
@ -420,7 +417,6 @@ open class GitActivity : AppCompatActivity() {
} catch (e: Exception) {
// ignore
}
}
}
@ -477,8 +473,8 @@ open class GitActivity : AppCompatActivity() {
return
// Warn if non-empty folder unless it's a just-initialized store that has just a .git folder
if (localDir.exists() && localDir.listFiles()!!.isNotEmpty()
&& !(localDir.listFiles()!!.size == 1 && localDir.listFiles()!![0].name == ".git")) {
if (localDir.exists() && localDir.listFiles()!!.isNotEmpty() &&
!(localDir.listFiles()!!.size == 1 && localDir.listFiles()!![0].name == ".git")) {
MaterialAlertDialogBuilder(this)
.setTitle(R.string.dialog_delete_title)
.setMessage(resources.getString(R.string.dialog_delete_msg) + " " + localDir.toString())
@ -489,7 +485,7 @@ open class GitActivity : AppCompatActivity() {
FileUtils.deleteDirectory(localDir)
launchGitOperation(REQUEST_CLONE)
} catch (e: IOException) {
//TODO Handle the exception correctly if we are unable to delete the directory...
// TODO Handle the exception correctly if we are unable to delete the directory...
e.printStackTrace()
MaterialAlertDialogBuilder(this).setMessage(e.message).show()
}
@ -509,11 +505,10 @@ open class GitActivity : AppCompatActivity() {
e.printStackTrace()
MaterialAlertDialogBuilder(this).setMessage(e.message).show()
}
}
} catch (e: Exception) {
//This is what happens when jgit fails :(
//TODO Handle the diffent cases of exceptions
// This is what happens when jgit fails :(
// TODO Handle the diffent cases of exceptions
e.printStackTrace()
MaterialAlertDialogBuilder(this).setMessage(e.message).show()
}
@ -612,11 +607,13 @@ open class GitActivity : AppCompatActivity() {
e.printStackTrace()
MaterialAlertDialogBuilder(this).setMessage(e.message).show()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int,
data: Intent?) {
override fun onActivityResult(
requestCode: Int,
resultCode: Int,
data: Intent?
) {
// In addition to the pre-operation-launch series of intents for OpenKeychain auth
// that will pass through here and back to launchGitOperation, there is one

View file

@ -3,10 +3,9 @@ package com.zeapo.pwdstore.git;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import com.zeapo.pwdstore.PasswordStore;
import com.zeapo.pwdstore.R;
import java.lang.ref.WeakReference;
import org.eclipse.jgit.api.CommitCommand;
import org.eclipse.jgit.api.GitCommand;
import org.eclipse.jgit.api.PullCommand;
@ -17,9 +16,6 @@ import org.eclipse.jgit.api.StatusCommand;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RemoteRefUpdate;
import java.lang.ref.WeakReference;
public class GitAsyncTask extends AsyncTask<GitCommand, Integer, String> {
private WeakReference<Activity> activityWeakReference;
private boolean finishOnEnd;
@ -27,7 +23,11 @@ public class GitAsyncTask extends AsyncTask<GitCommand, Integer, String> {
private ProgressDialog dialog;
private GitOperation operation;
public GitAsyncTask(Activity activity, boolean finishOnEnd, boolean refreshListOnEnd, GitOperation operation) {
public GitAsyncTask(
Activity activity,
boolean finishOnEnd,
boolean refreshListOnEnd,
GitOperation operation) {
this.activityWeakReference = new WeakReference<>(activity);
this.finishOnEnd = finishOnEnd;
this.refreshListOnEnd = refreshListOnEnd;
@ -41,7 +41,8 @@ public class GitAsyncTask extends AsyncTask<GitCommand, Integer, String> {
}
protected void onPreExecute() {
this.dialog.setMessage(getActivity().getResources().getString(R.string.running_dialog_text));
this.dialog.setMessage(
getActivity().getResources().getString(R.string.running_dialog_text));
this.dialog.setCancelable(false);
this.dialog.show();
}
@ -58,8 +59,7 @@ public class GitAsyncTask extends AsyncTask<GitCommand, Integer, String> {
nbChanges = status.getChanged().size() + status.getMissing().size();
} else if (command instanceof CommitCommand) {
// the previous status will eventually be used to avoid a commit
if (nbChanges == null || nbChanges > 0)
command.call();
if (nbChanges == null || nbChanges > 0) command.call();
} else if (command instanceof PullCommand) {
final PullResult result = ((PullCommand) command).call();
final RebaseResult rr = result.getRebaseResult();
@ -79,12 +79,14 @@ public class GitAsyncTask extends AsyncTask<GitCommand, Integer, String> {
case REJECTED_REMOTE_CHANGED:
case NON_EXISTING:
case NOT_ATTEMPTED:
return activity.getString(R.string.git_push_generic_error) + rru.getStatus().name();
return activity.getString(R.string.git_push_generic_error)
+ rru.getStatus().name();
case REJECTED_OTHER_REASON:
if ("non-fast-forward".equals(rru.getMessage())) {
return activity.getString(R.string.git_push_other_error);
} else {
return activity.getString(R.string.git_push_generic_error) + rru.getMessage();
return activity.getString(R.string.git_push_generic_error)
+ rru.getMessage();
}
default:
break;
@ -111,8 +113,7 @@ public class GitAsyncTask extends AsyncTask<GitCommand, Integer, String> {
// ignore
}
if (result == null)
result = "Unexpected error";
if (result == null) result = "Unexpected error";
if (!result.isEmpty()) {
this.operation.onError(result);

View file

@ -29,7 +29,7 @@ import java.io.File
/**
* Creates a new git operation
*
* @param fileDir the git working tree directory
* @param fileDir the git working tree directory
* @param callingActivity the calling activity
*/
abstract class GitOperation(fileDir: File, internal val callingActivity: Activity) {
@ -54,8 +54,8 @@ abstract class GitOperation(fileDir: File, internal val callingActivity: Activit
/**
* Sets the authentication using ssh-key scheme
*
* @param sshKey the ssh-key file
* @param username the username
* @param sshKey the ssh-key file
* @param username the username
* @param passphrase the passphrase
* @return the current object
*/
@ -87,14 +87,16 @@ abstract class GitOperation(fileDir: File, internal val callingActivity: Activit
* 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 to use in ssh-key connection mode
* @param identity the api identity to use for auth in OpenKeychain connection mode
* @param username the username
* @param sshKey the ssh-key file to use in ssh-key connection mode
* @param identity the api identity to use for auth in OpenKeychain connection mode
*/
fun executeAfterAuthentication(connectionMode: String,
username: String,
sshKey: File?,
identity: SshApiSessionFactory.ApiIdentity?) {
fun executeAfterAuthentication(
connectionMode: String,
username: String,
sshKey: File?,
identity: SshApiSessionFactory.ApiIdentity?
) {
executeAfterAuthentication(connectionMode, username, sshKey, identity, false)
}
@ -102,16 +104,18 @@ abstract class GitOperation(fileDir: File, internal val callingActivity: Activit
* 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 to use in ssh-key connection mode
* @param identity the api identity to use for auth in OpenKeychain connection mode
* @param showError show the passphrase edit text in red
* @param username the username
* @param sshKey the ssh-key file to use in ssh-key connection mode
* @param identity the api identity to use for auth in OpenKeychain connection mode
* @param showError show the passphrase edit text in red
*/
private fun executeAfterAuthentication(connectionMode: String,
username: String,
sshKey: File?,
identity: SshApiSessionFactory.ApiIdentity?,
showError: Boolean) {
private fun executeAfterAuthentication(
connectionMode: String,
username: String,
sshKey: File?,
identity: SshApiSessionFactory.ApiIdentity?,
showError: Boolean
) {
if (connectionMode.equals("ssh-key", ignoreCase = true)) {
if (sshKey == null || !sshKey.exists()) {
MaterialAlertDialogBuilder(callingActivity)
@ -199,7 +203,6 @@ abstract class GitOperation(fileDir: File, internal val callingActivity: Activit
.setPositiveButton("Ok") { _, _ -> callingActivity.finish() }
.show()
}
}
} else if (connectionMode.equals("OpenKeychain", ignoreCase = true)) {
setAuthentication(username, identity).execute()
@ -216,7 +219,6 @@ abstract class GitOperation(fileDir: File, internal val callingActivity: Activit
.setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ ->
// authenticate using the user/pwd and then execute the command
setAuthentication(username, password.text.toString()).execute()
}
.setNegativeButton(callingActivity.resources.getString(R.string.dialog_cancel)) { _, _ ->
callingActivity.finish()

View file

@ -10,7 +10,7 @@ import java.io.File
/**
* Creates a new git operation
*
* @param fileDir the git working tree directory
* @param fileDir the git working tree directory
* @param callingActivity the calling activity
*/
class PullOperation(fileDir: File, callingActivity: Activity) : GitOperation(fileDir, callingActivity) {
@ -36,10 +36,10 @@ class PullOperation(fileDir: File, callingActivity: Activity) : GitOperation(fil
override fun onError(errorMessage: String) {
MaterialAlertDialogBuilder(callingActivity)
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
.setMessage("Error occured during the pull operation, "
+ callingActivity.resources.getString(R.string.jgit_error_dialog_text)
+ errorMessage
+ "\nPlease check the FAQ for possible reasons why this error might occur.")
.setMessage("Error occured during the pull operation, " +
callingActivity.resources.getString(R.string.jgit_error_dialog_text) +
errorMessage +
"\nPlease check the FAQ for possible reasons why this error might occur.")
.setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> callingActivity.finish() }
.show()
}

View file

@ -10,7 +10,7 @@ import java.io.File
/**
* Creates a new git operation
*
* @param fileDir the git working tree directory
* @param fileDir the git working tree directory
* @param callingActivity the calling activity
*/
class PushOperation(fileDir: File, callingActivity: Activity) : GitOperation(fileDir, callingActivity) {

View file

@ -12,7 +12,7 @@ import java.io.File
/**
* Creates a new git operation
*
* @param fileDir the git working tree directory
* @param fileDir the git working tree directory
* @param callingActivity the calling activity
*/
class ResetToRemoteOperation(fileDir: File, callingActivity: Activity) : GitOperation(fileDir, callingActivity) {
@ -42,10 +42,10 @@ class ResetToRemoteOperation(fileDir: File, callingActivity: Activity) : GitOper
override fun onError(errorMessage: String) {
MaterialAlertDialogBuilder(callingActivity)
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
.setMessage("Error occured during the sync operation, "
+ "\nPlease check the FAQ for possible reasons why this error might occur."
+ callingActivity.resources.getString(R.string.jgit_error_dialog_text)
+ errorMessage)
.setMessage("Error occured during the sync operation, " +
"\nPlease check the FAQ for possible reasons why this error might occur." +
callingActivity.resources.getString(R.string.jgit_error_dialog_text) +
errorMessage)
.setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> }
.show()
}

View file

@ -14,7 +14,7 @@ import java.io.File
/**
* Creates a new git operation
*
* @param fileDir the git working tree directory
* @param fileDir the git working tree directory
* @param callingActivity the calling activity
*/
class SyncOperation(fileDir: File, callingActivity: Activity) : GitOperation(fileDir, callingActivity) {
@ -50,10 +50,10 @@ class SyncOperation(fileDir: File, callingActivity: Activity) : GitOperation(fil
override fun onError(errorMessage: String) {
MaterialAlertDialogBuilder(callingActivity)
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
.setMessage("Error occured during the sync operation, "
+ "\nPlease check the FAQ for possible reasons why this error might occur."
+ callingActivity.resources.getString(R.string.jgit_error_dialog_text)
+ errorMessage)
.setMessage("Error occured during the sync operation, " +
"\nPlease check the FAQ for possible reasons why this error might occur." +
callingActivity.resources.getString(R.string.jgit_error_dialog_text) +
errorMessage)
.setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> callingActivity.finish() }
.show()
}

View file

@ -19,4 +19,4 @@ open class GitConfigSessionFactory : JschConfigSessionFactory() {
jsch.removeAllIdentity()
return jsch
}
}
}

View file

@ -4,9 +4,7 @@ import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentSender;
import androidx.annotation.NonNull;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.jcraft.jsch.Identity;
import com.jcraft.jsch.JSch;
@ -14,7 +12,8 @@ import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo;
import com.zeapo.pwdstore.R;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
import org.eclipse.jgit.transport.CredentialItem;
import org.eclipse.jgit.transport.CredentialsProvider;
@ -33,15 +32,13 @@ import org.openintents.ssh.authentication.request.SigningRequest;
import org.openintents.ssh.authentication.request.SshPublicKeyRequest;
import org.openintents.ssh.authentication.util.SshAuthenticationApiUtils;
import java.util.List;
import java.util.concurrent.CountDownLatch;
public class SshApiSessionFactory extends GitConfigSessionFactory {
/**
* Intent request code indicating a completed signature that should be posted to an outstanding
* ApiIdentity
*/
public static final int POST_SIGNATURE = 301;
private String username;
private Identity identity;
@ -52,7 +49,8 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
@NonNull
@Override
protected JSch getJSch(@NonNull final OpenSshConfig.Host hc, @NonNull FS fs) throws JSchException {
protected JSch getJSch(@NonNull final OpenSshConfig.Host hc, @NonNull FS fs)
throws JSchException {
JSch jsch = super.getJSch(hc, fs);
jsch.removeAllIdentity();
jsch.addIdentity(identity, null);
@ -64,36 +62,38 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
session.setConfig("StrictHostKeyChecking", "no");
session.setConfig("PreferredAuthentications", "publickey");
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);
CredentialsProvider provider =
new CredentialsProvider() {
@Override
public boolean isInteractive() {
return false;
}
}
return true;
}
};
@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);
}
}
return true;
}
};
UserInfo userInfo = new CredentialsProviderUserInfo(session, provider);
session.setUserInfo(userInfo);
}
/**
* Helper to build up an ApiIdentity via the invocation of several pending intents that
* communicate with OpenKeychain. The user of this class must handle onActivityResult and
* keep feeding the resulting intents into the IdentityBuilder until it can successfully complete
* the build.
* communicate with OpenKeychain. The user of this class must handle onActivityResult and keep
* feeding the resulting intents into the IdentityBuilder until it can successfully complete the
* build.
*/
public static class IdentityBuilder {
private SshAuthenticationConnection connection;
@ -106,12 +106,14 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
* Construct a new IdentityBuilder
*
* @param callingActivity Activity that will be used to launch pending intents and that will
* receive and handle the results.
* receive and handle the results.
*/
public IdentityBuilder(Activity callingActivity) {
this.callingActivity = callingActivity;
List<String> providers = SshAuthenticationApiUtils.getAuthenticationProviderPackageNames(callingActivity);
List<String> providers =
SshAuthenticationApiUtils.getAuthenticationProviderPackageNames(
callingActivity);
if (providers.isEmpty())
throw new RuntimeException(callingActivity.getString(R.string.no_ssh_api_provider));
@ -120,42 +122,44 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
connection = new SshAuthenticationConnection(callingActivity, providers.get(0));
}
/**
* Free any resources associated with this IdentityBuilder
*/
/** Free any resources associated with this IdentityBuilder */
public void close() {
if (connection != null && connection.isConnected())
connection.disconnect();
if (connection != null && connection.isConnected()) connection.disconnect();
}
/**
* Helper to invoke an OpenKeyshain SSH API method and correctly interpret the result.
*
* @param request The request intent to launch
* @param request The request intent to launch
* @param requestCode The request code to use if a pending intent needs to be sent
* @return The resulting intent if the request completed immediately, or null if we had to
* launch a pending intent to interact with the user
* launch a pending intent to interact with the user
*/
private Intent executeApi(Request request, int requestCode) {
Intent result = api.executeApi(request.toIntent());
switch (result.getIntExtra(SshAuthenticationApi.EXTRA_RESULT_CODE, -1)) {
case SshAuthenticationApi.RESULT_CODE_ERROR:
SshAuthenticationApiError error = result.getParcelableExtra(SshAuthenticationApi.EXTRA_ERROR);
SshAuthenticationApiError error =
result.getParcelableExtra(SshAuthenticationApi.EXTRA_ERROR);
throw new RuntimeException(error.getMessage());
case SshAuthenticationApi.RESULT_CODE_SUCCESS:
break;
case SshAuthenticationApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
PendingIntent pendingIntent = result.getParcelableExtra(SshAuthenticationApi.EXTRA_PENDING_INTENT);
PendingIntent pendingIntent =
result.getParcelableExtra(SshAuthenticationApi.EXTRA_PENDING_INTENT);
try {
callingActivity.startIntentSenderForResult(pendingIntent.getIntentSender(), requestCode, null, 0, 0, 0);
callingActivity.startIntentSenderForResult(
pendingIntent.getIntentSender(), requestCode, null, 0, 0, 0);
return null;
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
throw new RuntimeException(callingActivity.getString(R.string.ssh_api_pending_intent_failed));
throw new RuntimeException(
callingActivity.getString(R.string.ssh_api_pending_intent_failed));
}
default:
throw new RuntimeException(callingActivity.getString(R.string.ssh_api_unknown_error));
throw new RuntimeException(
callingActivity.getString(R.string.ssh_api_unknown_error));
}
return result;
@ -168,8 +172,7 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
* @param intent The intent to inspect
*/
public void consume(Intent intent) {
if (intent == null)
return;
if (intent == null) return;
if (intent.hasExtra(SshAuthenticationApi.EXTRA_KEY_ID)) {
keyId = intent.getStringExtra(SshAuthenticationApi.EXTRA_KEY_ID);
@ -189,27 +192,31 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
*
* @param requestCode The request code to use if a pending intent needs to be sent
* @return The built identity, or null of user interaction is still required (in which case
* a pending intent will have already been launched)
* a pending intent will have already been launched)
*/
public ApiIdentity tryBuild(int requestCode) {
// First gate, need to initiate a connection to the service and wait for it to connect.
if (api == null) {
connection.connect(new SshAuthenticationConnection.OnBound() {
@Override
public void onBound(ISshAuthenticationService sshAgent) {
api = new SshAuthenticationApi(callingActivity, sshAgent);
// We can immediately try the next phase without needing to post back
// though onActivityResult
tryBuild(requestCode);
}
connection.connect(
new SshAuthenticationConnection.OnBound() {
@Override
public void onBound(ISshAuthenticationService sshAgent) {
api = new SshAuthenticationApi(callingActivity, sshAgent);
// We can immediately try the next phase without needing to post
// back
// though onActivityResult
tryBuild(requestCode);
}
@Override
public void onError() {
new MaterialAlertDialogBuilder(callingActivity)
.setMessage(callingActivity.getString(
R.string.openkeychain_ssh_api_connect_fail)).show();
}
});
@Override
public void onError() {
new MaterialAlertDialogBuilder(callingActivity)
.setMessage(
callingActivity.getString(
R.string.openkeychain_ssh_api_connect_fail))
.show();
}
});
return null;
}
@ -218,8 +225,7 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
if (keyId == null) {
consume(executeApi(new KeySelectionRequest(), requestCode));
// If we did not immediately get the result, bail for now and wait to be re-entered
if (keyId == null)
return null;
if (keyId == null) return null;
}
// Third gate, need to get the public key for the selected key. This one often does not
@ -227,8 +233,7 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
if (publicKey == null) {
consume(executeApi(new SshPublicKeyRequest(keyId), requestCode));
// If we did not immediately get the result, bail for now and wait to be re-entered
if (publicKey == null)
return null;
if (publicKey == null) return null;
}
// Have everything we need for now, build the identify
@ -236,9 +241,7 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
}
}
/**
* A Jsch identity that delegates key operations via the OpenKeychain SSH API
*/
/** A Jsch identity that delegates key operations via the OpenKeychain SSH API */
public static class ApiIdentity implements Identity {
private String keyId, description, alg;
private byte[] publicKey;
@ -247,7 +250,13 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
private CountDownLatch latch;
private byte[] signature;
ApiIdentity(String keyId, String description, byte[] publicKey, String alg, Activity callingActivity, SshAuthenticationApi api) {
ApiIdentity(
String keyId,
String description,
byte[] publicKey,
String alg,
Activity callingActivity,
SshAuthenticationApi api) {
this.keyId = keyId;
this.description = description;
this.publicKey = publicKey;
@ -271,33 +280,37 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
* Helper to handle the result of an OpenKeyshain SSH API signing request
*
* @param result The result intent to handle
* @return The signed challenge, or null if it was not immediately available, in which
* case the latch has been initialized and the pending intent started
* @return The signed challenge, or null if it was not immediately available, in which case
* the latch has been initialized and the pending intent started
*/
private byte[] handleSignResult(Intent result) {
switch (result.getIntExtra(SshAuthenticationApi.EXTRA_RESULT_CODE, -1)) {
case SshAuthenticationApi.RESULT_CODE_ERROR:
SshAuthenticationApiError error = result.getParcelableExtra(SshAuthenticationApi.EXTRA_ERROR);
SshAuthenticationApiError error =
result.getParcelableExtra(SshAuthenticationApi.EXTRA_ERROR);
throw new RuntimeException(error.getMessage());
case SshAuthenticationApi.RESULT_CODE_SUCCESS:
return result.getByteArrayExtra(SshAuthenticationApi.EXTRA_SIGNATURE);
case SshAuthenticationApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
PendingIntent pendingIntent = result.getParcelableExtra(SshAuthenticationApi.EXTRA_PENDING_INTENT);
PendingIntent pendingIntent =
result.getParcelableExtra(SshAuthenticationApi.EXTRA_PENDING_INTENT);
try {
latch = new CountDownLatch(1);
callingActivity.startIntentSenderForResult(pendingIntent.getIntentSender(), POST_SIGNATURE, null, 0, 0, 0);
callingActivity.startIntentSenderForResult(
pendingIntent.getIntentSender(), POST_SIGNATURE, null, 0, 0, 0);
return null;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(callingActivity.getString(R.string.ssh_api_pending_intent_failed));
throw new RuntimeException(
callingActivity.getString(R.string.ssh_api_pending_intent_failed));
}
default:
if (result.hasExtra(SshAuthenticationApi.EXTRA_CHALLENGE))
return handleSignResult(api.executeApi(result));
throw new RuntimeException(callingActivity.getString(R.string.ssh_api_unknown_error));
throw new RuntimeException(
callingActivity.getString(R.string.ssh_api_unknown_error));
}
}
@Override
@ -318,7 +331,6 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
return signature;
}
/**
* Post a signature response back to an in-progress operation using this ApiIdentity.
*
@ -328,8 +340,7 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
try {
signature = handleSignResult(data);
} finally {
if (latch != null)
latch.countDown();
if (latch != null) latch.countDown();
}
}
@ -354,8 +365,6 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
}
@Override
public void clear() {
}
public void clear() {}
}
}

View file

@ -5,7 +5,6 @@ import com.zeapo.pwdstore.R
import java.util.ArrayList
object PasswordGenerator {
internal const val DIGITS = 0x0001
internal const val UPPERS = 0x0002
@ -27,9 +26,9 @@ object PasswordGenerator {
/**
* Sets password generation preferences.
*
* @param ctx context from which to retrieve SharedPreferences from
* @param ctx context from which to retrieve SharedPreferences from
* preferences file 'PasswordGenerator'
* @param argv options for password generation
* @param argv options for password generation
* <table summary="options for password generation">
* <tr><td>Option</td><td>Description</td></tr>
* <tr><td>0</td><td>don't include numbers</td></tr>
@ -102,7 +101,7 @@ object PasswordGenerator {
phonemes = false
pwgenFlags = pwgenFlags or NO_VOWELS // | DIGITS | UPPERS;
}
}// pwgenFlags = DIGITS | UPPERS;
} // pwgenFlags = DIGITS | UPPERS;
}
}
@ -128,7 +127,6 @@ object PasswordGenerator {
phonemes = false
}
val passwords = ArrayList<String>()
val num = prefs.getInt("num", 1)
for (i in 0 until num) {
@ -143,4 +141,3 @@ object PasswordGenerator {
class PasswordGeneratorExeption(string: String) : Exception(string)
}

View file

@ -56,7 +56,7 @@ internal object Phonemes {
/**
* Generates a human-readable password.
*
* @param size length of password to generate
* @param size length of password to generate
* @param pwFlags flag field where set bits indicate conditions the
* generated password must meet
* <table summary="bits of flag field">
@ -105,8 +105,8 @@ internal object Phonemes {
continue
}
// Don't allow VOWEL followed a Vowel/Dipthong pair
if (prev and VOWEL > 0 && flags and VOWEL > 0
&& flags and DIPTHONG > 0
if (prev and VOWEL > 0 && flags and VOWEL > 0 &&
flags and DIPTHONG > 0
) {
continue
}
@ -200,8 +200,8 @@ internal object Phonemes {
shouldBe = if (shouldBe == CONSONANT) {
VOWEL
} else {
if (prev and VOWEL > 0 || flags and DIPTHONG > 0
|| RandomNumberGenerator.number(10) > 3
if (prev and VOWEL > 0 || flags and DIPTHONG > 0 ||
RandomNumberGenerator.number(10) > 3
) {
CONSONANT
} else {

View file

@ -5,7 +5,7 @@ internal object RandomPasswordGenerator {
/**
* Generates a completely random password.
*
* @param size length of password to generate
* @param size length of password to generate
* @param pwFlags flag field where set bits indicate conditions the
* generated password must meet
* <table summary ="bits of flag field">
@ -48,8 +48,8 @@ internal object RandomPasswordGenerator {
num = RandomNumberGenerator.number(bank.length)
cha = bank.toCharArray()[num]
character = cha.toString()
if (pwFlags and PasswordGenerator.AMBIGUOUS > 0
&& PasswordGenerator.AMBIGUOUS_STR.contains(character)) {
if (pwFlags and PasswordGenerator.AMBIGUOUS > 0 &&
PasswordGenerator.AMBIGUOUS_STR.contains(character)) {
continue
}
if (pwFlags and PasswordGenerator.NO_VOWELS > 0 && PasswordGenerator.VOWELS_STR.contains(character)) {

View file

@ -98,19 +98,23 @@ abstract class EntryRecyclerAdapter internal constructor(val values: ArrayList<P
protected abstract fun getOnClickListener(holder: ViewHolder, pass: PasswordItem): View.OnClickListener
// Create new views (invoked by the layout manager)
override fun onCreateViewHolder(parent: ViewGroup,
viewType: Int): ViewHolder {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ViewHolder {
// create a new view
val v = LayoutInflater.from(parent.context)
.inflate(R.layout.password_row_layout, parent, false)
return ViewHolder(v)
}
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
class ViewHolder(// each data item is just a string in this case
val view: View) : RecyclerView.ViewHolder(view) {
/*
Provide a reference to the views for each data item
Complex data items may need more than one view per item, and
you provide access to all the views for a data item in a view holder
each data item is just a string in this case
*/
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val name: AppCompatTextView = view.findViewById(R.id.label)
val type: AppCompatTextView = view.findViewById(R.id.type)
val typeImage: AppCompatImageView = view.findViewById(R.id.type_image)

View file

@ -6,8 +6,9 @@ import com.zeapo.pwdstore.SelectFolderFragment
import java.util.ArrayList
class FolderRecyclerAdapter(private val listener: SelectFolderFragment.OnFragmentInteractionListener,
values: ArrayList<PasswordItem>
class FolderRecyclerAdapter(
private val listener: SelectFolderFragment.OnFragmentInteractionListener,
values: ArrayList<PasswordItem>
) : EntryRecyclerAdapter(values) {
override fun getOnClickListener(holder: ViewHolder, pass: PasswordItem): View.OnClickListener {
@ -16,5 +17,4 @@ class FolderRecyclerAdapter(private val listener: SelectFolderFragment.OnFragmen
notifyItemChanged(holder.adapterPosition)
}
}
}

View file

@ -1,29 +1,27 @@
package com.zeapo.pwdstore.utils;
import android.util.Log;
import org.apache.commons.codec.binary.Base32;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base32;
public class Otp {
private static final Base32 BASE_32 = new Base32();
private Otp() {
}
private Otp() {}
public static String calculateCode(String secret, long counter, String algorithm, String digits) {
String[] steam = {"2", "3", "4", "5", "6", "7", "8", "9", "B", "C",
"D", "F", "G", "H", "J", "K", "M", "N", "P", "Q",
"R", "T", "V", "W", "X", "Y"};
public static String calculateCode(
String secret, long counter, String algorithm, String digits) {
String[] steam = {
"2", "3", "4", "5", "6", "7", "8", "9", "B", "C", "D", "F", "G", "H", "J", "K", "M",
"N", "P", "Q", "R", "T", "V", "W", "X", "Y"
};
String ALGORITHM = "Hmac" + algorithm.toUpperCase();
SecretKeySpec signingKey = new SecretKeySpec(BASE_32.decode(secret), ALGORITHM);

View file

@ -4,11 +4,11 @@ import com.zeapo.pwdstore.crypto.PgpActivity
import java.io.File
data class PasswordItem(
val name: String,
val parent: PasswordItem? = null,
val type: Char,
val file: File,
val rootDir: File
val name: String,
val parent: PasswordItem? = null,
val type: Char,
val file: File,
val rootDir: File
) : Comparable<PasswordItem> {
val fullPathToParent = file.absolutePath
.replace(rootDir.absolutePath, "")
@ -41,41 +41,40 @@ data class PasswordItem(
@JvmStatic
fun newCategory(
name: String,
file: File,
parent: PasswordItem,
rootDir: File
name: String,
file: File,
parent: PasswordItem,
rootDir: File
): PasswordItem {
return PasswordItem(name, parent, TYPE_CATEGORY, file, rootDir)
}
@JvmStatic
fun newCategory(
name: String,
file: File,
rootDir: File
name: String,
file: File,
rootDir: File
): PasswordItem {
return PasswordItem(name, null, TYPE_CATEGORY, file, rootDir)
}
@JvmStatic
fun newPassword(
name: String,
file: File,
parent: PasswordItem,
rootDir: File
name: String,
file: File,
parent: PasswordItem,
rootDir: File
): PasswordItem {
return PasswordItem(name, parent, TYPE_PASSWORD, file, rootDir)
}
@JvmStatic
fun newPassword(
name: String,
file: File,
rootDir: File
name: String,
file: File,
rootDir: File
): PasswordItem {
return PasswordItem(name, null, TYPE_PASSWORD, file, rootDir)
}
}
}
}

View file

@ -12,9 +12,10 @@ import com.zeapo.pwdstore.R
import java.util.ArrayList
import java.util.TreeSet
class PasswordRecyclerAdapter(private val activity: PasswordStore,
private val listener: PasswordFragment.OnFragmentInteractionListener,
values: ArrayList<PasswordItem>
class PasswordRecyclerAdapter(
private val activity: PasswordStore,
private val listener: PasswordFragment.OnFragmentInteractionListener,
values: ArrayList<PasswordItem>
) : EntryRecyclerAdapter(values) {
var actionMode: ActionMode? = null
private var canEdit: Boolean = false

View file

@ -32,7 +32,6 @@ open class PasswordRepository protected constructor() {
(p2.type + p1.name).compareTo(p1.type + p2.name, ignoreCase = true)
});
companion object {
@JvmStatic
fun getSortOrder(settings: SharedPreferences): PasswordSortOrder {
@ -63,7 +62,6 @@ open class PasswordRepository protected constructor() {
e.printStackTrace()
return null
}
}
return repository
}
@ -104,7 +102,6 @@ open class PasswordRepository protected constructor() {
} catch (e: Exception) {
e.printStackTrace()
}
} else if (replace!!) {
try {
val uri = URIish(url)
@ -127,7 +124,6 @@ open class PasswordRepository protected constructor() {
} catch (e: Exception) {
e.printStackTrace()
}
}
}
@ -214,7 +210,7 @@ open class PasswordRepository protected constructor() {
*/
@JvmStatic
fun getPasswords(path: File, rootDir: File, sortOrder: PasswordSortOrder): ArrayList<PasswordItem> {
//We need to recover the passwords then parse the files
// We need to recover the passwords then parse the files
val passList = getFilesList(path)
if (passList.size == 0) return ArrayList()
@ -259,10 +255,10 @@ open class PasswordRepository protected constructor() {
/**
* Sets a git config value
*
* @param section config section name
* @param section config section name
* @param subsection config subsection name
* @param name config name
* @param value the value to be set
* @param name config name
* @param value the value to be set
*/
@JvmStatic
@Suppress("SameParameterValue")
@ -275,7 +271,6 @@ open class PasswordRepository protected constructor() {
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}

View file

@ -8,8 +8,8 @@ import androidx.fragment.app.FragmentActivity
import com.zeapo.pwdstore.R
internal class Authenticator(
private val fragmentActivity: FragmentActivity,
private val callback: (AuthenticationResult) -> Unit
private val fragmentActivity: FragmentActivity,
private val callback: (AuthenticationResult) -> Unit
) {
private val handler = Handler()
private val biometricManager = BiometricManager.from(fragmentActivity)
@ -18,7 +18,7 @@ internal class Authenticator(
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
Log.d(TAG,"Error: $errorCode: $errString")
Log.d(TAG, "Error: $errorCode: $errString")
callback(AuthenticationResult.UnrecoverableError(errorCode, errString))
}

View file

@ -13,10 +13,10 @@ import android.widget.LinearLayout
import com.zeapo.pwdstore.R
class MultiselectableLinearLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
private var multiselected: Boolean = false
@ -48,4 +48,4 @@ class MultiselectableLinearLayout @JvmOverloads constructor(
companion object {
private val STATE_MULTISELECTED = intArrayOf(R.attr.state_multiselected)
}
}
}

View file

@ -45,3 +45,5 @@ tasks {
distributionType = Wrapper.DistributionType.ALL
}
}
configureSpotless()

16
buildSrc/build.gradle.kts Normal file
View file

@ -0,0 +1,16 @@
plugins {
`kotlin-dsl`
}
repositories {
maven("https://plugins.gradle.org/m2/")
jcenter()
}
kotlinDslPluginOptions {
experimentalWarning.set(false)
}
dependencies {
implementation("com.diffplug.spotless:spotless-plugin-gradle:3.24.3")
}

View file

@ -0,0 +1,47 @@
import com.diffplug.gradle.spotless.SpotlessExtension
import com.diffplug.gradle.spotless.SpotlessPlugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.configure
val kotlinLicenseHeader = """/*
* Copyright © 2014-2019 The Android Password Store Authors. All Rights Reserved.
* SPDX-License-Identifier: GPL-2.0
*/
""".trimIndent()
fun Project.configureSpotless() {
apply<SpotlessPlugin>()
configure<SpotlessExtension> {
java {
target("**/src/main/**/*.java")
trimTrailingWhitespace()
// @Suppress("INACCESSIBLE_TYPE")
// licenseHeader(kotlinLicenseHeader)
removeUnusedImports()
googleJavaFormat().aosp()
endWithNewline()
}
kotlinGradle {
target("*.gradle.kts", "gradle/*.gradle.kts", "buildSrc/*.gradle.kts")
ktlint("0.31.0").userData(mapOf("indent_size" to "4", "continuation_indent_size" to "4"))
// @Suppress("INACCESSIBLE_TYPE")
// licenseHeader(kotlinLicenseHeader, "import|tasks|apply|plugins|include")
trimTrailingWhitespace()
indentWithSpaces()
endWithNewline()
}
kotlin {
target("**/src/main/**/*.kt", "buildSrc/**/*.kt")
ktlint("0.31.0").userData(mapOf("indent_size" to "4", "continuation_indent_size" to "4"))
// @Suppress("INACCESSIBLE_TYPE")
// licenseHeader(kotlinLicenseHeader)
trimTrailingWhitespace()
indentWithSpaces()
endWithNewline()
}
}
}