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:
parent
9a1a54a6fc
commit
f1f59dc1ed
40 changed files with 907 additions and 682 deletions
2
.github/workflows/push.yml
vendored
2
.github/workflows/push.yml
vendored
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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 {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -45,3 +45,5 @@ tasks {
|
|||
distributionType = Wrapper.DistributionType.ALL
|
||||
}
|
||||
}
|
||||
|
||||
configureSpotless()
|
||||
|
|
16
buildSrc/build.gradle.kts
Normal file
16
buildSrc/build.gradle.kts
Normal 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")
|
||||
}
|
47
buildSrc/src/main/kotlin/SpotlessConfiguration.kt
Normal file
47
buildSrc/src/main/kotlin/SpotlessConfiguration.kt
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue