diff options
34 files changed, 925 insertions, 1428 deletions
diff --git a/app/build.gradle b/app/build.gradle index 030b1113..d138cc5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,7 +7,7 @@ android { compileSdkVersion 28 defaultConfig { applicationId "com.zeapo.pwdstore" - minSdkVersion 16 + minSdkVersion 21 targetSdkVersion 28 versionCode 10302 versionName "1.3.2" @@ -15,8 +15,8 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } lintOptions { abortOnError true // make sure build fails with lint errors! @@ -79,8 +79,6 @@ dependencies { androidTestImplementation 'androidx.test:rules:1.1.0-alpha4' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha4' androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0-alpha4' - - } repositories { mavenCentral() diff --git a/app/src/main/java/com/zeapo/pwdstore/DividerItemDecoration.java b/app/src/main/java/com/zeapo/pwdstore/DividerItemDecoration.java index 21510112..93673902 100644 --- a/app/src/main/java/com/zeapo/pwdstore/DividerItemDecoration.java +++ b/app/src/main/java/com/zeapo/pwdstore/DividerItemDecoration.java @@ -4,9 +4,10 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.drawable.Drawable; +import android.view.View; +import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.RecyclerView; -import android.view.View; class DividerItemDecoration extends RecyclerView.ItemDecoration { @@ -31,7 +32,7 @@ class DividerItemDecoration extends RecyclerView.ItemDecoration { } @Override - public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { + public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { int left = parent.getPaddingLeft(); int right = parent.getWidth() - parent.getPaddingRight(); diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.java b/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.java index 590a779e..d876aefd 100644 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.java +++ b/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.java @@ -11,14 +11,13 @@ import java.io.UnsupportedEncodingException; public class PasswordEntry { private static final String[] USERNAME_FIELDS = new String[]{"login", "username"}; - - private String extraContent; private final String password; private final String username; private final String totpSecret; private final String hotpSecret; private final Long hotpCounter; private final String content; + private String extraContent; private boolean isIncremented = false; public PasswordEntry(final ByteArrayOutputStream os) throws UnsupportedEncodingException { @@ -34,7 +33,7 @@ public class PasswordEntry { hotpCounter = findHotpCounter(content); extraContent = findExtraContent(passContent); username = findUsername(); - } + } public String getPassword() { return password; @@ -76,7 +75,9 @@ public class PasswordEntry { return hotpSecret != null && hotpCounter != null; } - public boolean hotpIsIncremented() { return isIncremented; } + public boolean hotpIsIncremented() { + return isIncremented; + } public void incrementHotp() { for (String line : content.split("\n")) { @@ -126,7 +127,7 @@ public class PasswordEntry { return null; } - private String findExtraContent(String [] passContent) { + private String findExtraContent(String[] passContent) { String extraContent = passContent.length > 1 ? passContent[1] : ""; // if there is a HOTP URI, we must return the extra content with the counter incremented if (hasHotp()) { diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.java b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.java index f6179703..63211af0 100644 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.java +++ b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.java @@ -4,16 +4,16 @@ import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; -import com.google.android.material.floatingactionbutton.FloatingActionButton; -import androidx.fragment.app.Fragment; -import androidx.appcompat.app.AppCompatActivity; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; import android.util.Log; 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; +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; @@ -24,16 +24,12 @@ import java.util.Stack; /** * A fragment representing a list of Items. - * <p /> + * <p/> * Large screen devices (such as tablets) are supported by replacing the ListView * with a GridView. - * <p /> + * <p/> */ -public class PasswordFragment extends Fragment{ - - public interface OnFragmentInteractionListener { - void onFragmentInteraction(PasswordItem item); - } +public class PasswordFragment extends Fragment { // store the pass files list in a stack private Stack<ArrayList<PasswordItem>> passListStack; @@ -43,12 +39,12 @@ public class PasswordFragment extends Fragment{ private RecyclerView recyclerView; private OnFragmentInteractionListener mListener; private SharedPreferences settings; - /** * 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) { @@ -60,18 +56,18 @@ public class PasswordFragment extends Fragment{ scrollPosition = new Stack<>(); pathStack = new Stack<>(); recyclerAdapter = new PasswordRecyclerAdapter((PasswordStore) getActivity(), mListener, - PasswordRepository.getPasswords(new File(path), PasswordRepository.getRepositoryDirectory(getActivity()), getSortOrder())); + PasswordRepository.getPasswords(new File(path), PasswordRepository.getRepositoryDirectory(getActivity()), getSortOrder())); } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, + 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 RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity()); - recyclerView = (RecyclerView) view.findViewById(R.id.pass_recycler); + recyclerView = view.findViewById(R.id.pass_recycler); recyclerView.setLayoutManager(mLayoutManager); // use divider @@ -80,13 +76,8 @@ public class PasswordFragment extends Fragment{ // Set the adapter recyclerView.setAdapter(recyclerAdapter); - final FloatingActionButton fab = (FloatingActionButton) view.findViewById(R.id.fab); - fab.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - ((PasswordStore) getActivity()).createPassword(); - } - }); + final FloatingActionButton fab = view.findViewById(R.id.fab); + fab.setOnClickListener(v -> ((PasswordStore) getActivity()).createPassword()); registerForContextMenu(recyclerView); return view; @@ -96,34 +87,32 @@ public class PasswordFragment extends Fragment{ public void onAttach(final Context context) { super.onAttach(context); try { - mListener = new OnFragmentInteractionListener() { - public void onFragmentInteraction(PasswordItem 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())); - - ((AppCompatActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true); + 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())); + + ((AppCompatActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } else { + if (getArguments().getBoolean("matchWith", false)) { + ((PasswordStore) getActivity()).matchPasswordWithApp(item); } else { - if (getArguments().getBoolean("matchWith", false)) { - ((PasswordStore) getActivity()).matchPasswordWithApp(item); - } else { - ((PasswordStore) getActivity()).decryptPassword(item); - } + ((PasswordStore) getActivity()).decryptPassword(item); } } }; } catch (ClassCastException e) { throw new ClassCastException(context.toString() - + " must implement OnFragmentInteractionListener"); + + " must implement OnFragmentInteractionListener"); } } @@ -146,12 +135,13 @@ public class PasswordFragment extends Fragment{ public void refreshAdapter() { recyclerAdapter.clear(); recyclerAdapter.addAll(pathStack.isEmpty() ? - PasswordRepository.getPasswords(PasswordRepository.getRepositoryDirectory(getActivity()), getSortOrder()) : - PasswordRepository.getPasswords(pathStack.peek(), PasswordRepository.getRepositoryDirectory(getActivity()), getSortOrder())); + PasswordRepository.getPasswords(PasswordRepository.getRepositoryDirectory(getActivity()), getSortOrder()) : + PasswordRepository.getPasswords(pathStack.peek(), PasswordRepository.getRepositoryDirectory(getActivity()), getSortOrder())); } /** * filters the list adapter + * * @param filter the filter to apply */ public void filterAdapter(String filter) { @@ -166,8 +156,9 @@ 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 @@ -205,6 +196,7 @@ public class PasswordFragment extends Fragment{ /** * gets the current directory + * * @return the current directory */ public File getCurrentDir() { @@ -227,4 +219,8 @@ public class PasswordFragment extends Fragment{ private PasswordRepository.PasswordSortOrder getSortOrder() { return PasswordRepository.PasswordSortOrder.getSortOrder(settings); } + + public interface OnFragmentInteractionListener { + void onFragmentInteraction(PasswordItem item); + } } diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordGeneratorDialogFragment.java b/app/src/main/java/com/zeapo/pwdstore/PasswordGeneratorDialogFragment.java index 1a51f6f2..f161b927 100644 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordGeneratorDialogFragment.java +++ b/app/src/main/java/com/zeapo/pwdstore/PasswordGeneratorDialogFragment.java @@ -4,7 +4,6 @@ import android.annotation.SuppressLint; import android.app.Activity; import android.app.Dialog; import android.content.Context; -import android.content.DialogInterface; import android.content.SharedPreferences; import android.graphics.Typeface; import android.os.Bundle; @@ -30,7 +29,6 @@ public class PasswordGeneratorDialogFragment extends DialogFragment { public PasswordGeneratorDialogFragment() { } - @NotNull @SuppressLint("SetTextI18n") @Override @@ -64,49 +62,35 @@ public class PasswordGeneratorDialogFragment extends DialogFragment { TextView textView = view.findViewById(R.id.lengthNumber); textView.setText(Integer.toString(prefs.getInt("length", 20))); - ((TextView) view.findViewById(R.id.passwordText)).setTypeface(monoTypeface); + TextView passwordText = view.findViewById(R.id.passwordText); + passwordText.setTypeface(monoTypeface); - builder.setPositiveButton(getResources().getString(R.string.dialog_ok), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - EditText edit = callingActivity.findViewById(R.id.crypto_password_edit); - TextView generate = view.findViewById(R.id.passwordText); - edit.setText(generate.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), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int 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(new DialogInterface.OnShowListener() { - @Override - public void onShow(DialogInterface dialog) { + ad.setOnShowListener(dialog -> { + setPreferences(); + passwordText.setText(PasswordGenerator.INSTANCE.generate(getActivity().getApplicationContext()).get(0)); + + Button b = ad.getButton(AlertDialog.BUTTON_NEUTRAL); + b.setOnClickListener(v -> { setPreferences(); - TextView textView = view.findViewById(R.id.passwordText); - textView.setText(PasswordGenerator.INSTANCE.generate(getActivity().getApplicationContext()).get(0)); - - Button b = ad.getButton(AlertDialog.BUTTON_NEUTRAL); - b.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - setPreferences(); - TextView textView = view.findViewById(R.id.passwordText); - textView.setText(PasswordGenerator.INSTANCE.generate(callingActivity.getApplicationContext()).get(0)); - } - }); - } + passwordText.setText(PasswordGenerator.INSTANCE.generate(callingActivity.getApplicationContext()).get(0)); + }); }); return ad; } - private void setPreferences () { + private void setPreferences() { ArrayList<String> preferences = new ArrayList<>(); if (!((CheckBox) getDialog().findViewById(R.id.numerals)).isChecked()) { preferences.add("0"); @@ -127,7 +111,7 @@ public class PasswordGeneratorDialogFragment extends DialogFragment { try { int length = Integer.valueOf(editText.getText().toString()); PasswordGenerator.INSTANCE.setPrefs(getActivity().getApplicationContext(), preferences, length); - } catch(NumberFormatException e) { + } catch (NumberFormatException e) { PasswordGenerator.INSTANCE.setPrefs(getActivity().getApplicationContext(), preferences); } } diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordStore.java b/app/src/main/java/com/zeapo/pwdstore/PasswordStore.java index a6e399ef..123acd2d 100644 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordStore.java +++ b/app/src/main/java/com/zeapo/pwdstore/PasswordStore.java @@ -3,7 +3,6 @@ package com.zeapo.pwdstore; import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; @@ -14,20 +13,6 @@ import android.graphics.drawable.Icon; import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; - -import androidx.annotation.NonNull; - -import com.google.android.material.snackbar.Snackbar; - -import androidx.core.app.ActivityCompat; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentTransaction; -import androidx.core.content.ContextCompat; -import androidx.core.view.MenuItemCompat; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.SearchView; - import android.text.TextUtils; import android.util.Log; import android.view.KeyEvent; @@ -35,16 +20,23 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.TextView; - +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.SearchView; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; +import androidx.core.view.MenuItemCompat; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; +import com.google.android.material.snackbar.Snackbar; import com.zeapo.pwdstore.crypto.PgpActivity; import com.zeapo.pwdstore.git.GitActivity; import com.zeapo.pwdstore.git.GitAsyncTask; import com.zeapo.pwdstore.git.GitOperation; -import com.zeapo.pwdstore.pwgen.PRNGFixes; 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; @@ -53,21 +45,15 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import java.io.File; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; public class PasswordStore extends AppCompatActivity { - private static final String TAG = PasswordStore.class.getName(); - private SharedPreferences settings; - private Activity activity; - private PasswordFragment plist; - private ShortcutManager shortcutManager; - private MenuItem searchItem = null; - private SearchView searchView; - - private final static int CLONE_REPO_BUTTON = 401; - private final static int NEW_REPO_BUTTON = 402; - private final static int HOME = 403; public static final int REQUEST_CODE_SIGN = 9910; public static final int REQUEST_CODE_ENCRYPT = 9911; public static final int REQUEST_CODE_SIGN_AND_ENCRYPT = 9912; @@ -76,6 +62,17 @@ public class PasswordStore extends AppCompatActivity { public static final int REQUEST_CODE_GET_KEY_IDS = 9915; 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 SharedPreferences settings; + private Activity activity; + private PasswordFragment plist; + private ShortcutManager shortcutManager; + private MenuItem searchItem = null; + private SearchView searchView; private static boolean isPrintable(char c) { Character.UnicodeBlock block = Character.UnicodeBlock.of(c); @@ -106,8 +103,6 @@ public class PasswordStore extends AppCompatActivity { return super.onKeyDown(keyCode, event); } - private final static int REQUEST_EXTERNAL_STORAGE = 50; - @Override @SuppressLint("NewApi") protected void onCreate(Bundle savedInstanceState) { @@ -116,7 +111,6 @@ public class PasswordStore extends AppCompatActivity { shortcutManager = getSystemService(ShortcutManager.class); } activity = this; - PRNGFixes.INSTANCE.apply(); // If user opens app with permission granted then revokes and returns, // prevent attempt to create password list fragment @@ -142,17 +136,12 @@ public class PasswordStore extends AppCompatActivity { // 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, new View.OnClickListener() { - @Override - public void onClick(View view) { - ActivityCompat.requestPermissions(activity, - new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, - REQUEST_EXTERNAL_STORAGE); - } - }); + .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(); - TextView tv = (TextView) view.findViewById(com.google.android.material.R.id.snackbar_text); + TextView tv = view.findViewById(com.google.android.material.R.id.snackbar_text); tv.setTextColor(Color.WHITE); tv.setMaxLines(10); } else { @@ -357,12 +346,9 @@ public class PasswordStore extends AppCompatActivity { if (keyIds.isEmpty()) new AlertDialog.Builder(this) .setMessage(this.getResources().getString(R.string.key_dialog_text)) - .setPositiveButton(this.getResources().getString(R.string.dialog_positive), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - Intent intent = new Intent(activity, UserPreference.class); - startActivityForResult(intent, GitActivity.REQUEST_INIT); - } + .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(); @@ -516,24 +502,18 @@ public class PasswordStore extends AppCompatActivity { if (!PasswordRepository.isInitialized()) { new AlertDialog.Builder(this) .setMessage(this.getResources().getString(R.string.creation_dialog_text)) - .setPositiveButton(this.getResources().getString(R.string.dialog_ok), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - } + .setPositiveButton(this.getResources().getString(R.string.dialog_ok), (dialogInterface, i) -> { }).show(); return; } - if (settings.getStringSet("openpgp_key_ids_set", new HashSet<String>()).isEmpty()) { + if (settings.getStringSet("openpgp_key_ids_set", new HashSet<>()).isEmpty()) { new AlertDialog.Builder(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), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - Intent intent = new Intent(activity, UserPreference.class); - startActivity(intent); - } + .setPositiveButton(this.getResources().getString(R.string.dialog_ok), (dialogInterface, i) -> { + Intent intent = new Intent(activity, UserPreference.class); + startActivity(intent); }).show(); return; } @@ -559,25 +539,19 @@ public class PasswordStore extends AppCompatActivity { new AlertDialog.Builder(this). setMessage(this.getResources().getString(R.string.delete_dialog_text) + item + "\"") - .setPositiveButton(this.getResources().getString(R.string.dialog_yes), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int 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); - } + .setPositiveButton(this.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), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - it.remove(); - deletePasswords(adapter, selectedItems); - } + .setNegativeButton(this.getResources().getString(R.string.dialog_no), (dialogInterface, i) -> { + it.remove(); + deletePasswords(adapter, selectedItems); }) .show(); } @@ -771,64 +745,54 @@ public class PasswordStore extends AppCompatActivity { new AlertDialog.Builder(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), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - settings.edit().putBoolean("git_external", false).apply(); - - 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; - } + .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); + + 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), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - settings.edit().putBoolean("git_external", true).apply(); - - 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 AlertDialog.Builder(activity) - .setTitle(getResources().getString(R.string.directory_selected_title)) - .setMessage(getResources().getString(R.string.directory_selected_message, externalRepo)) - .setPositiveButton(getResources().getString(R.string.use), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int 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), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - Intent intent = new Intent(activity, UserPreference.class); - intent.putExtra("operation", "git_external"); - startActivityForResult(intent, operation); - } - }).show(); - } + .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); + + if (externalRepo == null) { + Intent intent = new Intent(activity, UserPreference.class); + intent.putExtra("operation", "git_external"); + startActivityForResult(intent, operation); + } else { + new AlertDialog.Builder(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(); } }) .show(); diff --git a/app/src/main/java/com/zeapo/pwdstore/SelectFolderActivity.kt b/app/src/main/java/com/zeapo/pwdstore/SelectFolderActivity.kt index 30923ee1..2e894987 100644 --- a/app/src/main/java/com/zeapo/pwdstore/SelectFolderActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/SelectFolderActivity.kt @@ -2,9 +2,9 @@ package com.zeapo.pwdstore import android.app.Activity import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity import android.view.Menu import android.view.MenuItem +import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.FragmentManager import com.zeapo.pwdstore.utils.PasswordRepository diff --git a/app/src/main/java/com/zeapo/pwdstore/SelectFolderFragment.java b/app/src/main/java/com/zeapo/pwdstore/SelectFolderFragment.java index f4ec98b2..1fe79a2a 100644 --- a/app/src/main/java/com/zeapo/pwdstore/SelectFolderFragment.java +++ b/app/src/main/java/com/zeapo/pwdstore/SelectFolderFragment.java @@ -1,50 +1,44 @@ package com.zeapo.pwdstore; import android.content.Context; -import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; -import com.google.android.material.floatingactionbutton.FloatingActionButton; -import androidx.fragment.app.Fragment; -import androidx.appcompat.app.AppCompatActivity; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; 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; +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.ArrayList; import java.util.Stack; /** * A fragment representing a list of Items. - * <p /> + * <p/> * Large screen devices (such as tablets) are supported by replacing the ListView * with a GridView. - * <p /> + * <p/> */ -public class SelectFolderFragment extends Fragment{ - - public interface OnFragmentInteractionListener { - void onFragmentInteraction(PasswordItem item); - } +public class SelectFolderFragment extends Fragment { // store the pass files list in a stack private Stack<File> pathStack; private FolderRecyclerAdapter recyclerAdapter; private RecyclerView recyclerView; private OnFragmentInteractionListener mListener; - /** * 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) { @@ -53,11 +47,11 @@ public class SelectFolderFragment extends Fragment{ pathStack = new Stack<>(); recyclerAdapter = new FolderRecyclerAdapter((SelectFolderActivity) getActivity(), mListener, - PasswordRepository.getPasswords(new File(path), PasswordRepository.getRepositoryDirectory(getActivity()), getSortOrder())); + PasswordRepository.getPasswords(new File(path), PasswordRepository.getRepositoryDirectory(getActivity()), getSortOrder())); } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.password_recycler_view, container, false); @@ -81,28 +75,27 @@ public class SelectFolderFragment extends Fragment{ public void onAttach(final Context context) { super.onAttach(context); try { - mListener = new OnFragmentInteractionListener() { - public void onFragmentInteraction(PasswordItem 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())); - - ((AppCompatActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true); - } + 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())); + + ((AppCompatActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true); } }; } catch (ClassCastException e) { throw new ClassCastException(context.toString() - + " must implement OnFragmentInteractionListener"); + + " must implement OnFragmentInteractionListener"); } } /** * gets the current directory + * * @return the current directory */ public File getCurrentDir() { @@ -115,4 +108,8 @@ public class SelectFolderFragment extends Fragment{ private PasswordRepository.PasswordSortOrder getSortOrder() { return PasswordRepository.PasswordSortOrder.getSortOrder(PreferenceManager.getDefaultSharedPreferences(getActivity())); } + + public interface OnFragmentInteractionListener { + void onFragmentInteraction(PasswordItem item); + } } diff --git a/app/src/main/java/com/zeapo/pwdstore/SshKeyGen.java b/app/src/main/java/com/zeapo/pwdstore/SshKeyGen.java index 72d1c024..476ca6b1 100644 --- a/app/src/main/java/com/zeapo/pwdstore/SshKeyGen.java +++ b/app/src/main/java/com/zeapo/pwdstore/SshKeyGen.java @@ -8,14 +8,11 @@ import android.app.ProgressDialog; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; -import android.content.DialogInterface; import android.content.SharedPreferences; import android.graphics.Typeface; import android.os.AsyncTask; import android.os.Bundle; import android.preference.PreferenceManager; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; import android.text.InputType; import android.view.LayoutInflater; import android.view.View; @@ -24,15 +21,14 @@ import android.view.inputmethod.InputMethodManager; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CheckBox; -import android.widget.CompoundButton; import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; - +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; import com.jcraft.jsch.JSch; import com.jcraft.jsch.KeyPair; - import org.apache.commons.io.FileUtils; import java.io.File; @@ -41,6 +37,34 @@ import java.lang.ref.WeakReference; public class SshKeyGen extends AppCompatActivity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (getSupportActionBar() != null) + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + setTitle("Generate SSH Key"); + + if (savedInstanceState == null) { + getFragmentManager().beginTransaction() + .replace(android.R.id.content, new SshKeyGenFragment()).commit(); + } + } + + // Invoked when 'Generate' button of SshKeyGenFragment clicked. Generates a + // 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 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); + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + // SSH key generation UI public static class SshKeyGenFragment extends Fragment { public SshKeyGenFragment() { @@ -61,18 +85,15 @@ public class SshKeyGen extends AppCompatActivity { ((EditText) v.findViewById(R.id.passphrase)).setTypeface(monoTypeface); CheckBox checkbox = v.findViewById(R.id.show_passphrase); - checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - EditText editText = v.findViewById(R.id.passphrase); - 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) -> { + EditText editText = v.findViewById(R.id.passphrase); + 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; @@ -100,59 +121,31 @@ public class SshKeyGen extends AppCompatActivity { e.printStackTrace(); } - builder.setPositiveButton(getResources().getString(R.string.dialog_ok), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (getActivity() instanceof SshKeyGen) - getActivity().finish(); - } + builder.setPositiveButton(getResources().getString(R.string.dialog_ok), (dialog, which) -> { + if (getActivity() instanceof SshKeyGen) + getActivity().finish(); }); - builder.setNegativeButton(getResources().getString(R.string.dialog_cancel), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int 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(new DialogInterface.OnShowListener() { - @Override - public void onShow(DialogInterface dialog) { - Button b = ad.getButton(AlertDialog.BUTTON_NEUTRAL); - b.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - TextView textView = getDialog().findViewById(R.id.public_key); - ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText("public key", textView.getText().toString()); - clipboard.setPrimaryClip(clip); - } - }); - } + ad.setOnShowListener(dialog -> { + Button b = ad.getButton(AlertDialog.BUTTON_NEUTRAL); + b.setOnClickListener(v1 -> { + TextView textView1 = getDialog().findViewById(R.id.public_key); + ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("public key", textView1.getText().toString()); + clipboard.setPrimaryClip(clip); + }); }); return ad; } } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - if (getSupportActionBar() != null) - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - - setTitle("Generate SSH Key"); - - if (savedInstanceState == null) { - getFragmentManager().beginTransaction() - .replace(android.R.id.content, new SshKeyGenFragment()).commit(); - } - } - private static class KeyGenerateTask extends AsyncTask<String, Void, Exception> { private ProgressDialog pd; private WeakReference<SshKeyGen> weakReference; @@ -211,27 +204,11 @@ public class SshKeyGen extends AppCompatActivity { new AlertDialog.Builder(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), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - // pass - } + .setPositiveButton(weakReference.get().getResources().getString(R.string.dialog_ok), (dialogInterface, i) -> { + // pass }).show(); } } } - - // Invoked when 'Generate' button of SshKeyGenFragment clicked. Generates a - // 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 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); - imm.hideSoftInputFromWindow(view.getWindowToken(), 0); - } } diff --git a/app/src/main/java/com/zeapo/pwdstore/ToCloneOrNot.kt b/app/src/main/java/com/zeapo/pwdstore/ToCloneOrNot.kt index 3df845f2..75a7d4fc 100644 --- a/app/src/main/java/com/zeapo/pwdstore/ToCloneOrNot.kt +++ b/app/src/main/java/com/zeapo/pwdstore/ToCloneOrNot.kt @@ -1,18 +1,18 @@ package com.zeapo.pwdstore import android.os.Bundle -import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup - +import androidx.fragment.app.Fragment class ToCloneOrNot : Fragment() { - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { + override fun onCreateView( + 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) } - } diff --git a/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt b/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt index 69a1cf1a..d93d6bcc 100644 --- a/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt +++ b/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt @@ -15,16 +15,16 @@ import android.preference.Preference import android.preference.PreferenceFragment import android.preference.PreferenceManager import android.provider.Settings -import com.google.android.material.snackbar.Snackbar -import androidx.core.app.ActivityCompat -import androidx.core.content.ContextCompat -import androidx.appcompat.app.AlertDialog -import androidx.appcompat.app.AppCompatActivity import android.util.Log import android.view.MenuItem import android.view.accessibility.AccessibilityManager import android.widget.TextView import android.widget.Toast +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import com.google.android.material.snackbar.Snackbar import com.nononsenseapps.filepicker.FilePickerActivity import com.zeapo.pwdstore.autofill.AutofillPreferenceActivity import com.zeapo.pwdstore.crypto.PgpActivity @@ -36,8 +36,9 @@ import org.openintents.openpgp.util.OpenPgpUtils import java.io.File import java.io.IOException import java.text.SimpleDateFormat -import java.util.* -import kotlin.collections.HashSet +import java.util.ArrayList +import java.util.Date +import java.util.Locale class UserPreference : AppCompatActivity() { private lateinit var prefsFragment: PrefsFragment @@ -75,17 +76,19 @@ class UserPreference : AppCompatActivity() { true } - findPreference("ssh_key_clear_passphrase").onPreferenceClickListener = Preference.OnPreferenceClickListener { - sharedPreferences.edit().putString("ssh_key_passphrase", null).apply() - it.isEnabled = false - true - } + findPreference("ssh_key_clear_passphrase").onPreferenceClickListener = + Preference.OnPreferenceClickListener { + sharedPreferences.edit().putString("ssh_key_passphrase", null).apply() + it.isEnabled = false + true + } - findPreference("hotp_remember_clear_choice").onPreferenceClickListener = Preference.OnPreferenceClickListener { - sharedPreferences.edit().putBoolean("hotp_remember_check", false).apply() - it.isEnabled = false - true - } + findPreference("hotp_remember_clear_choice").onPreferenceClickListener = + Preference.OnPreferenceClickListener { + sharedPreferences.edit().putBoolean("hotp_remember_check", false).apply() + it.isEnabled = false + true + } findPreference("git_server_info").onPreferenceClickListener = Preference.OnPreferenceClickListener { val intent = Intent(callingActivity, GitActivity::class.java) @@ -104,28 +107,30 @@ class UserPreference : AppCompatActivity() { findPreference("git_delete_repo").onPreferenceClickListener = Preference.OnPreferenceClickListener { val repoDir = PasswordRepository.getRepositoryDirectory(callingActivity.applicationContext) AlertDialog.Builder(callingActivity) - .setTitle(R.string.pref_dialog_delete_title) - .setMessage("${resources.getString(R.string.dialog_delete_msg)} \n $repoDir") - .setCancelable(false) - .setPositiveButton(R.string.dialog_delete) { dialogInterface, _ -> - try { - FileUtils.cleanDirectory(PasswordRepository.getRepositoryDirectory(callingActivity.applicationContext)) - PasswordRepository.closeRepository() - } catch (e: Exception) { - //TODO Handle the diffent cases of exceptions - } - - sharedPreferences.edit().putBoolean("repository_initialized", false).apply() - dialogInterface.cancel() - callingActivity.finish() - }.setNegativeButton(R.string.dialog_do_not_delete) { dialogInterface, _ -> run { dialogInterface.cancel() } } - .show() + .setTitle(R.string.pref_dialog_delete_title) + .setMessage("${resources.getString(R.string.dialog_delete_msg)} \n $repoDir") + .setCancelable(false) + .setPositiveButton(R.string.dialog_delete) { dialogInterface, _ -> + try { + FileUtils.cleanDirectory(PasswordRepository.getRepositoryDirectory(callingActivity.applicationContext)) + PasswordRepository.closeRepository() + } catch (e: Exception) { + //TODO Handle the diffent cases of exceptions + } + + sharedPreferences.edit().putBoolean("repository_initialized", false).apply() + dialogInterface.cancel() + callingActivity.finish() + } + .setNegativeButton(R.string.dialog_do_not_delete) { dialogInterface, _ -> run { dialogInterface.cancel() } } + .show() true } val externalRepo = findPreference("pref_select_external") - externalRepo.summary = sharedPreferences.getString("git_external_repo", callingActivity.getString(R.string.no_repo_selected)) + externalRepo.summary = + sharedPreferences.getString("git_external_repo", callingActivity.getString(R.string.no_repo_selected)) externalRepo.onPreferenceClickListener = Preference.OnPreferenceClickListener { callingActivity.selectExternalGitRepository() true @@ -148,10 +153,14 @@ class UserPreference : AppCompatActivity() { } findPreference("autofill_enable").onPreferenceClickListener = Preference.OnPreferenceClickListener { - AlertDialog.Builder(callingActivity).setTitle(R.string.pref_autofill_enable_title).setView(R.layout.autofill_instructions).setPositiveButton(R.string.dialog_ok) { _, _ -> + AlertDialog.Builder(callingActivity).setTitle(R.string.pref_autofill_enable_title) + .setView(R.layout.autofill_instructions).setPositiveButton(R.string.dialog_ok) { _, _ -> val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) startActivity(intent) - }.setNegativeButton(R.string.dialog_cancel, null).setOnDismissListener { (findPreference("autofill_enable") as CheckBoxPreference).isChecked = (activity as UserPreference).isServiceEnabled }.show() + }.setNegativeButton(R.string.dialog_cancel, null).setOnDismissListener { + (findPreference("autofill_enable") as CheckBoxPreference).isChecked = + (activity as UserPreference).isServiceEnabled + }.show() true } @@ -164,24 +173,34 @@ class UserPreference : AppCompatActivity() { override fun onStart() { super.onStart() val sharedPreferences = preferenceManager.sharedPreferences - findPreference("pref_select_external").summary = preferenceManager.sharedPreferences.getString("git_external_repo", getString(R.string.no_repo_selected)) + findPreference("pref_select_external").summary = + preferenceManager.sharedPreferences.getString("git_external_repo", getString(R.string.no_repo_selected)) findPreference("ssh_see_key").isEnabled = sharedPreferences.getBoolean("use_generated_key", false) findPreference("git_delete_repo").isEnabled = !sharedPreferences.getBoolean("git_external", false) - findPreference("ssh_key_clear_passphrase").isEnabled = sharedPreferences.getString("ssh_key_passphrase", null)?.isNotEmpty() ?: false - findPreference("hotp_remember_clear_choice").isEnabled = sharedPreferences.getBoolean("hotp_remember_check", false) + findPreference("ssh_key_clear_passphrase").isEnabled = sharedPreferences.getString( + "ssh_key_passphrase", + null + )?.isNotEmpty() ?: false + findPreference("hotp_remember_clear_choice").isEnabled = + sharedPreferences.getBoolean("hotp_remember_check", false) val keyPref = findPreference("openpgp_key_id_pref") - val selectedKeys: Array<String> = ArrayList<String>(sharedPreferences.getStringSet("openpgp_key_ids_set", HashSet<String>())).toTypedArray() + val selectedKeys: Array<String> = ArrayList<String>( + sharedPreferences.getStringSet( + "openpgp_key_ids_set", + HashSet<String>() + ) + ).toTypedArray() if (selectedKeys.isEmpty()) { keyPref.summary = this.resources.getString(R.string.pref_no_key_selected) } else { - keyPref.summary = selectedKeys.joinToString(separator = ";") { - s -> + keyPref.summary = selectedKeys.joinToString(separator = ";") { s -> OpenPgpUtils.convertKeyIdToHex(java.lang.Long.valueOf(s)) } } // see if the autofill service is enabled and check the preference accordingly - (findPreference("autofill_enable") as CheckBoxPreference).isChecked = (activity as UserPreference).isServiceEnabled + (findPreference("autofill_enable") as CheckBoxPreference).isChecked = + (activity as UserPreference).isServiceEnabled } } @@ -203,24 +222,23 @@ class UserPreference : AppCompatActivity() { fun selectExternalGitRepository() { val activity = this AlertDialog.Builder(this) - .setTitle(this.resources.getString(R.string.external_repository_dialog_title)) - .setMessage(this.resources.getString(R.string.external_repository_dialog_text)) - .setPositiveButton(R.string.dialog_ok) { _, _ -> - // This always works - val i = Intent(activity.applicationContext, FilePickerActivity::class.java) - // This works if you defined the intent filter - // Intent i = new Intent(Intent.ACTION_GET_CONTENT); - - // Set these depending on your use case. These are the defaults. - i.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false) - i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, true) - i.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR) - - i.putExtra(FilePickerActivity.EXTRA_START_PATH, Environment.getExternalStorageDirectory().path) - - startActivityForResult(i, SELECT_GIT_DIRECTORY) - }.setNegativeButton(R.string.dialog_cancel, null).show() - + .setTitle(this.resources.getString(R.string.external_repository_dialog_title)) + .setMessage(this.resources.getString(R.string.external_repository_dialog_text)) + .setPositiveButton(R.string.dialog_ok) { _, _ -> + // This always works + val i = Intent(activity.applicationContext, FilePickerActivity::class.java) + // This works if you defined the intent filter + // Intent i = new Intent(Intent.ACTION_GET_CONTENT); + + // Set these depending on your use case. These are the defaults. + i.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false) + i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, true) + i.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR) + + i.putExtra(FilePickerActivity.EXTRA_START_PATH, Environment.getExternalStorageDirectory().path) + + startActivityForResult(i, SELECT_GIT_DIRECTORY) + }.setNegativeButton(R.string.dialog_cancel, null).show() } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -242,9 +260,9 @@ class UserPreference : AppCompatActivity() { * Opens a file explorer to import the private key */ fun getSshKeyWithPermissions(useDefaultPicker: Boolean) = runWithPermissions( - requestedPermission = Manifest.permission.READ_EXTERNAL_STORAGE, - requestCode = REQUEST_EXTERNAL_STORAGE_SSH_KEY, - reason = "We need access to the sd-card to import the ssh-key" + requestedPermission = Manifest.permission.READ_EXTERNAL_STORAGE, + requestCode = REQUEST_EXTERNAL_STORAGE_SSH_KEY, + reason = "We need access to the sd-card to import the ssh-key" ) { getSshKey(useDefaultPicker) } @@ -282,9 +300,9 @@ class UserPreference : AppCompatActivity() { if (ContextCompat.checkSelfPermission(this, requestedPermission) != PackageManager.PERMISSION_GRANTED) { if (ActivityCompat.shouldShowRequestPermissionRationale(this, requestedPermission)) { val snack = Snackbar.make(prefsFragment.view, reason, Snackbar.LENGTH_INDEFINITE) - .setAction(R.string.dialog_ok) { - ActivityCompat.requestPermissions(this, arrayOf(requestedPermission), requestCode) - } + .setAction(R.string.dialog_ok) { + ActivityCompat.requestPermissions(this, arrayOf(requestedPermission), requestCode) + } snack.show() val view = snack.view val tv = view.findViewById<TextView>(com.google.android.material.R.id.snackbar_text) @@ -297,16 +315,15 @@ class UserPreference : AppCompatActivity() { } else { body() } - } /** * Exports the passwords after requesting permissions */ fun exportPasswordsWithPermissions() = runWithPermissions( - requestedPermission = Manifest.permission.WRITE_EXTERNAL_STORAGE, - requestCode = REQUEST_EXTERNAL_STORAGE_SSH_KEY, - reason = "We need access to the sd-card to export the passwords" + requestedPermission = Manifest.permission.WRITE_EXTERNAL_STORAGE, + requestCode = REQUEST_EXTERNAL_STORAGE_SSH_KEY, + reason = "We need access to the sd-card to export the passwords" ) { exportPasswords() } @@ -355,15 +372,16 @@ class UserPreference : AppCompatActivity() { private val isServiceEnabled: Boolean get() { val am = this - .getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager + .getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager val runningServices = am - .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC) + .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC) return runningServices.any { "com.zeapo.pwdstore/.autofill.AutofillService" == it.id } } - - override fun onActivityResult(requestCode: Int, resultCode: Int, - data: Intent?) { + override fun onActivityResult( + requestCode: Int, resultCode: Int, + data: Intent? + ) { if (resultCode == Activity.RESULT_OK) { if (data == null) { setResult(Activity.RESULT_CANCELED) @@ -376,7 +394,11 @@ class UserPreference : AppCompatActivity() { val uri: Uri = data.data ?: throw IOException("Unable to open file") copySshKey(uri) - Toast.makeText(this, this.resources.getString(R.string.ssh_key_success_dialog_title), Toast.LENGTH_LONG).show() + Toast.makeText( + this, + this.resources.getString(R.string.ssh_key_success_dialog_title), + Toast.LENGTH_LONG + ).show() val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext) prefs.edit().putBoolean("use_generated_key", false).apply() @@ -388,11 +410,13 @@ class UserPreference : AppCompatActivity() { finish() } catch (e: IOException) { - AlertDialog.Builder(this).setTitle(this.resources.getString(R.string.ssh_key_error_dialog_title)).setMessage(this.resources.getString(R.string.ssh_key_error_dialog_text) + e.message).setPositiveButton(this.resources.getString(R.string.dialog_ok)) { _, _ -> - // pass - }.show() + AlertDialog.Builder(this) + .setTitle(this.resources.getString(R.string.ssh_key_error_dialog_title)) + .setMessage(this.resources.getString(R.string.ssh_key_error_dialog_text) + e.message) + .setPositiveButton(this.resources.getString(R.string.dialog_ok)) { _, _ -> + // pass + }.show() } - } EDIT_GIT_INFO -> { @@ -403,21 +427,23 @@ class UserPreference : AppCompatActivity() { if (uri?.path == Environment.getExternalStorageDirectory().path) { // the user wants to use the root of the sdcard as a store... AlertDialog.Builder(this) - .setTitle("SD-Card root selected") - .setMessage("You have selected the root of your sdcard for the store. " + - "This is extremely dangerous and you will lose your data " + - "as its content will, eventually, be deleted") - .setPositiveButton("Remove everything") { _, _ -> - PreferenceManager.getDefaultSharedPreferences(applicationContext) - .edit() - .putString("git_external_repo", uri?.path) - .apply() - }.setNegativeButton(R.string.dialog_cancel, null).show() + .setTitle("SD-Card root selected") + .setMessage( + "You have selected the root of your sdcard for the store. " + + "This is extremely dangerous and you will lose your data " + + "as its content will, eventually, be deleted" + ) + .setPositiveButton("Remove everything") { _, _ -> + PreferenceManager.getDefaultSharedPreferences(applicationContext) + .edit() + .putString("git_external_repo", uri?.path) + .apply() + }.setNegativeButton(R.string.dialog_cancel, null).show() } else { PreferenceManager.getDefaultSharedPreferences(applicationContext) - .edit() - .putString("git_external_repo", uri?.path) - .apply() + .edit() + .putString("git_external_repo", uri?.path) + .apply() } } EXPORT_PASSWORDS -> { @@ -433,7 +459,6 @@ class UserPreference : AppCompatActivity() { } catch (e: IOException) { Log.d("PWD_EXPORT", "Exception happened : " + e.message) } - } } else -> { diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillActivity.java b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillActivity.java index 0d39e01d..12d1de74 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillActivity.java +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillActivity.java @@ -6,11 +6,9 @@ import android.content.Intent; import android.content.IntentSender; import android.content.SharedPreferences; import android.os.Bundle; -import androidx.appcompat.app.AppCompatActivity; import android.util.Log; - +import androidx.appcompat.app.AppCompatActivity; import com.zeapo.pwdstore.PasswordStore; - import org.eclipse.jgit.util.StringUtils; import java.util.ArrayList; diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.java b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.java index 3a3b632d..d7095dae 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.java +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.java @@ -5,18 +5,13 @@ import android.app.Activity; import android.app.Dialog; import android.app.DialogFragment; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.core.content.ContextCompat; -import androidx.appcompat.app.AlertDialog; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; @@ -24,7 +19,9 @@ import android.widget.ListView; import android.widget.RadioButton; import android.widget.RadioGroup; import android.widget.TextView; - +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.core.content.ContextCompat; import com.zeapo.pwdstore.PasswordStore; import com.zeapo.pwdstore.R; @@ -84,12 +81,7 @@ public class AutofillFragment extends DialogFragment { ((ListView) view.findViewById(R.id.matched)).setAdapter(adapter); // delete items by clicking them ((ListView) view.findViewById(R.id.matched)).setOnItemClickListener( - new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - adapter.remove(adapter.getItem(position)); - } - }); + (parent, view1, position, id) -> adapter.remove(adapter.getItem(position))); // set the existing preference, if any SharedPreferences prefs; @@ -116,36 +108,27 @@ public class AutofillFragment extends DialogFragment { } // add items with the + button - View.OnClickListener matchPassword = new View.OnClickListener() { - @Override - public void onClick(View v) { - ((RadioButton) view.findViewById(R.id.match)).toggle(); - Intent intent = new Intent(getActivity(), PasswordStore.class); - intent.putExtra("matchWith", true); - startActivityForResult(intent, MATCH_WITH); - } + View.OnClickListener matchPassword = v -> { + ((RadioButton) view.findViewById(R.id.match)).toggle(); + Intent intent = new Intent(getActivity(), PasswordStore.class); + intent.putExtra("matchWith", true); + startActivityForResult(intent, MATCH_WITH); }; view.findViewById(R.id.matchButton).setOnClickListener(matchPassword); // write to preferences when OK clicked - builder.setPositiveButton(R.string.dialog_ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { + builder.setPositiveButton(R.string.dialog_ok, (dialog, which) -> { - } }); builder.setNegativeButton(R.string.dialog_cancel, null); final SharedPreferences.Editor editor = prefs.edit(); if (isWeb) { - builder.setNeutralButton(R.string.autofill_apps_delete, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (callingActivity.recyclerAdapter != null - && packageName != null && !packageName.equals("")) { - editor.remove(packageName); - callingActivity.recyclerAdapter.removeWebsite(packageName); - editor.apply(); - } + builder.setNeutralButton(R.string.autofill_apps_delete, (dialog, which) -> { + if (callingActivity.recyclerAdapter != null + && packageName != null && !packageName.equals("")) { + editor.remove(packageName); + callingActivity.recyclerAdapter.removeWebsite(packageName); + editor.apply(); } }); } @@ -157,91 +140,88 @@ public class AutofillFragment extends DialogFragment { public void onStart() { super.onStart(); AlertDialog ad = (AlertDialog) getDialog(); - if(ad != null) { + if (ad != null) { Button positiveButton = ad.getButton(Dialog.BUTTON_POSITIVE); - positiveButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - AutofillPreferenceActivity callingActivity = (AutofillPreferenceActivity) getActivity(); - Dialog dialog = getDialog(); - - SharedPreferences prefs; - if (!isWeb) { - prefs = getActivity().getApplicationContext().getSharedPreferences("autofill", Context.MODE_PRIVATE); - } else { - prefs = getActivity().getApplicationContext().getSharedPreferences("autofill_web", Context.MODE_PRIVATE); - } - SharedPreferences.Editor editor = prefs.edit(); + positiveButton.setOnClickListener(v -> { + AutofillPreferenceActivity callingActivity = (AutofillPreferenceActivity) getActivity(); + Dialog dialog = getDialog(); + + SharedPreferences prefs; + if (!isWeb) { + prefs = getActivity().getApplicationContext().getSharedPreferences("autofill", Context.MODE_PRIVATE); + } else { + prefs = getActivity().getApplicationContext().getSharedPreferences("autofill_web", Context.MODE_PRIVATE); + } + SharedPreferences.Editor editor = prefs.edit(); - String packageName = getArguments().getString("packageName", ""); - if (isWeb) { - // handle some errors and don't dismiss the dialog - EditText webURL = (EditText) dialog.findViewById(R.id.webURL); + String packageName = getArguments().getString("packageName", ""); + if (isWeb) { + // handle some errors and don't dismiss the dialog + EditText webURL = dialog.findViewById(R.id.webURL); - packageName = webURL.getText().toString(); + packageName = webURL.getText().toString(); - if (packageName.equals("")) { - webURL.setError("URL cannot be blank"); - return; - } - String oldPackageName = getArguments().getString("packageName", ""); - if (!oldPackageName.equals(packageName) && prefs.getAll().containsKey(packageName)) { - webURL.setError("URL already exists"); - return; - } + if (packageName.equals("")) { + webURL.setError("URL cannot be blank"); + return; } - - // write to preferences accordingly - RadioGroup radioGroup = (RadioGroup) dialog.findViewById(R.id.autofill_radiogroup); - switch (radioGroup.getCheckedRadioButtonId()) { - case R.id.use_default: - if (!isWeb) { - editor.remove(packageName); - } else { - editor.putString(packageName, ""); - } - break; - case R.id.first: - editor.putString(packageName, "/first"); - break; - case R.id.never: - editor.putString(packageName, "/never"); - break; - default: - StringBuilder paths = new StringBuilder(); - for (int i = 0; i < adapter.getCount(); i++) { - paths.append(adapter.getItem(i)); - if (i != adapter.getCount()) { - paths.append("\n"); - } - } - editor.putString(packageName, paths.toString()); + String oldPackageName = getArguments().getString("packageName", ""); + if (!oldPackageName.equals(packageName) && prefs.getAll().containsKey(packageName)) { + webURL.setError("URL already exists"); + return; } - editor.apply(); + } - // notify the recycler adapter if it is loaded - if (callingActivity.recyclerAdapter != null) { - int position; + // write to preferences accordingly + RadioGroup radioGroup = dialog.findViewById(R.id.autofill_radiogroup); + switch (radioGroup.getCheckedRadioButtonId()) { + case R.id.use_default: if (!isWeb) { - String appName = getArguments().getString("appName", ""); - position = callingActivity.recyclerAdapter.getPosition(appName); - callingActivity.recyclerAdapter.notifyItemChanged(position); + editor.remove(packageName); } else { - position = callingActivity.recyclerAdapter.getPosition(packageName); - String oldPackageName = getArguments().getString("packageName", ""); - if (oldPackageName.equals(packageName)) { - callingActivity.recyclerAdapter.notifyItemChanged(position); - } else if (oldPackageName.equals("")){ - callingActivity.recyclerAdapter.addWebsite(packageName); - } else { - editor.remove(oldPackageName); - callingActivity.recyclerAdapter.updateWebsite(oldPackageName, packageName); + editor.putString(packageName, ""); + } + break; + case R.id.first: + editor.putString(packageName, "/first"); + break; + case R.id.never: + editor.putString(packageName, "/never"); + break; + default: + StringBuilder paths = new StringBuilder(); + for (int i = 0; i < adapter.getCount(); i++) { + paths.append(adapter.getItem(i)); + if (i != adapter.getCount()) { + paths.append("\n"); } } - } + editor.putString(packageName, paths.toString()); + } + editor.apply(); - dismiss(); + // notify the recycler adapter if it is loaded + if (callingActivity.recyclerAdapter != null) { + int position; + if (!isWeb) { + String appName = getArguments().getString("appName", ""); + position = callingActivity.recyclerAdapter.getPosition(appName); + callingActivity.recyclerAdapter.notifyItemChanged(position); + } else { + position = callingActivity.recyclerAdapter.getPosition(packageName); + String oldPackageName = getArguments().getString("packageName", ""); + if (oldPackageName.equals(packageName)) { + callingActivity.recyclerAdapter.notifyItemChanged(position); + } else if (oldPackageName.equals("")) { + callingActivity.recyclerAdapter.addWebsite(packageName); + } else { + editor.remove(oldPackageName); + callingActivity.recyclerAdapter.updateWebsite(oldPackageName, packageName); + } + } } + + dismiss(); }); } } diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillPreferenceActivity.java b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillPreferenceActivity.java index b85a31c7..9fba959a 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillPreferenceActivity.java +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillPreferenceActivity.java @@ -8,19 +8,18 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.AsyncTask; import android.os.Bundle; -import com.google.android.material.floatingactionbutton.FloatingActionButton; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.SearchView; import androidx.core.app.NavUtils; import androidx.core.app.TaskStackBuilder; import androidx.core.view.MenuItemCompat; -import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import androidx.appcompat.widget.SearchView; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; - +import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.zeapo.pwdstore.R; import java.util.ArrayList; @@ -29,9 +28,8 @@ import java.util.Map; public class AutofillPreferenceActivity extends AppCompatActivity { - private RecyclerView recyclerView; AutofillRecyclerAdapter recyclerAdapter; // let fragment have access - + private RecyclerView recyclerView; private PackageManager pm; private boolean recreate; // flag for action on up press; origin autofill dialog? different act @@ -41,7 +39,7 @@ public class AutofillPreferenceActivity extends AppCompatActivity { super.onCreate(savedInstanceState); setContentView(R.layout.autofill_recycler_view); - recyclerView = (RecyclerView) findViewById(R.id.autofill_recycler); + recyclerView = findViewById(R.id.autofill_recycler); RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); @@ -62,64 +60,8 @@ public class AutofillPreferenceActivity extends AppCompatActivity { setTitle("Autofill Apps"); - final FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); - fab.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - showDialog("", "", true); - } - }); - } - - private class populateTask extends AsyncTask<Void, Void, Void> { - @Override - protected void onPreExecute() { - runOnUiThread(new Runnable() { - public void run() { - findViewById(R.id.progress_bar).setVisibility(View.VISIBLE); - } - }); - } - - @Override - protected Void doInBackground(Void... params) { - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.addCategory(Intent.CATEGORY_LAUNCHER); - List<ResolveInfo> allAppsResolveInfo = pm.queryIntentActivities(intent, 0); - List<AutofillRecyclerAdapter.AppInfo> allApps = new ArrayList<>(); - - for (ResolveInfo app : allAppsResolveInfo) { - allApps.add(new AutofillRecyclerAdapter.AppInfo(app.activityInfo.packageName - , app.loadLabel(pm).toString(), false, app.loadIcon(pm))); - } - - SharedPreferences prefs = getSharedPreferences("autofill_web", Context.MODE_PRIVATE); - Map<String, ?> prefsMap = prefs.getAll(); - for (String key : prefsMap.keySet()) { - try { - allApps.add(new AutofillRecyclerAdapter.AppInfo(key, key, true, pm.getApplicationIcon("com.android.browser"))); - } catch (PackageManager.NameNotFoundException e) { - allApps.add(new AutofillRecyclerAdapter.AppInfo(key, key, true, null)); - } - } - - recyclerAdapter = new AutofillRecyclerAdapter(allApps, pm, AutofillPreferenceActivity.this); - return null; - } - - @Override - protected void onPostExecute(Void aVoid) { - runOnUiThread(new Runnable() { - public void run() { - findViewById(R.id.progress_bar).setVisibility(View.GONE); - recyclerView.setAdapter(recyclerAdapter); - Bundle extras = getIntent().getExtras(); - if (extras != null) { - recyclerView.scrollToPosition(recyclerAdapter.getPosition(extras.getString("appName"))); - } - } - }); - } + final FloatingActionButton fab = findViewById(R.id.fab); + fab.setOnClickListener(v -> showDialog("", "", true)); } @Override @@ -175,4 +117,49 @@ public class AutofillPreferenceActivity extends AppCompatActivity { df.setArguments(args); df.show(getFragmentManager(), "autofill_dialog"); } + + private class populateTask extends AsyncTask<Void, Void, Void> { + @Override + protected void onPreExecute() { + runOnUiThread(() -> findViewById(R.id.progress_bar).setVisibility(View.VISIBLE)); + } + + @Override + protected Void doInBackground(Void... params) { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + List<ResolveInfo> allAppsResolveInfo = pm.queryIntentActivities(intent, 0); + List<AutofillRecyclerAdapter.AppInfo> allApps = new ArrayList<>(); + + for (ResolveInfo app : allAppsResolveInfo) { + allApps.add(new AutofillRecyclerAdapter.AppInfo(app.activityInfo.packageName + , app.loadLabel(pm).toString(), false, app.loadIcon(pm))); + } + + SharedPreferences prefs = getSharedPreferences("autofill_web", Context.MODE_PRIVATE); + Map<String, ?> prefsMap = prefs.getAll(); + for (String key : prefsMap.keySet()) { + try { + allApps.add(new AutofillRecyclerAdapter.AppInfo(key, key, true, pm.getApplicationIcon("com.android.browser"))); + } catch (PackageManager.NameNotFoundException e) { + allApps.add(new AutofillRecyclerAdapter.AppInfo(key, key, true, null)); + } + } + + recyclerAdapter = new AutofillRecyclerAdapter(allApps, pm, AutofillPreferenceActivity.this); + return null; + } + + @Override + protected void onPostExecute(Void aVoid) { + runOnUiThread(() -> { + findViewById(R.id.progress_bar).setVisibility(View.GONE); + recyclerView.setAdapter(recyclerAdapter); + Bundle extras = getIntent().getExtras(); + if (extras != null) { + recyclerView.scrollToPosition(recyclerAdapter.getPosition(extras.getString("appName"))); + } + }); + } + } } diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillRecyclerAdapter.java b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillRecyclerAdapter.java index 0c1ec224..bed2aa7e 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillRecyclerAdapter.java +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillRecyclerAdapter.java @@ -4,15 +4,14 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; -import androidx.recyclerview.widget.SortedList; -import androidx.recyclerview.widget.RecyclerView; -import androidx.recyclerview.widget.SortedListAdapterCallback; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; - +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.SortedList; +import androidx.recyclerview.widget.SortedListAdapterCallback; import com.zeapo.pwdstore.R; import java.util.ArrayList; @@ -25,50 +24,6 @@ class AutofillRecyclerAdapter extends RecyclerView.Adapter<AutofillRecyclerAdapt private AutofillPreferenceActivity activity; private Drawable browserIcon = null; - class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { - public View view; - public TextView name; - TextView secondary; - public ImageView icon; - String packageName; - String appName; - Boolean isWeb; - - ViewHolder(View view) { - super(view); - this.view = view; - name = (TextView) view.findViewById(R.id.app_name); - secondary = (TextView) view.findViewById(R.id.secondary_text); - icon = (ImageView) view.findViewById(R.id.app_icon); - view.setOnClickListener(this); - } - - @Override - public void onClick(View v) { - activity.showDialog(packageName, appName, isWeb); - } - - } - - static class AppInfo { - String packageName; - String appName; - boolean isWeb; - public Drawable icon; - - AppInfo(String packageName, String appName, boolean isWeb, Drawable icon) { - this.packageName = packageName; - this.appName = appName; - this.isWeb = isWeb; - this.icon = icon; - } - - @Override - public boolean equals(Object o) { - return o != null && o instanceof AppInfo && this.appName.equals(((AppInfo) o).appName); - } - } - AutofillRecyclerAdapter(List<AppInfo> allApps, final PackageManager pm , AutofillPreferenceActivity activity) { SortedList.Callback<AppInfo> callback = new SortedListAdapterCallback<AppInfo>(this) { @@ -76,17 +31,17 @@ class AutofillRecyclerAdapter extends RecyclerView.Adapter<AutofillRecyclerAdapt // for the limited add/remove usage for websites @Override public int compare(AppInfo o1, AppInfo o2) { - return o1.appName.toLowerCase().compareTo(o2.appName.toLowerCase()); + return o1.appName.toLowerCase().compareTo(o2.appName.toLowerCase()); } @Override public boolean areContentsTheSame(AppInfo oldItem, AppInfo newItem) { - return oldItem.appName.equals(newItem.appName); + return oldItem.appName.equals(newItem.appName); } @Override public boolean areItemsTheSame(AppInfo item1, AppInfo item2) { - return item1.appName.equals(item2.appName); + return item1.appName.equals(item2.appName); } }; this.apps = new SortedList<>(AppInfo.class, callback); @@ -170,7 +125,7 @@ class AutofillRecyclerAdapter extends RecyclerView.Adapter<AutofillRecyclerAdapt } void updateWebsite(String oldPackageName, String packageName) { - apps.updateItemAt(getPosition(oldPackageName), new AppInfo (packageName, packageName, true, browserIcon)); + apps.updateItemAt(getPosition(oldPackageName), new AppInfo(packageName, packageName, true, browserIcon)); allApps.remove(new AppInfo(null, oldPackageName, false, null)); // compare with equals allApps.add(new AppInfo(null, packageName, false, null)); } @@ -190,4 +145,48 @@ class AutofillRecyclerAdapter extends RecyclerView.Adapter<AutofillRecyclerAdapt } apps.endBatchedUpdates(); } + + static class AppInfo { + public Drawable icon; + String packageName; + String appName; + boolean isWeb; + + AppInfo(String packageName, String appName, boolean isWeb, Drawable icon) { + this.packageName = packageName; + this.appName = appName; + this.isWeb = isWeb; + this.icon = icon; + } + + @Override + public boolean equals(Object o) { + return o instanceof AppInfo && this.appName.equals(((AppInfo) o).appName); + } + } + + class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + public View view; + public TextView name; + public ImageView icon; + TextView secondary; + String packageName; + String appName; + Boolean isWeb; + + ViewHolder(View view) { + super(view); + this.view = view; + name = view.findViewById(R.id.app_name); + secondary = view.findViewById(R.id.secondary_text); + icon = view.findViewById(R.id.app_icon); + view.setOnClickListener(this); + } + + @Override + public void onClick(View v) { + activity.showDialog(packageName, appName, isWeb); + } + + } } diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.java b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.java index 7a4d59fa..8ad27b4f 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.java +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.java @@ -1,12 +1,10 @@ package com.zeapo.pwdstore.autofill; import android.accessibilityservice.AccessibilityService; -import android.annotation.TargetApi; import android.app.PendingIntent; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; @@ -16,18 +14,16 @@ import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; import android.provider.Settings; -import androidx.appcompat.app.AlertDialog; import android.util.Log; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityWindowInfo; import android.widget.Toast; - +import androidx.appcompat.app.AlertDialog; import com.zeapo.pwdstore.PasswordEntry; import com.zeapo.pwdstore.R; import com.zeapo.pwdstore.utils.PasswordRepository; - import org.apache.commons.io.FileUtils; import org.openintents.openpgp.IOpenPgpService2; import org.openintents.openpgp.OpenPgpError; @@ -62,10 +58,6 @@ public class AutofillService extends AccessibilityService { private PasswordEntry lastPassword; private long lastPasswordMaxDate; - final class Constants { - static final String TAG = "Keychain"; - } - public static AutofillService getInstance() { return instance; } @@ -96,11 +88,6 @@ public class AutofillService extends AccessibilityService { @Override public void onAccessibilityEvent(AccessibilityEvent event) { - // TODO there should be a better way of disabling service - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { - return; - } - // remove stored password from cache if (lastPassword != null && System.currentTimeMillis() > lastPasswordMaxDate) { lastPassword = null; @@ -405,20 +392,14 @@ public class AutofillService extends AccessibilityService { } AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.Theme_AppCompat_Dialog); - builder.setNegativeButton(R.string.dialog_cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface d, int which) { - dialog.dismiss(); - dialog = null; - } + builder.setNegativeButton(R.string.dialog_cancel, (d, which) -> { + dialog.dismiss(); + dialog = null; }); - builder.setPositiveButton(R.string.autofill_paste, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface d, int which) { - pasteText(node, password.getUsername()); - dialog.dismiss(); - dialog = null; - } + builder.setPositiveButton(R.string.autofill_paste, (d, which) -> { + pasteText(node, password.getUsername()); + dialog.dismiss(); + dialog = null; }); builder.setMessage(getString(R.string.autofill_paste_username, password.getUsername())); @@ -436,24 +417,19 @@ public class AutofillService extends AccessibilityService { } AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.Theme_AppCompat_Dialog); - builder.setNegativeButton(R.string.dialog_cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface d, int which) { - dialog.dismiss(); - dialog = null; - } + builder.setNegativeButton(R.string.dialog_cancel, (d, which) -> { + dialog.dismiss(); + dialog = null; }); - builder.setNeutralButton("Settings", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { //TODO make icon? gear? - // the user will have to return to the app themselves. - Intent intent = new Intent(AutofillService.this, AutofillPreferenceActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - intent.putExtra("packageName", packageName); - intent.putExtra("appName", appName); - intent.putExtra("isWeb", isWeb); - startActivity(intent); - } + builder.setNeutralButton("Settings", (dialog, which) -> { + //TODO make icon? gear? + // the user will have to return to the app themselves. + Intent intent = new Intent(AutofillService.this, AutofillPreferenceActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.putExtra("packageName", packageName); + intent.putExtra("appName", appName); + intent.putExtra("isWeb", isWeb); + startActivity(intent); }); // populate the dialog items, always with pick + pick and match. Could @@ -464,26 +440,23 @@ public class AutofillService extends AccessibilityService { } itemNames[items.size()] = getString(R.string.autofill_pick); itemNames[items.size() + 1] = getString(R.string.autofill_pick_and_match); - builder.setItems(itemNames, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - lastWhichItem = which; - if (which < items.size()) { - bindDecryptAndVerify(); - } else if (which == items.size()) { - Intent intent = new Intent(AutofillService.this, AutofillActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - intent.putExtra("pick", true); - startActivity(intent); - } else { - lastWhichItem--; // will add one element to items, so lastWhichItem=items.size()+1 - Intent intent = new Intent(AutofillService.this, AutofillActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - intent.putExtra("pickMatchWith", true); - intent.putExtra("packageName", packageName); - intent.putExtra("isWeb", isWeb); - startActivity(intent); - } + builder.setItems(itemNames, (dialog, which) -> { + lastWhichItem = which; + if (which < items.size()) { + bindDecryptAndVerify(); + } else if (which == items.size()) { + Intent intent = new Intent(AutofillService.this, AutofillActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.putExtra("pick", true); + startActivity(intent); + } else { + lastWhichItem--; // will add one element to items, so lastWhichItem=items.size()+1 + Intent intent = new Intent(AutofillService.this, AutofillActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.putExtra("pickMatchWith", true); + intent.putExtra("packageName", packageName); + intent.putExtra("isWeb", isWeb); + startActivity(intent); } }); @@ -515,18 +488,6 @@ public class AutofillService extends AccessibilityService { } - private class onBoundListener implements OpenPgpServiceConnection.OnBound { - @Override - public void onBound(IOpenPgpService2 service) { - decryptAndVerify(); - } - - @Override - public void onError(Exception e) { - e.printStackTrace(); - } - } - private void bindDecryptAndVerify() { if (serviceConnection.getService() == null) { // the service was disconnected, need to bind again @@ -600,7 +561,6 @@ public class AutofillService extends AccessibilityService { } } - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) private void pasteText(final AccessibilityNodeInfo node, final String text) { // if the user focused on something else, take focus back // but this will open another dialog...hack to ignore this @@ -627,4 +587,20 @@ public class AutofillService extends AccessibilityService { } node.recycle(); } + + final class Constants { + static final String TAG = "Keychain"; + } + + private class onBoundListener implements OpenPgpServiceConnection.OnBound { + @Override + public void onBound(IOpenPgpService2 service) { + decryptAndVerify(); + } + + @Override + public void onError(Exception e) { + e.printStackTrace(); + } + } } diff --git a/app/src/main/java/com/zeapo/pwdstore/crypto/PgpActivity.kt b/app/src/main/java/com/zeapo/pwdstore/crypto/PgpActivity.kt index 63fff21d..9be1f170 100644 --- a/app/src/main/java/com/zeapo/pwdstore/crypto/PgpActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/crypto/PgpActivity.kt @@ -32,9 +32,9 @@ import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import com.zeapo.pwdstore.PasswordEntry +import com.zeapo.pwdstore.PasswordGeneratorDialogFragment import com.zeapo.pwdstore.R import com.zeapo.pwdstore.UserPreference -import com.zeapo.pwdstore.PasswordGeneratorDialogFragment import com.zeapo.pwdstore.utils.Otp import kotlinx.android.synthetic.main.decrypt_layout.* import kotlinx.android.synthetic.main.encrypt_layout.* @@ -73,7 +73,14 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { private val fullPath: String by lazy { intent.getStringExtra("FILE_PATH") } private val name: String by lazy { getName(fullPath) } - private val lastChangedString: CharSequence by lazy { getLastChangedString(intent.getIntExtra("LAST_CHANGED_TIMESTAMP", -1)) } + private val lastChangedString: CharSequence by lazy { + getLastChangedString( + intent.getIntExtra( + "LAST_CHANGED_TIMESTAMP", + -1 + ) + ) + } private val relativeParentPath: String by lazy { getParentPath(fullPath, repoPath) } private val settings: SharedPreferences by lazy { PreferenceManager.getDefaultSharedPreferences(this) } @@ -122,7 +129,6 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { crypto_password_category.text = getRelativePath(fullPath, repoPath) } } - } override fun onDestroy() { @@ -147,7 +153,7 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { override fun onOptionsItemSelected(item: MenuItem?): Boolean { when (item?.itemId) { android.R.id.home -> { - if(passwordEntry?.hotpIsIncremented() == false) { + if (passwordEntry?.hotpIsIncremented() == false) { setResult(RESULT_CANCELED) } finish() @@ -157,7 +163,7 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { R.id.edit_password -> editPassword() R.id.crypto_confirm_add -> encrypt() R.id.crypto_cancel_add -> { - if(passwordEntry?.hotpIsIncremented() == false) { + if (passwordEntry?.hotpIsIncremented() == false) { setResult(RESULT_CANCELED) } finish() @@ -186,8 +192,9 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { val pi: PendingIntent = result.getParcelableExtra(RESULT_INTENT) try { this@PgpActivity.startIntentSenderFromChild( - this@PgpActivity, pi.intentSender, requestCode, - null, 0, 0, 0) + this@PgpActivity, pi.intentSender, requestCode, + null, 0, 0, 0 + ) } catch (e: IntentSender.SendIntentException) { Log.e(TAG, "SendIntentException", e) } @@ -258,8 +265,8 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { null } else { HoldToShowPasswordTransformation( - crypto_password_toggle_show, - Runnable { crypto_password_show.text = entry.password } + crypto_password_toggle_show, + Runnable { crypto_password_show.text = entry.password } ) } @@ -277,8 +284,7 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { crypto_copy_username.setOnClickListener { copyUsernameToClipBoard(entry.username) } crypto_username_show.typeface = monoTypeface crypto_username_show.text = entry.username - } - else { + } else { crypto_username_show.visibility = View.GONE crypto_username_show_label.visibility = View.GONE crypto_copy_username.visibility = View.GONE @@ -295,8 +301,16 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { crypto_copy_otp.visibility = View.VISIBLE if (entry.hasTotp()) { - crypto_copy_otp.setOnClickListener { copyOtpToClipBoard(Otp.calculateCode(entry.totpSecret, Date().time / (1000 * Otp.TIME_WINDOW))) } - crypto_otp_show.text = Otp.calculateCode(entry.totpSecret, Date().time / (1000 * Otp.TIME_WINDOW)) + crypto_copy_otp.setOnClickListener { + copyOtpToClipBoard( + Otp.calculateCode( + entry.totpSecret, + Date().time / (1000 * Otp.TIME_WINDOW) + ) + ) + } + crypto_otp_show.text = + Otp.calculateCode(entry.totpSecret, Date().time / (1000 * Otp.TIME_WINDOW)) } else { // we only want to calculate and show HOTP if the user requests it crypto_copy_otp.setOnClickListener { @@ -310,22 +324,23 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { // show a dialog asking permission to update the HOTP counter in the entry val checkInflater = LayoutInflater.from(this) val checkLayout = checkInflater.inflate(R.layout.otp_confirm_layout, null) - val rememberCheck : CheckBox = checkLayout.findViewById(R.id.hotp_remember_checkbox) + val rememberCheck: CheckBox = + checkLayout.findViewById(R.id.hotp_remember_checkbox) val dialogBuilder = AlertDialog.Builder(this) dialogBuilder.setView(checkLayout) dialogBuilder.setMessage(R.string.dialog_update_body) - .setCancelable(false) - .setPositiveButton(R.string.dialog_update_positive) { _, _ -> - run { - calculateAndCommitHotp(entry) - if (rememberCheck.isChecked) { - val editor = settings.edit() - editor.putBoolean("hotp_remember_check", true) - editor.putBoolean("hotp_remember_choice", true) - editor.apply() - } + .setCancelable(false) + .setPositiveButton(R.string.dialog_update_positive) { _, _ -> + run { + calculateAndCommitHotp(entry) + if (rememberCheck.isChecked) { + val editor = settings.edit() + editor.putBoolean("hotp_remember_check", true) + editor.putBoolean("hotp_remember_choice", true) + editor.apply() } } + } .setNegativeButton(R.string.dialog_update_negative) { _, _ -> run { calculateHotp(entry) @@ -343,7 +358,6 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { crypto_otp_show.setText(R.string.hotp_pending) } crypto_otp_show.typeface = monoTypeface - } else { crypto_otp_show.visibility = View.GONE crypto_otp_show_label.visibility = View.GONE @@ -353,7 +367,6 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { if (settings.getBoolean("copy_on_decrypt", true)) { copyPasswordToClipBoard() } - } catch (e: Exception) { Log.e(TAG, "An Exception occurred", e) } @@ -370,7 +383,7 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { */ private fun encrypt() { // if HOTP was incremented, we leave fields as is; they have already been set - if(intent.getStringExtra("OPERATION") != "INCREMENT") { + if (intent.getStringExtra("OPERATION") != "INCREMENT") { editName = crypto_password_file_edit.text.toString().trim() editPass = crypto_password_edit.text.toString() editExtra = crypto_extra_edit.text.toString() @@ -400,37 +413,37 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { val path = if (intent.getBooleanExtra("fromDecrypt", false)) fullPath else "$fullPath/$editName.gpg" - api?.executeApiAsync(data, iStream, oStream) { result: Intent? -> when (result?.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { - OpenPgpApi.RESULT_CODE_SUCCESS -> { - try { - // TODO This might fail, we should check that the write is successful - val outputStream = FileUtils.openOutputStream(File(path)) - outputStream.write(oStream.toByteArray()) - outputStream.close() - - val returnIntent = Intent() - returnIntent.putExtra("CREATED_FILE", path) - returnIntent.putExtra("NAME", editName) - returnIntent.putExtra("LONG_NAME", getLongName(fullPath, repoPath, this.editName!!)) - - // if coming from decrypt screen->edit button - if (intent.getBooleanExtra("fromDecrypt", false)) { - returnIntent.putExtra("OPERATION", "EDIT") - returnIntent.putExtra("needCommit", true) + api?.executeApiAsync(data, iStream, oStream) { result: Intent? -> + when (result?.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { + OpenPgpApi.RESULT_CODE_SUCCESS -> { + try { + // TODO This might fail, we should check that the write is successful + val outputStream = FileUtils.openOutputStream(File(path)) + outputStream.write(oStream.toByteArray()) + outputStream.close() + + val returnIntent = Intent() + returnIntent.putExtra("CREATED_FILE", path) + returnIntent.putExtra("NAME", editName) + returnIntent.putExtra("LONG_NAME", getLongName(fullPath, repoPath, this.editName!!)) + + // if coming from decrypt screen->edit button + if (intent.getBooleanExtra("fromDecrypt", false)) { + returnIntent.putExtra("OPERATION", "EDIT") + returnIntent.putExtra("needCommit", true) + } + setResult(RESULT_OK, returnIntent) + finish() + } catch (e: Exception) { + Log.e(TAG, "An Exception occurred", e) } - setResult(RESULT_OK, returnIntent) - finish() - } catch (e: Exception) { - Log.e(TAG, "An Exception occurred", e) } + OpenPgpApi.RESULT_CODE_ERROR -> handleError(result) } - OpenPgpApi.RESULT_CODE_ERROR -> handleError(result) - } } } - /** * Opens EncryptActivity with the information for this file to be edited */ @@ -467,7 +480,7 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { private fun checkAndIncrementHotp() { // we do not want to increment the HOTP counter if the user has edited the entry or has not // generated an HOTP code - if(intent.getStringExtra("OPERATION") != "EDIT" && passwordEntry?.hotpIsIncremented() == true) { + if (intent.getStringExtra("OPERATION") != "EDIT" && passwordEntry?.hotpIsIncremented() == true) { editName = name.trim() editPass = passwordEntry?.password editExtra = passwordEntry?.extraContent @@ -480,13 +493,13 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { } } - private fun calculateHotp(entry : PasswordEntry) { + private fun calculateHotp(entry: PasswordEntry) { copyOtpToClipBoard(Otp.calculateCode(entry.hotpSecret, entry.hotpCounter + 1)) crypto_otp_show.text = Otp.calculateCode(entry.hotpSecret, entry.hotpCounter + 1) crypto_extra_show.text = entry.extraContent } - private fun calculateAndCommitHotp(entry : PasswordEntry) { + private fun calculateAndCommitHotp(entry: PasswordEntry) { calculateHotp(entry) entry.incrementHotp() // we must set the result before encrypt() is called, since in @@ -568,7 +581,7 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { } private inner class HoldToShowPasswordTransformation constructor(button: Button, private val onToggle: Runnable) : - PasswordTransformationMethod(), View.OnTouchListener { + PasswordTransformationMethod(), View.OnTouchListener { private var shown = false init { @@ -634,7 +647,12 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { sendIntent.action = Intent.ACTION_SEND sendIntent.putExtra(Intent.EXTRA_TEXT, passwordEntry?.password) sendIntent.type = "text/plain" - startActivity(Intent.createChooser(sendIntent, resources.getText(R.string.send_plaintext_password_to)))//Always show a picker to give the user a chance to cancel + startActivity( + Intent.createChooser( + sendIntent, + resources.getText(R.string.send_plaintext_password_to) + ) + )//Always show a picker to give the user a chance to cancel } private fun setTimer() { @@ -673,7 +691,7 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { // This signals the DelayShow task to stop and avoids it having // to poll the AsyncTask.isCancelled() excessively. If skipClearing // is true, the cancelled task won't clear the clipboard. - fun cancelAndSignal(skipClearing : Boolean) { + fun cancelAndSignal(skipClearing: Boolean) { skip = skipClearing cancelNotify.open() } @@ -710,7 +728,7 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { while (current < showTime) { // Block for 1s or until cancel is signalled - if(cancelNotify.block(1000)) { + if (cancelNotify.block(1000)) { Log.d("DELAY_SHOW", "Cancelled") return true } @@ -734,14 +752,17 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { val handler = Handler() for (i in 0..18) { val count = i.toString() - handler.postDelayed({ clipboard.primaryClip = ClipData.newPlainText(count, count) }, (i * 500).toLong()) + handler.postDelayed( + { clipboard.primaryClip = ClipData.newPlainText(count, count) }, + (i * 500).toLong() + ) } } } if (crypto_password_show != null) { // clear password; if decrypt changed to encrypt layout via edit button, no need - if(passwordEntry?.hotpIsIncremented() == false) { + if (passwordEntry?.hotpIsIncremented() == false) { setResult(AppCompatActivity.RESULT_CANCELED) } passwordEntry = null @@ -771,7 +792,7 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { * Gets the relative path to the repository */ fun getRelativePath(fullPath: String, repositoryPath: String): String = - fullPath.replace(repositoryPath, "").replace("/+".toRegex(), "/") + fullPath.replace(repositoryPath, "").replace("/+".toRegex(), "/") /** * Gets the Parent path, relative to the repository diff --git a/app/src/main/java/com/zeapo/pwdstore/git/CloneOperation.java b/app/src/main/java/com/zeapo/pwdstore/git/CloneOperation.java index c3f22a64..e3de3f05 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/CloneOperation.java +++ b/app/src/main/java/com/zeapo/pwdstore/git/CloneOperation.java @@ -2,10 +2,7 @@ package com.zeapo.pwdstore.git; import android.app.Activity; import android.app.AlertDialog; -import android.content.DialogInterface; - import com.zeapo.pwdstore.R; - import org.eclipse.jgit.api.CloneCommand; import org.eclipse.jgit.api.Git; @@ -79,10 +76,7 @@ public class CloneOperation extends GitOperation { + callingActivity.getResources().getString(R.string.jgit_error_dialog_text) + errorMessage + "\nPlease check the FAQ for possible reasons why this error might occur."). - setPositiveButton(callingActivity.getResources().getString(R.string.dialog_ok), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - } + setPositiveButton(callingActivity.getResources().getString(R.string.dialog_ok), (dialogInterface, i) -> { }).show(); } } diff --git a/app/src/main/java/com/zeapo/pwdstore/git/GitActivity.java b/app/src/main/java/com/zeapo/pwdstore/git/GitActivity.java index b6e9af82..ca451247 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/GitActivity.java +++ b/app/src/main/java/com/zeapo/pwdstore/git/GitActivity.java @@ -2,13 +2,10 @@ package com.zeapo.pwdstore.git; import android.app.Activity; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; @@ -20,11 +17,11 @@ import android.widget.ArrayAdapter; import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; - +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; import com.zeapo.pwdstore.R; import com.zeapo.pwdstore.UserPreference; import com.zeapo.pwdstore.utils.PasswordRepository; - import org.apache.commons.io.FileUtils; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.RebaseCommand; @@ -39,29 +36,24 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; public class GitActivity extends AppCompatActivity { + public static final int REQUEST_PULL = 101; + public static final int REQUEST_PUSH = 102; + public static final int REQUEST_CLONE = 103; + public static final int REQUEST_INIT = 104; + public static final int EDIT_SERVER = 105; + public static final int REQUEST_SYNC = 106; + public static final int REQUEST_CREATE = 107; + public static final int EDIT_GIT_CONFIG = 108; private static final String TAG = "GitAct"; private static final String emailPattern = "^[^@]+@[^@]+$"; - private Activity activity; private Context context; - private String protocol; private String connectionMode; - private File localDir; private String hostname; - private SharedPreferences settings; - public static final int REQUEST_PULL = 101; - public static final int REQUEST_PUSH = 102; - public static final int REQUEST_CLONE = 103; - public static final int REQUEST_INIT = 104; - public static final int EDIT_SERVER = 105; - public static final int REQUEST_SYNC = 106; - public static final int REQUEST_CREATE = 107; - public static final int EDIT_GIT_CONFIG = 108; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -83,8 +75,8 @@ public class GitActivity extends AppCompatActivity { setContentView(R.layout.activity_git_clone); setTitle(R.string.title_activity_git_clone); - final Spinner protcol_spinner = (Spinner) findViewById(R.id.clone_protocol); - final Spinner connection_mode_spinner = (Spinner) findViewById(R.id.connection_mode); + final Spinner protcol_spinner = findViewById(R.id.clone_protocol); + final Spinner connection_mode_spinner = findViewById(R.id.connection_mode); // init the spinner for connection modes final ArrayAdapter<CharSequence> connection_mode_adapter = ArrayAdapter.createFromResource(this, @@ -157,11 +149,11 @@ public class GitActivity extends AppCompatActivity { } // init the server information - final EditText server_url = ((EditText) findViewById(R.id.server_url)); - final EditText server_port = ((EditText) findViewById(R.id.server_port)); - final EditText server_path = ((EditText) findViewById(R.id.server_path)); - final EditText server_user = ((EditText) findViewById(R.id.server_user)); - final EditText server_uri = ((EditText) findViewById(R.id.clone_uri)); + final EditText server_url = findViewById(R.id.server_url); + final EditText server_port = findViewById(R.id.server_port); + final EditText server_path = findViewById(R.id.server_path); + final EditText server_user = findViewById(R.id.server_user); + final EditText server_uri = findViewById(R.id.clone_uri); server_url.setText(settings.getString("git_remote_server", "")); server_port.setText(settings.getString("git_remote_port", "")); @@ -282,11 +274,11 @@ public class GitActivity extends AppCompatActivity { * Fills in the server_uri field with the information coming from other fields */ private void updateURI() { - EditText uri = (EditText) findViewById(R.id.clone_uri); - EditText server_url = ((EditText) findViewById(R.id.server_url)); - EditText server_port = ((EditText) findViewById(R.id.server_port)); - EditText server_path = ((EditText) findViewById(R.id.server_path)); - EditText server_user = ((EditText) findViewById(R.id.server_user)); + EditText uri = findViewById(R.id.clone_uri); + EditText server_url = findViewById(R.id.server_url); + EditText server_port = findViewById(R.id.server_port); + EditText server_path = findViewById(R.id.server_path); + EditText server_user = findViewById(R.id.server_user); if (uri != null) { switch (protocol) { @@ -301,7 +293,7 @@ public class GitActivity extends AppCompatActivity { findViewById(R.id.warn_url).setVisibility(View.GONE); } else { - TextView warn_url = (TextView) findViewById(R.id.warn_url); + TextView warn_url = findViewById(R.id.warn_url); if (!server_path.getText().toString().matches("/.*") && !server_port.getText().toString().isEmpty()) { warn_url.setText(R.string.warn_malformed_url_port); warn_url.setVisibility(View.VISIBLE); @@ -342,11 +334,11 @@ public class GitActivity extends AppCompatActivity { * Splits the information in server_uri into the other fields */ private void splitURI() { - EditText server_uri = (EditText) findViewById(R.id.clone_uri); - EditText server_url = ((EditText) findViewById(R.id.server_url)); - EditText server_port = ((EditText) findViewById(R.id.server_port)); - EditText server_path = ((EditText) findViewById(R.id.server_path)); - EditText server_user = ((EditText) findViewById(R.id.server_user)); + EditText server_uri = findViewById(R.id.clone_uri); + EditText server_url = findViewById(R.id.server_url); + EditText server_port = findViewById(R.id.server_port); + EditText server_path = findViewById(R.id.server_path); + EditText server_user = findViewById(R.id.server_user); String uri = server_uri.getText().toString(); Pattern pattern = Pattern.compile("(.+)@([\\w\\d\\.]+):([\\d]+)*(.*)"); @@ -361,7 +353,7 @@ public class GitActivity extends AppCompatActivity { server_port.setText(matcher.group(3)); server_path.setText(matcher.group(4)); - TextView warn_url = (TextView) findViewById(R.id.warn_url); + TextView warn_url = findViewById(R.id.warn_url); if (!server_path.getText().toString().matches("/.*") && !server_port.getText().toString().isEmpty()) { warn_url.setText(R.string.warn_malformed_url_port); warn_url.setVisibility(View.VISIBLE); @@ -467,8 +459,8 @@ public class GitActivity extends AppCompatActivity { private void showGitConfig() { // init the server information - final EditText git_user_name = ((EditText) findViewById(R.id.git_user_name)); - final EditText git_user_email = ((EditText) findViewById(R.id.git_user_email)); + final EditText git_user_name = findViewById(R.id.git_user_name); + final EditText git_user_email = findViewById(R.id.git_user_email); git_user_name.setText(settings.getString("git_config_user_name", "")); git_user_email.setText(settings.getString("git_config_user_email", "")); @@ -476,7 +468,7 @@ public class GitActivity extends AppCompatActivity { // git status Repository repo = PasswordRepository.getRepository(PasswordRepository.getRepositoryDirectory(activity.getApplicationContext())); if (repo != null) { - final TextView git_commit_hash = (TextView) findViewById(R.id.git_commit_hash); + final TextView git_commit_hash = findViewById(R.id.git_commit_hash); try { ObjectId objectId = repo.resolve(Constants.HEAD); Ref ref = repo.getRef("refs/heads/master"); @@ -532,6 +524,7 @@ public class GitActivity extends AppCompatActivity { GitAsyncTask tasks = new GitAsyncTask(activity, false, true, this); tasks.execute(new Git(repo).rebase().setOperation(RebaseCommand.Operation.ABORT)); } + @Override public void onSuccess() { showGitConfig(); @@ -560,36 +553,30 @@ public class GitActivity extends AppCompatActivity { setMessage(getResources().getString(R.string.dialog_delete_msg) + " " + localDir.toString()). setCancelable(false). setPositiveButton(R.string.dialog_delete, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { + (dialog, id) -> { + try { + FileUtils.deleteDirectory(localDir); try { - FileUtils.deleteDirectory(localDir); - try { - new CloneOperation(localDir, activity) - .setCommand(hostname) - .executeAfterAuthentication(connectionMode, settings.getString("git_remote_username", "git"), new File(getFilesDir() + "/.ssh_key")); - } catch (Exception e) { - //This is what happens when jgit fails :( - //TODO Handle the diffent cases of exceptions - e.printStackTrace(); - new AlertDialog.Builder(GitActivity.this).setMessage(e.getMessage()).show(); - } - } catch (IOException e) { - //TODO Handle the exception correctly if we are unable to delete the directory... + new CloneOperation(localDir, activity) + .setCommand(hostname) + .executeAfterAuthentication(connectionMode, settings.getString("git_remote_username", "git"), new File(getFilesDir() + "/.ssh_key")); + } catch (Exception e) { + //This is what happens when jgit fails :( + //TODO Handle the diffent cases of exceptions e.printStackTrace(); new AlertDialog.Builder(GitActivity.this).setMessage(e.getMessage()).show(); } - - dialog.cancel(); + } catch (IOException e) { + //TODO Handle the exception correctly if we are unable to delete the directory... + e.printStackTrace(); + new AlertDialog.Builder(GitActivity.this).setMessage(e.getMessage()).show(); } + + dialog.cancel(); } ). setNegativeButton(R.string.dialog_do_not_delete, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - } + (dialog, id) -> dialog.cancel() ). show(); } else { @@ -626,20 +613,14 @@ public class GitActivity extends AppCompatActivity { settings.getString("git_remote_location", "").isEmpty()) new AlertDialog.Builder(this) .setMessage(activity.getResources().getString(R.string.set_information_dialog_text)) - .setPositiveButton(activity.getResources().getString(R.string.dialog_positive), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - Intent intent = new Intent(activity, UserPreference.class); - startActivityForResult(intent, REQUEST_PULL); - } + .setPositiveButton(activity.getResources().getString(R.string.dialog_positive), (dialogInterface, i) -> { + Intent intent = new Intent(activity, UserPreference.class); + startActivityForResult(intent, REQUEST_PULL); }) - .setNegativeButton(activity.getResources().getString(R.string.dialog_negative), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - // do nothing :( - setResult(RESULT_OK); - finish(); - } + .setNegativeButton(activity.getResources().getString(R.string.dialog_negative), (dialogInterface, i) -> { + // do nothing :( + setResult(RESULT_OK); + finish(); }) .show(); diff --git a/app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.java b/app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.java index 7fe31979..79234a32 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.java +++ b/app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.java @@ -4,10 +4,8 @@ import android.app.Activity; import android.app.ProgressDialog; import android.os.AsyncTask; import android.util.Log; - import com.zeapo.pwdstore.PasswordStore; import com.zeapo.pwdstore.R; - import org.eclipse.jgit.api.CommitCommand; import org.eclipse.jgit.api.GitCommand; import org.eclipse.jgit.api.PushCommand; @@ -52,7 +50,7 @@ public class GitAsyncTask extends AsyncTask<GitCommand, Integer, String> { // the previous status will eventually be used to avoid a commit if (nbChanges == null || nbChanges > 0) command.call(); - }else if (command instanceof PushCommand) { + } else if (command instanceof PushCommand) { for (final PushResult result : ((PushCommand) command).call()) { // Code imported (modified) from Gerrit PushOp, license Apache v2 for (final RemoteRefUpdate rru : result.getRemoteUpdates()) { diff --git a/app/src/main/java/com/zeapo/pwdstore/git/GitOperation.java b/app/src/main/java/com/zeapo/pwdstore/git/GitOperation.java index a0da14ab..7f6ecd81 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/GitOperation.java +++ b/app/src/main/java/com/zeapo/pwdstore/git/GitOperation.java @@ -2,19 +2,17 @@ package com.zeapo.pwdstore.git; import android.annotation.SuppressLint; import android.app.Activity; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.preference.PreferenceManager; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; import android.text.InputType; import android.view.LayoutInflater; import android.view.View; import android.widget.CheckBox; import android.widget.EditText; import android.widget.LinearLayout; - +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.KeyPair; @@ -23,7 +21,6 @@ import com.zeapo.pwdstore.UserPreference; import com.zeapo.pwdstore.git.config.GitConfigSessionFactory; import com.zeapo.pwdstore.git.config.SshConfigSessionFactory; import com.zeapo.pwdstore.utils.PasswordRepository; - import org.eclipse.jgit.api.GitCommand; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.transport.JschConfigSessionFactory; @@ -109,46 +106,37 @@ public abstract class GitOperation { new AlertDialog.Builder(callingActivity) .setMessage(callingActivity.getResources().getString(R.string.ssh_preferences_dialog_text)) .setTitle(callingActivity.getResources().getString(R.string.ssh_preferences_dialog_title)) - .setPositiveButton(callingActivity.getResources().getString(R.string.ssh_preferences_dialog_import), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - try { - // Ask the UserPreference to provide us with the ssh-key - // onResult has to be handled by the callingActivity - Intent intent = new Intent(callingActivity.getApplicationContext(), UserPreference.class); - intent.putExtra("operation", "get_ssh_key"); - callingActivity.startActivityForResult(intent, GET_SSH_KEY_FROM_CLONE); - } catch (Exception e) { - System.out.println("Exception caught :("); - e.printStackTrace(); - } + .setPositiveButton(callingActivity.getResources().getString(R.string.ssh_preferences_dialog_import), (dialog, id) -> { + try { + // Ask the UserPreference to provide us with the ssh-key + // onResult has to be handled by the callingActivity + Intent intent = new Intent(callingActivity.getApplicationContext(), UserPreference.class); + intent.putExtra("operation", "get_ssh_key"); + callingActivity.startActivityForResult(intent, GET_SSH_KEY_FROM_CLONE); + } catch (Exception e) { + System.out.println("Exception caught :("); + e.printStackTrace(); } }) - .setNegativeButton(callingActivity.getResources().getString(R.string.ssh_preferences_dialog_generate), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - try { - // Duplicated code - Intent intent = new Intent(callingActivity.getApplicationContext(), UserPreference.class); - intent.putExtra("operation", "make_ssh_key"); - callingActivity.startActivityForResult(intent, GET_SSH_KEY_FROM_CLONE); - } catch (Exception e) { - System.out.println("Exception caught :("); - e.printStackTrace(); - } + .setNegativeButton(callingActivity.getResources().getString(R.string.ssh_preferences_dialog_generate), (dialog, which) -> { + try { + // Duplicated code + Intent intent = new Intent(callingActivity.getApplicationContext(), UserPreference.class); + intent.putExtra("operation", "make_ssh_key"); + callingActivity.startActivityForResult(intent, GET_SSH_KEY_FROM_CLONE); + } catch (Exception e) { + System.out.println("Exception caught :("); + e.printStackTrace(); } }) - .setNeutralButton(callingActivity.getResources().getString(R.string.dialog_cancel), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - // Finish the blank GitActivity so user doesn't have to press back - callingActivity.finish(); - } + .setNeutralButton(callingActivity.getResources().getString(R.string.dialog_cancel), (dialog, id) -> { + // Finish the blank GitActivity so user doesn't have to press back + callingActivity.finish(); }).show(); } else { LayoutInflater layoutInflater = LayoutInflater.from(callingActivity.getApplicationContext()); @SuppressLint("InflateParams") final View dialogView = layoutInflater.inflate(R.layout.git_passphrase_layout, null); - final EditText passphrase = (EditText) dialogView.findViewById(R.id.sshkey_passphrase); + final EditText passphrase = dialogView.findViewById(R.id.sshkey_passphrase); final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(callingActivity.getApplicationContext()); final String sshKeyPassphrase = settings.getString("ssh_key_passphrase", null); if (showError) { @@ -172,25 +160,21 @@ public abstract class GitOperation { .setTitle(callingActivity.getResources().getString(R.string.passphrase_dialog_title)) .setMessage(callingActivity.getResources().getString(R.string.passphrase_dialog_text)) .setView(dialogView) - .setPositiveButton(callingActivity.getResources().getString(R.string.dialog_ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - if (keyPair.decrypt(passphrase.getText().toString())) { - boolean rememberPassphrase = ((CheckBox) dialogView.findViewById(R.id.sshkey_remember_passphrase)).isChecked(); - if (rememberPassphrase) { - settings.edit().putString("ssh_key_passphrase", passphrase.getText().toString()).apply(); - } - // Authenticate using the ssh-key and then execute the command - setAuthentication(sshKey, username, passphrase.getText().toString()).execute(); - } else { - settings.edit().putString("ssh_key_passphrase", null).apply(); - // call back the method - executeAfterAuthentication(connectionMode, username, sshKey, true); + .setPositiveButton(callingActivity.getResources().getString(R.string.dialog_ok), (dialog, whichButton) -> { + if (keyPair.decrypt(passphrase.getText().toString())) { + boolean rememberPassphrase = ((CheckBox) dialogView.findViewById(R.id.sshkey_remember_passphrase)).isChecked(); + if (rememberPassphrase) { + settings.edit().putString("ssh_key_passphrase", passphrase.getText().toString()).apply(); } + // Authenticate using the ssh-key and then execute the command + setAuthentication(sshKey, username, passphrase.getText().toString()).execute(); + } else { + settings.edit().putString("ssh_key_passphrase", null).apply(); + // call back the method + executeAfterAuthentication(connectionMode, username, sshKey, true); } - }).setNegativeButton(callingActivity.getResources().getString(R.string.dialog_cancel), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - // Do nothing. - } + }).setNegativeButton(callingActivity.getResources().getString(R.string.dialog_cancel), (dialog, whichButton) -> { + // Do nothing. }).show(); } } else { @@ -200,11 +184,8 @@ public abstract class GitOperation { new AlertDialog.Builder(callingActivity) .setTitle("Unable to open the ssh-key") .setMessage("Please check that it was imported.") - .setPositiveButton("Ok", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { + .setPositiveButton("Ok", (dialogInterface, i) -> { - } }).show(); } } @@ -218,16 +199,12 @@ public abstract class GitOperation { .setTitle(callingActivity.getResources().getString(R.string.passphrase_dialog_title)) .setMessage(callingActivity.getResources().getString(R.string.password_dialog_text)) .setView(password) - .setPositiveButton(callingActivity.getResources().getString(R.string.dialog_ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - // authenticate using the user/pwd and then execute the command - setAuthentication(username, password.getText().toString()).execute(); + .setPositiveButton(callingActivity.getResources().getString(R.string.dialog_ok), (dialog, whichButton) -> { + // authenticate using the user/pwd and then execute the command + setAuthentication(username, password.getText().toString()).execute(); - } - }).setNegativeButton(callingActivity.getResources().getString(R.string.dialog_cancel), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - // Do nothing. - } + }).setNegativeButton(callingActivity.getResources().getString(R.string.dialog_cancel), (dialog, whichButton) -> { + // Do nothing. }).show(); } } @@ -239,12 +216,9 @@ public abstract class GitOperation { new AlertDialog.Builder(callingActivity). setTitle(callingActivity.getResources().getString(R.string.jgit_error_dialog_title)). setMessage(callingActivity.getResources().getString(R.string.jgit_error_dialog_text) + errorMessage). - setPositiveButton(callingActivity.getResources().getString(R.string.dialog_ok), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - callingActivity.setResult(Activity.RESULT_CANCELED); - callingActivity.finish(); - } + setPositiveButton(callingActivity.getResources().getString(R.string.dialog_ok), (dialogInterface, i) -> { + callingActivity.setResult(Activity.RESULT_CANCELED); + callingActivity.finish(); }).show(); } diff --git a/app/src/main/java/com/zeapo/pwdstore/git/PullOperation.java b/app/src/main/java/com/zeapo/pwdstore/git/PullOperation.java index 1a903aaf..322a6206 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/PullOperation.java +++ b/app/src/main/java/com/zeapo/pwdstore/git/PullOperation.java @@ -2,13 +2,9 @@ package com.zeapo.pwdstore.git; import android.app.Activity; import android.app.AlertDialog; -import android.content.DialogInterface; - import com.zeapo.pwdstore.R; - import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.PullCommand; -import org.eclipse.jgit.merge.MergeStrategy; import java.io.File; @@ -26,6 +22,7 @@ public class PullOperation extends GitOperation { /** * Sets the command + * * @return the current object */ public PullOperation setCommand() { @@ -52,11 +49,6 @@ public class PullOperation extends GitOperation { + callingActivity.getResources().getString(R.string.jgit_error_dialog_text) + errorMessage + "\nPlease check the FAQ for possible reasons why this error might occur."). - setPositiveButton(callingActivity.getResources().getString(R.string.dialog_ok), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - callingActivity.finish(); - } - }).show(); + setPositiveButton(callingActivity.getResources().getString(R.string.dialog_ok), (dialogInterface, i) -> callingActivity.finish()).show(); } } diff --git a/app/src/main/java/com/zeapo/pwdstore/git/PushOperation.java b/app/src/main/java/com/zeapo/pwdstore/git/PushOperation.java index 06eb6bb3..056b7034 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/PushOperation.java +++ b/app/src/main/java/com/zeapo/pwdstore/git/PushOperation.java @@ -2,10 +2,7 @@ package com.zeapo.pwdstore.git; import android.app.Activity; import android.app.AlertDialog; -import android.content.DialogInterface; - import com.zeapo.pwdstore.R; - import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.PushCommand; @@ -25,6 +22,7 @@ public class PushOperation extends GitOperation { /** * Sets the command + * * @return the current object */ public PushOperation setCommand() { @@ -49,11 +47,6 @@ public class PushOperation extends GitOperation { new AlertDialog.Builder(callingActivity). setTitle(callingActivity.getResources().getString(R.string.jgit_error_dialog_title)). setMessage(callingActivity.getString(R.string.jgit_error_push_dialog_text) + errorMessage). - setPositiveButton(callingActivity.getResources().getString(R.string.dialog_ok), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - callingActivity.finish(); - } - }).show(); + setPositiveButton(callingActivity.getResources().getString(R.string.dialog_ok), (dialogInterface, i) -> callingActivity.finish()).show(); } } diff --git a/app/src/main/java/com/zeapo/pwdstore/git/SyncOperation.java b/app/src/main/java/com/zeapo/pwdstore/git/SyncOperation.java index 9dd43cb9..4e7f037d 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/SyncOperation.java +++ b/app/src/main/java/com/zeapo/pwdstore/git/SyncOperation.java @@ -2,10 +2,7 @@ package com.zeapo.pwdstore.git; import android.app.Activity; import android.app.AlertDialog; -import android.content.DialogInterface; - import com.zeapo.pwdstore.R; - import org.eclipse.jgit.api.AddCommand; import org.eclipse.jgit.api.CommitCommand; import org.eclipse.jgit.api.Git; @@ -64,11 +61,6 @@ public class SyncOperation extends GitOperation { + callingActivity.getResources().getString(R.string.jgit_error_dialog_text) + errorMessage + "\nPlease check the FAQ for possible reasons why this error might occur."). - setPositiveButton(callingActivity.getResources().getString(R.string.dialog_ok), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - callingActivity.finish(); - } - }).show(); + setPositiveButton(callingActivity.getResources().getString(R.string.dialog_ok), (dialogInterface, i) -> callingActivity.finish()).show(); } } diff --git a/app/src/main/java/com/zeapo/pwdstore/git/config/GitConfigSessionFactory.java b/app/src/main/java/com/zeapo/pwdstore/git/config/GitConfigSessionFactory.java index ab23361e..58762888 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/config/GitConfigSessionFactory.java +++ b/app/src/main/java/com/zeapo/pwdstore/git/config/GitConfigSessionFactory.java @@ -3,7 +3,6 @@ package com.zeapo.pwdstore.git.config; import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; - import org.eclipse.jgit.transport.JschConfigSessionFactory; import org.eclipse.jgit.transport.OpenSshConfig; import org.eclipse.jgit.util.FS; @@ -15,8 +14,7 @@ public class GitConfigSessionFactory extends JschConfigSessionFactory { } @Override - protected JSch - getJSch(final OpenSshConfig.Host hc, FS fs) throws JSchException { + protected JSch getJSch(final OpenSshConfig.Host hc, FS fs) throws JSchException { JSch jsch = super.getJSch(hc, fs); jsch.removeAllIdentity(); return jsch; diff --git a/app/src/main/java/com/zeapo/pwdstore/git/config/SshConfigSessionFactory.java b/app/src/main/java/com/zeapo/pwdstore/git/config/SshConfigSessionFactory.java index b958546e..38c5fb7e 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/config/SshConfigSessionFactory.java +++ b/app/src/main/java/com/zeapo/pwdstore/git/config/SshConfigSessionFactory.java @@ -4,7 +4,6 @@ import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; import com.jcraft.jsch.UserInfo; - import org.eclipse.jgit.errors.UnsupportedCredentialItem; import org.eclipse.jgit.transport.CredentialItem; import org.eclipse.jgit.transport.CredentialsProvider; diff --git a/app/src/main/java/com/zeapo/pwdstore/pwgen/PRNGFixes.kt b/app/src/main/java/com/zeapo/pwdstore/pwgen/PRNGFixes.kt deleted file mode 100644 index 98c3735f..00000000 --- a/app/src/main/java/com/zeapo/pwdstore/pwgen/PRNGFixes.kt +++ /dev/null @@ -1,310 +0,0 @@ -package com.zeapo.pwdstore.pwgen - -/* - * This software is provided 'as-is', without any express or implied - * warranty. In no event will Google be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, as long as the origin is not misrepresented. - */ - -import android.os.Build -import android.os.Process -import android.util.Log - -import java.io.ByteArrayOutputStream -import java.io.DataInputStream -import java.io.DataOutputStream -import java.io.File -import java.io.FileInputStream -import java.io.FileOutputStream -import java.io.IOException -import java.io.OutputStream -import java.io.UnsupportedEncodingException -import java.security.NoSuchAlgorithmException -import java.security.Provider -import java.security.SecureRandom -import java.security.SecureRandomSpi -import java.security.Security - -/** - * Fixes for the output of the default PRNG having low entropy. - * - * The fixes need to be applied via [.apply] before any use of Java - * Cryptography Architecture primitives. A good place to invoke them is in the - * application's `onCreate`. - */ -object PRNGFixes { - - private const val VERSION_CODE_JELLY_BEAN_MR2 = 18 - private val BUILD_FINGERPRINT_AND_DEVICE_SERIAL = buildFingerprintAndDeviceSerial - - private val buildFingerprintAndDeviceSerial: ByteArray - get() { - val result = StringBuilder() - val fingerprint = Build.FINGERPRINT - if (fingerprint != null) { - result.append(fingerprint) - } - // TODO: Build#SERIAL is deprecated and should be removed or replaced - val serial = Build.SERIAL - if (serial != null) { - result.append(serial) - } - try { - return result.toString().toByteArray(charset("UTF-8")) - } catch (e: UnsupportedEncodingException) { - throw RuntimeException("UTF-8 encoding not supported") - } - } - - /** - * Applies all fixes. - * - * @throws SecurityException if a fix is needed but could not be applied. - */ - fun apply() { - applyOpenSSLFix() - installLinuxPRNGSecureRandom() - } - - /** - * Applies the fix for OpenSSL PRNG having low entropy. Does nothing if the - * fix is not needed. - * - * @throws SecurityException if the fix is needed but could not be applied. - */ - @Throws(SecurityException::class) - private fun applyOpenSSLFix() { - if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) { - // No need to apply the fix - return - } - - try { - // Mix in the device- and invocation-specific seed. - Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto") - .getMethod("RAND_seed", ByteArray::class.java) - .invoke(null, generateSeed()) - - // Mix output of Linux PRNG into OpenSSL's PRNG - val bytesRead = Class.forName( - "org.apache.harmony.xnet.provider.jsse.NativeCrypto" - ) - .getMethod("RAND_load_file", String::class.java, Long::class.javaPrimitiveType) - .invoke(null, "/dev/urandom", 1024) as Int - if (bytesRead != 1024) { - throw IOException( - "Unexpected number of bytes read from Linux PRNG: $bytesRead" - ) - } - } catch (e: Exception) { - throw SecurityException("Failed to seed OpenSSL PRNG", e) - } - } - - /** - * Installs a Linux PRNG-backed `SecureRandom` implementation as the - * default. Does nothing if the implementation is already the default or if - * there is not need to install the implementation. - * - * @throws SecurityException if the fix is needed but could not be applied. - */ - @Throws(SecurityException::class) - private fun installLinuxPRNGSecureRandom() { - if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) { - // No need to apply the fix - return - } - - // Install a Linux PRNG-based SecureRandom implementation as the - // default, if not yet installed. - val secureRandomProviders = Security.getProviders("SecureRandom.SHA1PRNG") - if (secureRandomProviders == null - || secureRandomProviders.isEmpty() - || LinuxPRNGSecureRandomProvider::class.java != secureRandomProviders[0].javaClass - ) { - Security.insertProviderAt(LinuxPRNGSecureRandomProvider(), 1) - } - - // Assert that new SecureRandom() and - // SecureRandom.getInstance("SHA1PRNG") return a SecureRandom backed - // by the Linux PRNG-based SecureRandom implementation. - val rng1 = SecureRandom() - if (LinuxPRNGSecureRandomProvider::class.java != rng1.provider.javaClass) { - throw SecurityException( - "new SecureRandom() backed by wrong Provider: " + rng1.provider.javaClass - ) - } - - val rng2: SecureRandom - try { - rng2 = SecureRandom.getInstance("SHA1PRNG") - } catch (e: NoSuchAlgorithmException) { - throw SecurityException("SHA1PRNG not available", e) - } - - if (LinuxPRNGSecureRandomProvider::class.java != rng2.provider.javaClass) { - throw SecurityException( - "SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong" - + " Provider: " + rng2.provider.javaClass - ) - } - } - - /** - * `Provider` of `SecureRandom` engines which pass through - * all requests to the Linux PRNG. - */ - private class LinuxPRNGSecureRandomProvider : - Provider("LinuxPRNG", 1.0, "A Linux-specific random number provider that uses" + " /dev/urandom") { - init { - // Although /dev/urandom is not a SHA-1 PRNG, some apps - // explicitly request a SHA1PRNG SecureRandom and we thus need to - // prevent them from getting the default implementation whose output - // may have low entropy. - put("SecureRandom.SHA1PRNG", LinuxPRNGSecureRandom::class.java.name) - put("SecureRandom.SHA1PRNG ImplementedIn", "Software") - } - } - - /** - * [SecureRandomSpi] which passes all requests to the Linux PRNG - * (`/dev/urandom`). - */ - class LinuxPRNGSecureRandom : SecureRandomSpi() { - - /** - * Whether this engine instance has been seeded. This is needed because - * each instance needs to seed itself if the client does not explicitly - * seed it. - */ - private var mSeeded: Boolean = false - - private// NOTE: Consider inserting a BufferedInputStream between - // DataInputStream and FileInputStream if you need higher - // PRNG output performance and can live with future PRNG - // output being pulled into this process prematurely. - val urandomInputStream: DataInputStream - get() = synchronized(sLock) { - if (sUrandomIn == null) { - try { - sUrandomIn = DataInputStream( - FileInputStream(URANDOM_FILE) - ) - } catch (e: IOException) { - throw SecurityException( - "Failed to open " - + URANDOM_FILE + " for reading", e - ) - } - } - return sUrandomIn as DataInputStream - } - - private val urandomOutputStream: OutputStream - @Throws(IOException::class) - get() = synchronized(sLock) { - if (sUrandomOut == null) { - sUrandomOut = FileOutputStream(URANDOM_FILE) - } - return sUrandomOut as OutputStream - } - - @Synchronized - override fun engineSetSeed(bytes: ByteArray) { - try { - val out: OutputStream = urandomOutputStream - out.write(bytes) - out.flush() - } catch (e: IOException) { - // On a small fraction of devices /dev/urandom is not writable. - // Log and ignore. - Log.w( - PRNGFixes::class.java.simpleName, - "Failed to mix seed into $URANDOM_FILE" - ) - } finally { - mSeeded = true - } - } - - @Synchronized - override fun engineNextBytes(bytes: ByteArray) { - if (!mSeeded) { - // Mix in the device- and invocation-specific seed. - engineSetSeed(generateSeed()) - } - - try { - val `in`: DataInputStream = urandomInputStream - synchronized(`in`) { - `in`.readFully(bytes) - } - } catch (e: IOException) { - throw SecurityException( - "Failed to read from $URANDOM_FILE", e - ) - } - } - - override fun engineGenerateSeed(size: Int): ByteArray { - val seed = ByteArray(size) - engineNextBytes(seed) - return seed - } - - companion object { - - /* - * IMPLEMENTATION NOTE: Requests to generate bytes and to mix in a seed - * are passed through to the Linux PRNG (/dev/urandom). Instances of - * this class seed themselves by mixing in the current time, PID, UID, - * build fingerprint, and hardware serial number (where available) into - * Linux PRNG. - * - * Concurrency: Read requests to the underlying Linux PRNG are - * serialized (on sLock) to ensure that multiple threads do not get - * duplicated PRNG output. - */ - - private val URANDOM_FILE = File("/dev/urandom") - - private val sLock = Any() - - /** - * Input stream for reading from Linux PRNG or `null` if not yet - * opened. - */ - private var sUrandomIn: DataInputStream? = null - - /** - * Output stream for writing to Linux PRNG or `null` if not yet - * opened. - */ - private var sUrandomOut: OutputStream? = null - } - } - - /** - * Generates a device- and invocation-specific seed to be mixed into the - * Linux PRNG. - */ - private fun generateSeed(): ByteArray { - try { - val seedBuffer = ByteArrayOutputStream() - val seedBufferOut = DataOutputStream(seedBuffer) - seedBufferOut.writeLong(System.currentTimeMillis()) - seedBufferOut.writeLong(System.nanoTime()) - seedBufferOut.writeInt(Process.myPid()) - seedBufferOut.writeInt(Process.myUid()) - seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL) - seedBufferOut.close() - return seedBuffer.toByteArray() - } catch (e: IOException) { - throw SecurityException("Failed to generate seed", e) - } - } -} diff --git a/app/src/main/java/com/zeapo/pwdstore/pwgen/PasswordGenerator.kt b/app/src/main/java/com/zeapo/pwdstore/pwgen/PasswordGenerator.kt index 0374236c..3a7dcc27 100644 --- a/app/src/main/java/com/zeapo/pwdstore/pwgen/PasswordGenerator.kt +++ b/app/src/main/java/com/zeapo/pwdstore/pwgen/PasswordGenerator.kt @@ -1,26 +1,25 @@ package com.zeapo.pwdstore.pwgen import android.content.Context -import android.content.SharedPreferences import java.util.ArrayList object PasswordGenerator { - internal val DIGITS = 0x0001 - internal val UPPERS = 0x0002 - internal val SYMBOLS = 0x0004 - internal val AMBIGUOUS = 0x0008 - internal val NO_VOWELS = 0x0010 + internal const val DIGITS = 0x0001 + internal const val UPPERS = 0x0002 + internal const val SYMBOLS = 0x0004 + internal const val AMBIGUOUS = 0x0008 + internal const val NO_VOWELS = 0x0010 - internal val DIGITS_STR = "0123456789" - internal val UPPERS_STR = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - internal val LOWERS_STR = "abcdefghijklmnopqrstuvwxyz" - internal val SYMBOLS_STR = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" - internal val AMBIGUOUS_STR = "B8G6I1l0OQDS5Z2" - internal val VOWELS_STR = "01aeiouyAEIOUY" + internal const val DIGITS_STR = "0123456789" + internal const val UPPERS_STR = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + internal const val LOWERS_STR = "abcdefghijklmnopqrstuvwxyz" + internal const val SYMBOLS_STR = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" + internal const val AMBIGUOUS_STR = "B8G6I1l0OQDS5Z2" + internal const val VOWELS_STR = "01aeiouyAEIOUY" // No a, c, n, h, H, C, 1, N - private val pwOptions = "0ABsvy" + private const val pwOptions = "0ABsvy" /** * Sets password generation preferences. diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/EntryRecyclerAdapter.java b/app/src/main/java/com/zeapo/pwdstore/utils/EntryRecyclerAdapter.java index cabc0dcf..c108cb52 100644 --- a/app/src/main/java/com/zeapo/pwdstore/utils/EntryRecyclerAdapter.java +++ b/app/src/main/java/com/zeapo/pwdstore/utils/EntryRecyclerAdapter.java @@ -3,15 +3,14 @@ package com.zeapo.pwdstore.utils; import android.annotation.SuppressLint; import android.app.Activity; import android.graphics.Color; -import androidx.annotation.NonNull; -import androidx.core.content.ContextCompat; -import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; - +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; +import androidx.recyclerview.widget.RecyclerView; import com.zeapo.pwdstore.R; import java.util.ArrayList; @@ -19,9 +18,9 @@ import java.util.Set; import java.util.TreeSet; public abstract class EntryRecyclerAdapter extends RecyclerView.Adapter<EntryRecyclerAdapter.ViewHolder> { + final Set<Integer> selectedItems = new TreeSet<>(); private final Activity activity; private final ArrayList<PasswordItem> values; - final Set<Integer> selectedItems = new TreeSet<>(); EntryRecyclerAdapter(Activity activity, ArrayList<PasswordItem> values) { this.activity = activity; @@ -85,18 +84,13 @@ public abstract class EntryRecyclerAdapter extends RecyclerView.Adapter<EntryRec @NonNull View.OnLongClickListener getOnLongClickListener(ViewHolder holder, PasswordItem pass) { - return new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - return false; - } - }; + return v -> false; } // Replace the contents of a view (invoked by the layout manager) @SuppressLint("SetTextI18n") @Override - public void onBindViewHolder(final ViewHolder holder, int position) { + public void onBindViewHolder(@NonNull final ViewHolder holder, int position) { final PasswordItem pass = getValues().get(position); holder.name.setText(pass.toString()); if (pass.getType() == PasswordItem.TYPE_CATEGORY) { @@ -127,6 +121,17 @@ public abstract class EntryRecyclerAdapter extends RecyclerView.Adapter<EntryRec @NonNull protected abstract View.OnClickListener getOnClickListener(ViewHolder holder, PasswordItem pass); + // Create new views (invoked by the layout manager) + @Override + @NonNull + public PasswordRecyclerAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, + int viewType) { + // create a new view + View v = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.password_row_layout, parent, false); + return new 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 @@ -140,19 +145,9 @@ public abstract class EntryRecyclerAdapter extends RecyclerView.Adapter<EntryRec ViewHolder(View v) { super(v); view = v; - name = (TextView) view.findViewById(R.id.label); - type = (TextView) view.findViewById(R.id.type); - typeImage = (ImageView) view.findViewById(R.id.type_image); + name = view.findViewById(R.id.label); + type = view.findViewById(R.id.type); + typeImage = view.findViewById(R.id.type_image); } } - - // Create new views (invoked by the layout manager) - @Override - public PasswordRecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, - int viewType) { - // create a new view - View v = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.password_row_layout, parent, false); - return new ViewHolder(v); - } } diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/FolderRecyclerAdapter.java b/app/src/main/java/com/zeapo/pwdstore/utils/FolderRecyclerAdapter.java index d155802f..f6a29113 100644 --- a/app/src/main/java/com/zeapo/pwdstore/utils/FolderRecyclerAdapter.java +++ b/app/src/main/java/com/zeapo/pwdstore/utils/FolderRecyclerAdapter.java @@ -1,8 +1,7 @@ package com.zeapo.pwdstore.utils; -import androidx.annotation.NonNull; import android.view.View; - +import androidx.annotation.NonNull; import com.zeapo.pwdstore.SelectFolderActivity; import com.zeapo.pwdstore.SelectFolderFragment; @@ -19,12 +18,9 @@ public class FolderRecyclerAdapter extends EntryRecyclerAdapter { @NonNull protected View.OnClickListener getOnClickListener(final ViewHolder holder, final PasswordItem pass) { - return new View.OnClickListener() { - @Override - public void onClick(View v) { - listener.onFragmentInteraction(pass); - notifyItemChanged(holder.getAdapterPosition()); - } + return v -> { + listener.onFragmentInteraction(pass); + notifyItemChanged(holder.getAdapterPosition()); }; } diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/Otp.java b/app/src/main/java/com/zeapo/pwdstore/utils/Otp.java index 44e2aa64..35e773e4 100644 --- a/app/src/main/java/com/zeapo/pwdstore/utils/Otp.java +++ b/app/src/main/java/com/zeapo/pwdstore/utils/Otp.java @@ -1,18 +1,16 @@ package com.zeapo.pwdstore.utils; import android.util.Log; - import org.apache.commons.codec.binary.Base32; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; 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; - public class Otp { public static final int TIME_WINDOW = 30; @@ -27,7 +25,7 @@ public class Otp { public static String calculateCode(String secret, long counter) { SecretKeySpec signingKey = new SecretKeySpec(BASE_32.decode(secret), ALGORITHM); - Mac mac = null; + Mac mac; try { mac = Mac.getInstance(ALGORITHM); mac.init(signingKey); diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordItem.java b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordItem.java index 5c2231a5..b79f2130 100644 --- a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordItem.java +++ b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordItem.java @@ -1,8 +1,7 @@ package com.zeapo.pwdstore.utils; -import com.zeapo.pwdstore.crypto.PgpActivity; - import androidx.annotation.NonNull; +import com.zeapo.pwdstore.crypto.PgpActivity; import java.io.File; @@ -18,8 +17,9 @@ public class PasswordItem implements Comparable { private final String fullPathToParent; private final String longName; - /** Create a password item - * + /** + * Create a password item + * <p> * Make it protected so that we use a builder */ private PasswordItem(String name, PasswordItem parent, char type, File file, File rootDir) { @@ -33,35 +33,39 @@ public class PasswordItem implements Comparable { longName = PgpActivity.getLongName(fullPathToParent, rootDir.getAbsolutePath(), toString()); } - /** Create a new Category item + /** + * Create a new Category item */ public static PasswordItem newCategory(String name, File file, PasswordItem parent, File rootDir) { return new PasswordItem(name, parent, TYPE_CATEGORY, file, rootDir); } - /** Create a new parentless category item + /** + * Create a new parentless category item */ public static PasswordItem newCategory(String name, File file, File rootDir) { return new PasswordItem(name, null, TYPE_CATEGORY, file, rootDir); } - /** Create a new password item + /** + * Create a new password item */ - public static PasswordItem newPassword(String name, File file, PasswordItem parent, File rootDir) { + public static PasswordItem newPassword(String name, File file, PasswordItem parent, File rootDir) { return new PasswordItem(name, parent, TYPE_PASSWORD, file, rootDir); } - /** Create a new parentless password item + /** + * Create a new parentless password item */ public static PasswordItem newPassword(String name, File file, File rootDir) { return new PasswordItem(name, null, TYPE_PASSWORD, file, rootDir); } - public char getType(){ + public char getType() { return this.type; } - String getName(){ + String getName() { return this.name; } @@ -81,8 +85,9 @@ public class PasswordItem implements Comparable { return longName; } + @NonNull @Override - public String toString(){ + public String toString() { return this.getName().replace(".gpg", ""); } diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRecyclerAdapter.java b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRecyclerAdapter.java index 751d8559..83244a3f 100644 --- a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRecyclerAdapter.java +++ b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRecyclerAdapter.java @@ -1,11 +1,10 @@ package com.zeapo.pwdstore.utils; -import androidx.annotation.NonNull; -import androidx.appcompat.view.ActionMode; import android.view.Menu; import android.view.MenuItem; import android.view.View; - +import androidx.annotation.NonNull; +import androidx.appcompat.view.ActionMode; import com.zeapo.pwdstore.PasswordFragment; import com.zeapo.pwdstore.PasswordStore; import com.zeapo.pwdstore.R; @@ -19,63 +18,6 @@ public class PasswordRecyclerAdapter extends EntryRecyclerAdapter { private final PasswordFragment.OnFragmentInteractionListener listener; public ActionMode mActionMode; private Boolean canEdit; - - // Provide a suitable constructor (depends on the kind of dataset) - public PasswordRecyclerAdapter(PasswordStore activity, PasswordFragment.OnFragmentInteractionListener listener, ArrayList<PasswordItem> values) { - super(activity, values); - this.activity = activity; - this.listener = listener; - } - - @Override - @NonNull - protected View.OnLongClickListener getOnLongClickListener(final ViewHolder holder, final PasswordItem pass) { - return new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (mActionMode != null) { - return false; - } - toggleSelection(holder.getAdapterPosition()); - canEdit = pass.getType() == PasswordItem.TYPE_PASSWORD; - // Start the CAB using the ActionMode.Callback - mActionMode = activity.startSupportActionMode(mActionModeCallback); - mActionMode.setTitle("" + selectedItems.size()); - mActionMode.invalidate(); - notifyItemChanged(holder.getAdapterPosition()); - return true; - } - }; - } - - @Override - @NonNull - protected View.OnClickListener getOnClickListener(final ViewHolder holder, final PasswordItem pass) { - return new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mActionMode != null) { - toggleSelection(holder.getAdapterPosition()); - mActionMode.setTitle("" + selectedItems.size()); - if (selectedItems.isEmpty()) { - mActionMode.finish(); - } else if (selectedItems.size() == 1 && !canEdit) { - if (getValues().get(selectedItems.iterator().next()).getType() == PasswordItem.TYPE_PASSWORD) { - canEdit = true; - mActionMode.invalidate(); - } - } else if (selectedItems.size() >= 1 && canEdit) { - canEdit = false; - mActionMode.invalidate(); - } - } else { - listener.onFragmentInteraction(pass); - } - notifyItemChanged(holder.getAdapterPosition()); - } - }; - } - private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { // Called when the action mode is created; startActionMode() was called @@ -136,4 +78,54 @@ public class PasswordRecyclerAdapter extends EntryRecyclerAdapter { activity.findViewById(R.id.fab).setVisibility(View.VISIBLE); } }; + + // Provide a suitable constructor (depends on the kind of dataset) + public PasswordRecyclerAdapter(PasswordStore activity, PasswordFragment.OnFragmentInteractionListener listener, ArrayList<PasswordItem> values) { + super(activity, values); + this.activity = activity; + this.listener = listener; + } + + @Override + @NonNull + protected View.OnLongClickListener getOnLongClickListener(final ViewHolder holder, final PasswordItem pass) { + return v -> { + if (mActionMode != null) { + return false; + } + toggleSelection(holder.getAdapterPosition()); + canEdit = pass.getType() == PasswordItem.TYPE_PASSWORD; + // Start the CAB using the ActionMode.Callback + mActionMode = activity.startSupportActionMode(mActionModeCallback); + mActionMode.setTitle("" + selectedItems.size()); + mActionMode.invalidate(); + notifyItemChanged(holder.getAdapterPosition()); + return true; + }; + } + + @Override + @NonNull + protected View.OnClickListener getOnClickListener(final ViewHolder holder, final PasswordItem pass) { + return v -> { + if (mActionMode != null) { + toggleSelection(holder.getAdapterPosition()); + mActionMode.setTitle("" + selectedItems.size()); + if (selectedItems.isEmpty()) { + mActionMode.finish(); + } else if (selectedItems.size() == 1 && !canEdit) { + if (getValues().get(selectedItems.iterator().next()).getType() == PasswordItem.TYPE_PASSWORD) { + canEdit = true; + mActionMode.invalidate(); + } + } else if (selectedItems.size() >= 1 && canEdit) { + canEdit = false; + mActionMode.invalidate(); + } + } else { + listener.onFragmentInteraction(pass); + } + notifyItemChanged(holder.getAdapterPosition()); + }; + } } diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRepository.java b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRepository.java index fdb53ac4..e79acac8 100644 --- a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRepository.java +++ b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRepository.java @@ -4,7 +4,6 @@ import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.util.Log; - import org.apache.commons.io.filefilter.FileFilterUtils; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.lib.Repository; @@ -269,9 +268,7 @@ public class PasswordRepository { return (p2.getType() + p1.getName()) .compareToIgnoreCase(p1.getType() + p2.getName()); } - }) - - ; + }); private Comparator<PasswordItem> comparator; |