Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Confirm list deletion #1083

Merged
merged 6 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 =
[email protected]?.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 = [email protected]
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
Loading