Skip to content

Commit

Permalink
Merge pull request #1083 from UweTrottmann/confirm-list-deletion
Browse files Browse the repository at this point in the history
Confirm list deletion
  • Loading branch information
UweTrottmann authored Dec 11, 2024
2 parents 3ae116c + edde7e5 commit 893806f
Show file tree
Hide file tree
Showing 27 changed files with 210 additions and 78 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Releases marked with 🧪 (or previously with the "beta" suffix) were released o
### Unreleased

* 🔧 Shows: revert to search symbol for primary button on discover screen.
* 🔧 Lists: ask for confirmation before deleting a list, actually call it delete instead of "just"
remove.

### 2024.5.2 - 2024-12-04 🧪

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2023 Uwe Trottmann
// SPDX-License-Identifier: Apache-2.0
// Copyright 2012-2024 Uwe Trottmann

package com.battlelancer.seriesguide.lists

Expand All @@ -12,7 +12,7 @@ import android.widget.TextView
import androidx.appcompat.app.AppCompatDialogFragment
import androidx.fragment.app.FragmentManager
import com.battlelancer.seriesguide.R
import com.battlelancer.seriesguide.databinding.DialogListManageBinding
import com.battlelancer.seriesguide.databinding.DialogAddListBinding
import com.battlelancer.seriesguide.provider.SeriesGuideContract
import com.battlelancer.seriesguide.util.safeShow
import com.google.android.material.dialog.MaterialAlertDialogBuilder
Expand All @@ -23,10 +23,10 @@ import com.google.android.material.textfield.TextInputLayout
*/
class AddListDialogFragment : AppCompatDialogFragment() {

private var binding: DialogListManageBinding? = null
private var binding: DialogAddListBinding? = null

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val binding = DialogListManageBinding.inflate(layoutInflater)
val binding = DialogAddListBinding.inflate(layoutInflater)
this.binding = binding

// title
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Uwe Trottmann

package com.battlelancer.seriesguide.lists

import android.app.Dialog
import android.os.Bundle
import androidx.appcompat.app.AppCompatDialogFragment
import androidx.core.os.bundleOf
import com.battlelancer.seriesguide.R
import com.battlelancer.seriesguide.provider.SgRoomDatabase
import com.google.android.material.dialog.MaterialAlertDialogBuilder

/**
* Dialog to confirm deletion of a list and its items.
*/
class DeleteListDialogFragment : AppCompatDialogFragment() {

private lateinit var listId: String

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

listId = requireArguments().getString(ARG_LIST_ID)
?: throw IllegalArgumentException("$ARG_LIST_ID must be supplied")
}

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val listTitle = SgRoomDatabase.getInstance(requireContext()).sgListHelper()
.getList(listId)
?.name ?: getString(R.string.unknown)

// Explicitly make negative button cancel (non-destructive action) as the delete list button
// is the negative action in the originating dialog; so accidentally pressing again in the
// same region does not do the destructive action.
return MaterialAlertDialogBuilder(requireContext())
.setTitle(requireContext().getString(R.string.confirm_delete, listTitle))
.setNegativeButton(android.R.string.cancel) { _, _ ->
// just dismiss
}
.setPositiveButton(R.string.list_remove) { _, _ ->
ListsTools.deleteList(requireContext(), listId)
}
.create()
}

companion object {

private const val ARG_LIST_ID = "list_id"

fun create(listId: String): DeleteListDialogFragment {
return DeleteListDialogFragment().apply {
arguments = bundleOf(ARG_LIST_ID to listId)
}
}
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2023 Uwe Trottmann
// SPDX-License-Identifier: Apache-2.0
// Copyright 2012-2024 Uwe Trottmann

package com.battlelancer.seriesguide.lists

Expand All @@ -12,7 +12,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.battlelancer.seriesguide.R
import com.battlelancer.seriesguide.databinding.DialogListManageBinding
import com.battlelancer.seriesguide.provider.SeriesGuideContract
import com.battlelancer.seriesguide.provider.SgRoomDatabase
import com.battlelancer.seriesguide.util.safeShow
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.launch
Expand All @@ -39,23 +39,29 @@ class ListManageDialogFragment : AppCompatDialogFragment() {
this.binding = binding

// buttons
binding.buttonNegative.isEnabled = false
binding.buttonNegative.setText(R.string.list_remove)
binding.buttonNegative.setOnClickListener {
// remove list and items
ListsTools.removeList(requireContext(), listId)
dismiss()
binding.buttonListManageDelete.apply {
isEnabled = false
setText(R.string.list_remove)
setOnClickListener {
// ask about removing list
DeleteListDialogFragment.create(listId)
.safeShow(parentFragmentManager, "confirm-delete-list")
dismiss()
}
}
binding.buttonPositive.setText(android.R.string.ok)
binding.buttonPositive.setOnClickListener {
val editText = this.binding?.textInputLayoutListManageListName?.editText
?: return@setOnClickListener

// update title
val listName = editText.text.toString().trim()
ListsTools.renameList(requireContext(), listId, listName)

dismiss()
binding.buttonListManageConfirm.apply {
setText(R.string.action_save)
setOnClickListener {
val editText =
this@ListManageDialogFragment.binding?.textInputLayoutListManageListName?.editText
?: return@setOnClickListener

// update title
val listName = editText.text.toString().trim()
ListsTools.renameList(requireContext(), listId, listName)

dismiss()
}
}

// Delay loading data for views to after this function
Expand All @@ -71,26 +77,16 @@ class ListManageDialogFragment : AppCompatDialogFragment() {
}

private fun configureViews() {
// Querying on main thread as the queries are very small
val listHelper = SgRoomDatabase.getInstance(requireContext()).sgListHelper()
// pre-populate list title
val list = requireContext().contentResolver
.query(
SeriesGuideContract.Lists.buildListUri(listId), arrayOf(
SeriesGuideContract.Lists.NAME
), null, null, null
)
val list = listHelper.getList(listId)
if (list == null) {
// list might have been removed, or query failed
dismiss()
return
}
if (!list.moveToFirst()) {
// list not found
list.close()
dismiss()
return
}
val listName = list.getString(0)
list.close()
val listName = list.name

val binding = this@ListManageDialogFragment.binding
if (binding == null) {
Expand All @@ -104,21 +100,14 @@ class ListManageDialogFragment : AppCompatDialogFragment() {
editTextName.addTextChangedListener(
AddListDialogFragment.ListNameTextWatcher(
requireContext(), textInputLayoutName,
binding.buttonPositive, listName
binding.buttonListManageConfirm, listName
)
)

// do only allow removing if this is NOT the last list
val lists = requireContext().contentResolver.query(
SeriesGuideContract.Lists.CONTENT_URI, arrayOf(
SeriesGuideContract.Lists._ID
), null, null, null
)
if (lists != null) {
if (lists.count > 1) {
binding.buttonNegative.isEnabled = true
}
lists.close()
val listsCount = listHelper.getListsCount()
if (listsCount > 1) {
binding.buttonListManageDelete.isEnabled = true
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import com.battlelancer.seriesguide.util.tasks.AddListTask;
import com.battlelancer.seriesguide.util.tasks.ChangeListItemListsTask;
import com.battlelancer.seriesguide.util.tasks.RemoveListItemTask;
import com.battlelancer.seriesguide.util.tasks.RemoveListTask;
import com.battlelancer.seriesguide.util.tasks.DeleteListTask;
import com.battlelancer.seriesguide.util.tasks.RenameListTask;
import com.battlelancer.seriesguide.util.tasks.ReorderListsTask;
import com.uwetrottmann.seriesguide.backend.lists.model.SgListItem;
Expand Down Expand Up @@ -60,8 +60,8 @@ static void renameList(@NonNull Context context, @NonNull String listId,
AsyncTask.THREAD_POOL_EXECUTOR);
}

static void removeList(@NonNull Context context, @NonNull String listId) {
new RemoveListTask(context, listId).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
static void deleteList(@NonNull Context context, @NonNull String listId) {
new DeleteListTask(context, listId).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}

static void reorderLists(@NonNull Context context,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,24 @@ import com.battlelancer.seriesguide.shows.tools.ShowStatus
@Dao
interface SgListHelper {

/**
* Is null on error or if it does not exist.
*/
@Query("SELECT * FROM lists WHERE list_id = :id")
fun getList(id: String): SgList?

@Query("SELECT * FROM lists ORDER BY ${Lists.SORT_ORDER_THEN_NAME}")
fun getListsForDisplay(): LiveData<List<SgList>>

@Query("SELECT * FROM lists ORDER BY ${Lists.SORT_ORDER_THEN_NAME}")
fun getListsForExport(): List<SgList>

/**
* Is 0 on error.
*/
@Query("SELECT COUNT(_id) FROM lists")
fun getListsCount(): Int

@Query("SELECT * FROM listitems WHERE item_ref_id = :tmdbId AND item_type = ${ListItemTypes.TMDB_SHOW}")
fun getListItemsWithTmdbId(tmdbId: Int): List<SgListItem>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@
import java.io.IOException;

/**
* Task to remove a list.
* Task to delete a list and its items.
*/
public class RemoveListTask extends BaseActionTask {
public class DeleteListTask extends BaseActionTask {

@NonNull protected final String listId;

public RemoveListTask(@NonNull Context context, @NonNull String listId) {
public DeleteListTask(@NonNull Context context, @NonNull String listId) {
super(context);
this.listId = listId;
}
Expand Down
69 changes: 69 additions & 0 deletions app/src/main/res/layout/dialog_add_list.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<FrameLayout
style="@style/DefaultPadding.DialogContent"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<com.google.android.material.textfield.TextInputLayout
android:id="@+id/textInputLayoutListManageListName"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<!-- Set inputType as it does not default to (single-line) text like EditText. -->
<!-- Set minWidth on EditText so FrameLayout sizes correctly. -->
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/list_title_hint"
android:inputType="text"
android:minWidth="@dimen/dialog_min_width" />

</com.google.android.material.textfield.TextInputLayout>

</FrameLayout>

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/large_padding"
android:paddingTop="@dimen/inline_padding"
android:paddingRight="@dimen/large_padding"
android:paddingBottom="@dimen/inline_padding">

<androidx.constraintlayout.helper.widget.Flow
android:layout_width="0dp"
android:layout_height="wrap_content"
app:constraint_referenced_ids="buttonNegative,buttonPositive"
app:flow_horizontalBias="1"
app:flow_horizontalStyle="packed"
app:flow_wrapMode="chain"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/buttonNegative"
style="?attr/buttonBarNegativeButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Button Negative With Long Text" />

<Button
android:id="@+id/buttonPositive"
style="?attr/buttonBarPositiveButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Button Positive With Long Text" />

</androidx.constraintlayout.widget.ConstraintLayout>

</LinearLayout>
12 changes: 7 additions & 5 deletions app/src/main/res/layout/dialog_list_manage.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/textInputLayoutListManageListName"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
app:endIconMode="clear_text">

<!-- Set inputType as it does not default to (single-line) text like EditText. -->
<!-- Set minWidth on EditText so FrameLayout sizes correctly. -->
Expand All @@ -38,27 +39,28 @@
android:paddingRight="@dimen/large_padding"
android:paddingBottom="@dimen/inline_padding">

<!-- Use spread_inside so delete button is far away from OK button -->
<androidx.constraintlayout.helper.widget.Flow
android:layout_width="0dp"
android:layout_height="wrap_content"
app:constraint_referenced_ids="buttonNegative,buttonPositive"
app:constraint_referenced_ids="buttonListManageDelete,buttonListManageConfirm"
app:flow_horizontalBias="1"
app:flow_horizontalStyle="packed"
app:flow_horizontalStyle="spread_inside"
app:flow_wrapMode="chain"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/buttonNegative"
android:id="@+id/buttonListManageDelete"
style="?attr/buttonBarNegativeButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Button Negative With Long Text" />

<Button
android:id="@+id/buttonPositive"
android:id="@+id/buttonListManageConfirm"
style="?attr/buttonBarPositiveButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
Expand Down
Loading

0 comments on commit 893806f

Please sign in to comment.