Rewrite PasswordFragment in Kotlin
Signed-off-by: Harsh Shandilya <msfjarvis@gmail.com>
This commit is contained in:
parent
14d997f500
commit
8fc7dd3d42
2 changed files with 206 additions and 257 deletions
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
206
app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt
Normal file
206
app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue