diff options
-rw-r--r-- | app/src/main/java/com/zeapo/pwdstore/PasswordFragment.java | 257 | ||||
-rw-r--r-- | app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt | 206 |
2 files changed, 206 insertions, 257 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.java b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.java deleted file mode 100644 index 7a51996f..00000000 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright © 2014-2019 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ -package com.zeapo.pwdstore; - -import android.content.Context; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.Fragment; -import androidx.preference.PreferenceManager; -import androidx.recyclerview.widget.DividerItemDecoration; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import com.google.android.material.floatingactionbutton.FloatingActionButton; -import com.zeapo.pwdstore.ui.adapters.PasswordRecyclerAdapter; -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>Large screen devices (such as tablets) are supported by replacing the ListView with a - * GridView. - * - * <p> - */ -public class PasswordFragment extends Fragment { - - // store the pass files list in a stack - private Stack<ArrayList<PasswordItem>> passListStack; - private Stack<File> pathStack; - private Stack<Integer> scrollPosition; - private PasswordRecyclerAdapter recyclerAdapter; - 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() {} - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - String path = getArguments().getString("Path"); - - settings = PreferenceManager.getDefaultSharedPreferences(requireActivity()); - passListStack = new Stack<>(); - scrollPosition = new Stack<>(); - pathStack = new Stack<>(); - recyclerAdapter = - new PasswordRecyclerAdapter( - (PasswordStore) requireActivity(), - mListener, - PasswordRepository.getPasswords( - new File(path), - PasswordRepository.getRepositoryDirectory(requireContext()), - getSortOrder())); - } - - @Override - public View onCreateView( - @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.password_recycler_view, container, false); - - // use a linear layout manager - RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(requireContext()); - - recyclerView = view.findViewById(R.id.pass_recycler); - recyclerView.setLayoutManager(mLayoutManager); - - // use divider - recyclerView.addItemDecoration( - new DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL)); - - // Set the adapter - recyclerView.setAdapter(recyclerAdapter); - - final FloatingActionButton fab = view.findViewById(R.id.fab); - fab.setOnClickListener(v -> ((PasswordStore) requireActivity()).createPassword()); - - registerForContextMenu(recyclerView); - return view; - } - - @Override - public void onAttach(final Context context) { - super.onAttach(context); - try { - mListener = - item -> { - if (item.getType() == PasswordItem.TYPE_CATEGORY) { - // push the current password list (non filtered plz!) - passListStack.push( - pathStack.isEmpty() - ? PasswordRepository.getPasswords( - PasswordRepository.getRepositoryDirectory( - context), - getSortOrder()) - : PasswordRepository.getPasswords( - pathStack.peek(), - PasswordRepository.getRepositoryDirectory( - context), - getSortOrder())); - // push the category were we're going - pathStack.push(item.getFile()); - scrollPosition.push(recyclerView.getVerticalScrollbarPosition()); - - recyclerView.scrollToPosition(0); - recyclerAdapter.clear(); - recyclerAdapter.addAll( - PasswordRepository.getPasswords( - item.getFile(), - PasswordRepository.getRepositoryDirectory(context), - getSortOrder())); - - ((AppCompatActivity) requireActivity()) - .getSupportActionBar() - .setDisplayHomeAsUpEnabled(true); - } else { - if (getArguments().getBoolean("matchWith", false)) { - ((PasswordStore) requireActivity()).matchPasswordWithApp(item); - } else { - ((PasswordStore) requireActivity()).decryptPassword(item); - } - } - }; - } catch (ClassCastException e) { - throw new ClassCastException(context + " must implement OnFragmentInteractionListener"); - } - } - - /** clears the adapter content and sets it back to the root view */ - public void updateAdapter() { - passListStack.clear(); - pathStack.clear(); - scrollPosition.clear(); - recyclerAdapter.clear(); - recyclerAdapter.addAll( - PasswordRepository.getPasswords( - PasswordRepository.getRepositoryDirectory(requireContext()), - getSortOrder())); - - ((AppCompatActivity) requireActivity()) - .getSupportActionBar() - .setDisplayHomeAsUpEnabled(false); - } - - /** refreshes the adapter with the latest opened category */ - public void refreshAdapter() { - recyclerAdapter.clear(); - recyclerAdapter.addAll( - pathStack.isEmpty() - ? PasswordRepository.getPasswords( - PasswordRepository.getRepositoryDirectory(requireContext()), - getSortOrder()) - : PasswordRepository.getPasswords( - pathStack.peek(), - PasswordRepository.getRepositoryDirectory(requireContext()), - getSortOrder())); - } - - /** - * filters the list adapter - * - * @param filter the filter to apply - */ - public void filterAdapter(String filter) { - if (filter.isEmpty()) { - refreshAdapter(); - } else { - recursiveFilter(filter, pathStack.isEmpty() ? null : pathStack.peek()); - } - } - - /** - * recursively filters a directory and extract all the matching items - * - * @param filter the filter to apply - * @param dir the directory to filter - */ - private void recursiveFilter(String filter, File dir) { - // on the root the pathStack is empty - ArrayList<PasswordItem> passwordItems = - dir == null - ? PasswordRepository.getPasswords( - PasswordRepository.getRepositoryDirectory(requireContext()), - getSortOrder()) - : PasswordRepository.getPasswords( - dir, - PasswordRepository.getRepositoryDirectory(requireContext()), - getSortOrder()); - - boolean rec = settings.getBoolean("filter_recursively", true); - for (PasswordItem item : passwordItems) { - if (item.getType() == PasswordItem.TYPE_CATEGORY && rec) { - recursiveFilter(filter, item.getFile()); - } - boolean matches = item.toString().toLowerCase().contains(filter.toLowerCase()); - boolean inAdapter = recyclerAdapter.getValues().contains(item); - if (matches && !inAdapter) { - recyclerAdapter.add(item); - } else if (!matches && inAdapter) { - recyclerAdapter.remove(recyclerAdapter.getValues().indexOf(item)); - } - } - } - - /** Goes back one level back in the path */ - public void popBack() { - if (passListStack.isEmpty()) return; - - recyclerView.scrollToPosition(scrollPosition.pop()); - recyclerAdapter.clear(); - recyclerAdapter.addAll(passListStack.pop()); - pathStack.pop(); - } - - /** - * gets the current directory - * - * @return the current directory - */ - public File getCurrentDir() { - if (pathStack.isEmpty()) return PasswordRepository.getRepositoryDirectory(requireContext()); - else return pathStack.peek(); - } - - public boolean isNotEmpty() { - return !passListStack.isEmpty(); - } - - public void dismissActionMode() { - if (recyclerAdapter != null && recyclerAdapter.getActionMode() != null) { - recyclerAdapter.getActionMode().finish(); - } - } - - 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/PasswordFragment.kt b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt new file mode 100644 index 00000000..d41d9286 --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt @@ -0,0 +1,206 @@ +/* + * Copyright © 2014-2019 The Android Password Store Authors. All Rights Reserved. + * SPDX-License-Identifier: GPL-3.0-only + */ +package com.zeapo.pwdstore + +import android.content.Context +import android.content.SharedPreferences +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment +import androidx.preference.PreferenceManager +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.floatingactionbutton.FloatingActionButton +import com.zeapo.pwdstore.ui.adapters.PasswordRecyclerAdapter +import com.zeapo.pwdstore.utils.PasswordItem +import com.zeapo.pwdstore.utils.PasswordRepository +import com.zeapo.pwdstore.utils.PasswordRepository.Companion.getPasswords +import com.zeapo.pwdstore.utils.PasswordRepository.Companion.getRepositoryDirectory +import com.zeapo.pwdstore.utils.PasswordRepository.PasswordSortOrder.Companion.getSortOrder +import java.io.File +import java.util.Locale +import java.util.Stack + +/** + * A fragment representing a list of Items. + * + * + * Large screen devices (such as tablets) are supported by replacing the ListView with a + * GridView. + * + * + * + */ +class PasswordFragment +/** + * Mandatory empty constructor for the fragment manager to instantiate the fragment (e.g. upon + * screen orientation changes). + */ + : Fragment() { + // store the pass files list in a stack + private var passListStack: Stack<ArrayList<PasswordItem>> = Stack() + private var pathStack: Stack<File> = Stack() + private var scrollPosition: Stack<Int> = Stack() + private lateinit var recyclerAdapter: PasswordRecyclerAdapter + private lateinit var recyclerView: RecyclerView + private lateinit var listener: OnFragmentInteractionListener + private lateinit var settings: SharedPreferences + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val path = requireNotNull(requireArguments().getString("Path")) + settings = PreferenceManager.getDefaultSharedPreferences(requireActivity()) + recyclerAdapter = PasswordRecyclerAdapter((requireActivity() as PasswordStore), + listener, getPasswords(File(path), getRepositoryDirectory(requireContext()), sortOrder)) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + val view = inflater.inflate(R.layout.password_recycler_view, container, false) + // use a linear layout manager + val layoutManager = LinearLayoutManager(requireContext()) + recyclerView = view.findViewById(R.id.pass_recycler) + recyclerView.layoutManager = layoutManager + // use divider + recyclerView.addItemDecoration(DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL)) + // Set the adapter + recyclerView.adapter = recyclerAdapter + val fab: FloatingActionButton = view.findViewById(R.id.fab) + fab.setOnClickListener { (requireActivity() as PasswordStore).createPassword() } + registerForContextMenu(recyclerView) + return view + } + + override fun onAttach(context: Context) { + super.onAttach(context) + try { + listener = object : OnFragmentInteractionListener { + override fun onFragmentInteraction(item: PasswordItem) { + if (item.type == PasswordItem.TYPE_CATEGORY) { // push the current password list (non filtered plz!) + passListStack.push( + if (pathStack.isEmpty()) + getPasswords(getRepositoryDirectory(context), sortOrder) + else + getPasswords(pathStack.peek(), getRepositoryDirectory(context), sortOrder) + ) + // push the category were we're going + pathStack.push(item.file) + scrollPosition.push(recyclerView.verticalScrollbarPosition) + recyclerView.scrollToPosition(0) + recyclerAdapter.clear() + recyclerAdapter.addAll(getPasswords(item.file, getRepositoryDirectory(context), sortOrder)) + (requireActivity() as AppCompatActivity).supportActionBar?.setDisplayHomeAsUpEnabled(true) + } else { + if (requireArguments().getBoolean("matchWith", false)) { + (requireActivity() as PasswordStore).matchPasswordWithApp(item) + } else { + (requireActivity() as PasswordStore).decryptPassword(item) + } + } + } + } + } catch (e: ClassCastException) { + throw ClassCastException("$context must implement OnFragmentInteractionListener") + } + } + + /** clears the adapter content and sets it back to the root view */ + fun updateAdapter() { + passListStack.clear() + pathStack.clear() + scrollPosition.clear() + recyclerAdapter.clear() + recyclerAdapter.addAll(getPasswords(getRepositoryDirectory(requireContext()), sortOrder)) + (requireActivity() as AppCompatActivity).supportActionBar?.setDisplayHomeAsUpEnabled(false) + } + + /** refreshes the adapter with the latest opened category */ + fun refreshAdapter() { + recyclerAdapter.clear() + recyclerAdapter.addAll( + if (pathStack.isEmpty()) + getPasswords(getRepositoryDirectory(requireContext()), sortOrder) + else + getPasswords(pathStack.peek(), getRepositoryDirectory(requireContext()), sortOrder) + ) + } + + /** + * filters the list adapter + * + * @param filter the filter to apply + */ + fun filterAdapter(filter: String) { + if (filter.isEmpty()) { + refreshAdapter() + } else { + recursiveFilter(filter, if (pathStack.isEmpty()) null else pathStack.peek()) + } + } + + /** + * recursively filters a directory and extract all the matching items + * + * @param filter the filter to apply + * @param dir the directory to filter + */ + private fun recursiveFilter(filter: String, dir: File?) { // on the root the pathStack is empty + val passwordItems = if (dir == null) + getPasswords(getRepositoryDirectory(requireContext()), sortOrder) + else + getPasswords(dir, getRepositoryDirectory(requireContext()), sortOrder) + val rec = settings.getBoolean("filter_recursively", true) + for (item in passwordItems) { + if (item.type == PasswordItem.TYPE_CATEGORY && rec) { + recursiveFilter(filter, item.file) + } + val matches = item.toString().toLowerCase(Locale.ROOT).contains(filter.toLowerCase(Locale.ROOT)) + val inAdapter = recyclerAdapter.values.contains(item) + if (matches && !inAdapter) { + recyclerAdapter.add(item) + } else if (!matches && inAdapter) { + recyclerAdapter.remove(recyclerAdapter.values.indexOf(item)) + } + } + } + + /** Goes back one level back in the path */ + fun popBack() { + if (passListStack.isEmpty()) return + recyclerView.scrollToPosition(scrollPosition.pop()) + recyclerAdapter.clear() + recyclerAdapter.addAll(passListStack.pop()) + pathStack.pop() + } + + /** + * gets the current directory + * + * @return the current directory + */ + val currentDir: File? + get() = if (pathStack.isEmpty()) getRepositoryDirectory(requireContext()) else pathStack.peek() + + val isNotEmpty: Boolean + get() = !passListStack.isEmpty() + + fun dismissActionMode() { + recyclerAdapter.actionMode?.finish() + } + + private val sortOrder: PasswordRepository.PasswordSortOrder + get() = getSortOrder(settings) + + interface OnFragmentInteractionListener { + fun onFragmentInteraction(item: PasswordItem) + } +} |