diff --git a/app/src/main/java/protect/card_locker/ManageGroupsActivity.java b/app/src/main/java/protect/card_locker/ManageGroupsActivity.java deleted file mode 100644 index 17439b20f..000000000 --- a/app/src/main/java/protect/card_locker/ManageGroupsActivity.java +++ /dev/null @@ -1,247 +0,0 @@ -package protect.card_locker; - -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.database.sqlite.SQLiteDatabase; -import android.os.Bundle; -import android.text.InputType; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.TextView; - -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.widget.Toolbar; -import androidx.recyclerview.widget.DefaultItemAnimator; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import com.google.android.material.floatingactionbutton.FloatingActionButton; - -import java.util.List; - -import protect.card_locker.databinding.ManageGroupsActivityBinding; - -public class ManageGroupsActivity extends CatimaAppCompatActivity implements GroupCursorAdapter.GroupAdapterListener { - private ManageGroupsActivityBinding binding; - private static final String TAG = "Catima"; - - private SQLiteDatabase mDatabase; - private TextView mHelpText; - private RecyclerView mGroupList; - GroupCursorAdapter mAdapter; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - binding = ManageGroupsActivityBinding.inflate(getLayoutInflater()); - setTitle(R.string.groups); - setContentView(binding.getRoot()); - Utils.applyWindowInsets(binding.getRoot()); - Toolbar toolbar = binding.toolbar; - setSupportActionBar(toolbar); - enableToolbarBackButton(); - - mDatabase = new DBHelper(this).getWritableDatabase(); - } - - @Override - protected void onResume() { - super.onResume(); - - FloatingActionButton addButton = binding.fabAdd; - addButton.setOnClickListener(v -> createGroup()); - addButton.bringToFront(); - - mGroupList = binding.include.list; - mHelpText = binding.include.helpText; - - // Init group list - RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext()); - mGroupList.setLayoutManager(mLayoutManager); - mGroupList.setItemAnimator(new DefaultItemAnimator()); - - mAdapter = new GroupCursorAdapter(this, null, this); - mGroupList.setAdapter(mAdapter); - - updateGroupList(); - } - - private void updateGroupList() { - mAdapter.swapCursor(DBHelper.getGroupCursor(mDatabase)); - - if (DBHelper.getGroupCount(mDatabase) == 0) { - mGroupList.setVisibility(View.GONE); - mHelpText.setVisibility(View.VISIBLE); - - return; - } - - mGroupList.setVisibility(View.VISIBLE); - mHelpText.setVisibility(View.GONE); - } - - private void invalidateHomescreenActiveTab() { - SharedPreferences activeTabPref = getApplicationContext().getSharedPreferences( - getString(R.string.sharedpreference_active_tab), - Context.MODE_PRIVATE); - SharedPreferences.Editor activeTabPrefEditor = activeTabPref.edit(); - activeTabPrefEditor.putInt(getString(R.string.sharedpreference_active_tab), 0); - activeTabPrefEditor.apply(); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - int id = item.getItemId(); - - if (id == android.R.id.home) { - finish(); - } - - return super.onOptionsItemSelected(item); - } - - private void createGroup() { - AlertDialog.Builder builder = new MaterialAlertDialogBuilder(this); - - // Header - builder.setTitle(R.string.enter_group_name); - - // Layout - LinearLayout layout = new LinearLayout(this); - layout.setOrientation(LinearLayout.VERTICAL); - LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ); - int contentPadding = getResources().getDimensionPixelSize(R.dimen.alert_dialog_content_padding); - params.leftMargin = contentPadding; - params.topMargin = contentPadding / 2; - params.rightMargin = contentPadding; - - // EditText with spacing - final EditText input = new EditText(this); - input.setInputType(InputType.TYPE_CLASS_TEXT); - input.setLayoutParams(params); - layout.addView(input); - - // Set layout - builder.setView(layout); - - // Buttons - builder.setPositiveButton(getString(R.string.ok), (dialog, which) -> { - DBHelper.insertGroup(mDatabase, input.getText().toString().trim()); - updateGroupList(); - }); - builder.setNegativeButton(getString(R.string.cancel), (dialog, which) -> dialog.cancel()); - AlertDialog dialog = builder.create(); - - // Now that the dialog exists, we can bind something that affects the OK button - input.addTextChangedListener(new SimpleTextWatcher() { - public void onTextChanged(CharSequence s, int start, int before, int count) { - String groupName = s.toString().trim(); - - if (groupName.length() == 0) { - input.setError(getString(R.string.group_name_is_empty)); - dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); - return; - } - - if (DBHelper.getGroup(mDatabase, groupName) != null) { - input.setError(getString(R.string.group_name_already_in_use)); - dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); - return; - } - - input.setError(null); - dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true); - } - }); - - dialog.show(); - - // Disable button (must be done **after** dialog is shown to prevent crash - dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); - // Set focus on input field - dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); - input.requestFocus(); - } - - private String getGroupName(View view) { - TextView groupNameTextView = view.findViewById(R.id.name); - return (String) groupNameTextView.getText(); - } - - private void moveGroup(View view, boolean up) { - List groups = DBHelper.getGroups(mDatabase); - final String groupName = getGroupName(view); - - int currentIndex = DBHelper.getGroup(mDatabase, groupName).order; - int newIndex; - - // Reinsert group in correct position - if (up) { - newIndex = currentIndex - 1; - } else { - newIndex = currentIndex + 1; - } - - // Don't try to move out of bounds - if (newIndex < 0 || newIndex >= groups.size()) { - return; - } - - Group group = groups.remove(currentIndex); - groups.add(newIndex, group); - - // Update database - DBHelper.reorderGroups(mDatabase, groups); - - // Update UI - updateGroupList(); - - // Ordering may have changed, so invalidate - invalidateHomescreenActiveTab(); - } - - @Override - public void onMoveDownButtonClicked(View view) { - moveGroup(view, false); - } - - @Override - public void onMoveUpButtonClicked(View view) { - moveGroup(view, true); - } - - @Override - public void onEditButtonClicked(View view) { - Intent intent = new Intent(this, ManageGroupActivity.class); - intent.putExtra("group", getGroupName(view)); - startActivity(intent); - } - - @Override - public void onDeleteButtonClicked(View view) { - final String groupName = getGroupName(view); - - AlertDialog.Builder builder = new MaterialAlertDialogBuilder(this); - builder.setTitle(R.string.deleteConfirmationGroup); - builder.setMessage(groupName); - - builder.setPositiveButton(getString(R.string.ok), (dialog, which) -> { - DBHelper.deleteGroup(mDatabase, groupName); - updateGroupList(); - // Delete may change ordering, so invalidate - invalidateHomescreenActiveTab(); - }); - builder.setNegativeButton(getString(R.string.cancel), (dialog, which) -> dialog.cancel()); - AlertDialog dialog = builder.create(); - dialog.show(); - } -} diff --git a/app/src/main/java/protect/card_locker/ManageGroupsActivity.kt b/app/src/main/java/protect/card_locker/ManageGroupsActivity.kt new file mode 100644 index 000000000..84fcf133b --- /dev/null +++ b/app/src/main/java/protect/card_locker/ManageGroupsActivity.kt @@ -0,0 +1,240 @@ +package protect.card_locker + +import android.content.DialogInterface +import android.content.Intent +import android.database.sqlite.SQLiteDatabase +import android.os.Bundle +import android.text.InputType +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import android.widget.EditText +import android.widget.LinearLayout +import android.widget.TextView +import androidx.appcompat.app.AlertDialog +import androidx.core.content.edit +import androidx.core.widget.doOnTextChanged +import androidx.recyclerview.widget.DefaultItemAnimator +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import protect.card_locker.GroupCursorAdapter.GroupAdapterListener +import protect.card_locker.databinding.ManageGroupsActivityBinding + +class ManageGroupsActivity : CatimaAppCompatActivity(), GroupAdapterListener { + private lateinit var binding: ManageGroupsActivityBinding + private lateinit var mDatabase: SQLiteDatabase + private lateinit var mHelpText: TextView + private lateinit var mGroupList: RecyclerView + private lateinit var mAdapter: GroupCursorAdapter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding = ManageGroupsActivityBinding.inflate(layoutInflater) + setTitle(R.string.groups) + setContentView(binding.root) + Utils.applyWindowInsets(binding.root) + setSupportActionBar(binding.toolbar) + enableToolbarBackButton() + + mDatabase = DBHelper(this).writableDatabase + } + + override fun onResume() { + super.onResume() + + with(binding.fabAdd) { + setOnClickListener { v: View -> + createGroup() + } + bringToFront() + } + + mGroupList = binding.include.list + mHelpText = binding.include.helpText + + // Init group list + LinearLayoutManager(applicationContext).apply { + mGroupList.layoutManager = this + } + mGroupList.setItemAnimator(DefaultItemAnimator()) + mAdapter = GroupCursorAdapter(this, null, this) + mGroupList.setAdapter(mAdapter) + + updateGroupList() + } + + private fun updateGroupList() { + mAdapter.swapCursor(DBHelper.getGroupCursor(mDatabase)) + + if (DBHelper.getGroupCount(mDatabase) == 0) { + mGroupList.visibility = View.GONE + mHelpText.visibility = View.VISIBLE + + return + } + + mGroupList.visibility = View.VISIBLE + mHelpText.visibility = View.GONE + } + + private fun invalidateHomescreenActiveTab() { + val activeTabPref = getSharedPreferences( + getString(R.string.sharedpreference_active_tab), + MODE_PRIVATE + ) + activeTabPref.edit { + putInt(getString(R.string.sharedpreference_active_tab), 0) + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) { + finish() + } + + return super.onOptionsItemSelected(item) + } + + private fun createGroup() { + val builder: AlertDialog.Builder = MaterialAlertDialogBuilder(this) + + // Header + builder.setTitle(R.string.enter_group_name) + + // Layout + val layout = LinearLayout(this) + layout.orientation = LinearLayout.VERTICAL + val params = LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ).apply { + val contentPadding = + resources.getDimensionPixelSize(R.dimen.alert_dialog_content_padding) + leftMargin = contentPadding + topMargin = contentPadding / 2 + rightMargin = contentPadding + } + + // EditText with spacing + val input = EditText(this) + input.setInputType(InputType.TYPE_CLASS_TEXT) + input.setLayoutParams(params) + layout.addView(input) + + // Set layout + builder.setView(layout) + + // Buttons + builder.setPositiveButton(getString(R.string.ok)) { dialog: DialogInterface, which: Int -> + DBHelper.insertGroup(mDatabase, input.text.trim().toString()) + updateGroupList() + } + builder.setNegativeButton(getString(R.string.cancel)) { dialog: DialogInterface, which: Int -> + dialog.cancel() + } + val dialog = builder.create() + + // Now that the dialog exists, we can bind something that affects the OK button + input.doOnTextChanged { s: CharSequence?, start: Int, before: Int, count: Int -> + val groupName = s?.trim().toString() + + if (groupName.isEmpty()) { + input.error = getString(R.string.group_name_is_empty) + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false) + return@doOnTextChanged + } + + if (DBHelper.getGroup(mDatabase, groupName) != null) { + input.error = getString(R.string.group_name_already_in_use) + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false) + return@doOnTextChanged + } + + input.error = null + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true) + } + + dialog.apply { + show() + // Disable button (must be done **after** dialog is shown to prevent crash + getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false) + // Set focus on input field + window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE) + } + + input.requestFocus() + } + + private fun getGroupName(view: View): String { + val groupNameTextView = view.findViewById(R.id.name) + return groupNameTextView.text.toString() + } + + private fun moveGroup(view: View, up: Boolean) { + val groups = DBHelper.getGroups(mDatabase) + val groupName = getGroupName(view) + + val currentIndex = DBHelper.getGroup(mDatabase, groupName).order + + // Reinsert group in correct position + val newIndex: Int = if (up) { + currentIndex - 1 + } else { + currentIndex + 1 + } + + // Don't try to move out of bounds + if (newIndex < 0 || newIndex >= groups.size) { + return + } + + val group = groups.removeAt(currentIndex) + groups.add(newIndex, group) + + // Update database + DBHelper.reorderGroups(mDatabase, groups) + + // Update UI + updateGroupList() + + // Ordering may have changed, so invalidate + invalidateHomescreenActiveTab() + } + + override fun onMoveDownButtonClicked(view: View) { + moveGroup(view, false) + } + + override fun onMoveUpButtonClicked(view: View) { + moveGroup(view, true) + } + + override fun onEditButtonClicked(view: View) { + Intent(this, ManageGroupActivity::class.java).apply { + putExtra("group", getGroupName(view)) + startActivity(this) + } + } + + override fun onDeleteButtonClicked(view: View) { + val groupName = getGroupName(view) + + MaterialAlertDialogBuilder(this).apply { + setTitle(R.string.deleteConfirmationGroup) + setMessage(groupName) + + setPositiveButton(getString(R.string.ok)) { dialog: DialogInterface, which: Int -> + DBHelper.deleteGroup(mDatabase, groupName) + updateGroupList() + // Delete may change ordering, so invalidate + invalidateHomescreenActiveTab() + } + setNegativeButton(getString(R.string.cancel)) { dialog: DialogInterface, which: Int -> + dialog.cancel() + } + }.create().show() + } +}