diff --git a/app/src/main/java/fr/free/nrw/commons/delete/DeleteHelper.kt b/app/src/main/java/fr/free/nrw/commons/delete/DeleteHelper.kt index 3f9ae47ac1..bc7f55ddb5 100644 --- a/app/src/main/java/fr/free/nrw/commons/delete/DeleteHelper.kt +++ b/app/src/main/java/fr/free/nrw/commons/delete/DeleteHelper.kt @@ -5,7 +5,13 @@ import android.content.Context import android.content.DialogInterface import android.content.Intent import android.net.Uri +import android.text.Editable +import android.text.TextWatcher +import android.view.LayoutInflater +import android.widget.CheckBox +import android.widget.LinearLayout import androidx.appcompat.app.AlertDialog +import com.google.android.material.textfield.TextInputEditText import fr.free.nrw.commons.BuildConfig import fr.free.nrw.commons.Media import fr.free.nrw.commons.R @@ -206,7 +212,6 @@ class DeleteHelper @Inject constructor( alert.setCancelable(false) alert.setTitle(question) - val checkedItems = booleanArrayOf(false, false, false, false) val mUserReason = arrayListOf() val reasonList: Array @@ -217,7 +222,8 @@ class DeleteHelper @Inject constructor( reasonList = arrayOf( context.getString(R.string.delete_helper_ask_spam_selfie), context.getString(R.string.delete_helper_ask_spam_blurry), - context.getString(R.string.delete_helper_ask_spam_nonsense) + context.getString(R.string.delete_helper_ask_spam_nonsense), + context.getString(R.string.delete_helper_ask_spam_other) ) reasonListEnglish = arrayOf( getLocalizedResources(context, Locale.ENGLISH) @@ -225,7 +231,9 @@ class DeleteHelper @Inject constructor( getLocalizedResources(context, Locale.ENGLISH) .getString(R.string.delete_helper_ask_spam_blurry), getLocalizedResources(context, Locale.ENGLISH) - .getString(R.string.delete_helper_ask_spam_nonsense) + .getString(R.string.delete_helper_ask_spam_nonsense), + getLocalizedResources(context, Locale.ENGLISH) + .getString(R.string.delete_helper_ask_spam_other) ) } ReviewController.DeleteReason.COPYRIGHT_VIOLATION -> { @@ -256,21 +264,49 @@ class DeleteHelper @Inject constructor( } } - alert.setMultiChoiceItems( - reasonList, - checkedItems - ) { dialogInterface, position, isChecked -> - if (isChecked) { - mUserReason.add(position) - } else { - mUserReason.remove(position) - } + // Inflate custom layout + val inflater = LayoutInflater.from(context) + val customView = inflater.inflate(R.layout.dialog_deletion_reason, null) + val checkboxContainer = customView.findViewById(R.id.checkboxContainer) + val otherNotesEditText = customView.findViewById(R.id.otherNotesEditText) + + // Function to update OK button state + fun updateOkButtonState() { + val hasSelection = mUserReason.isNotEmpty() + val hasText = !otherNotesEditText?.text.isNullOrBlank() + + d?.getButton(AlertDialog.BUTTON_POSITIVE)?.isEnabled = hasSelection && hasText + } - // Safely enable or disable the OK button based on selection - val dialog = dialogInterface as? AlertDialog - dialog?.getButton(AlertDialog.BUTTON_POSITIVE)?.isEnabled = mUserReason.isNotEmpty() + // Create checkboxes dynamically + val checkBoxes = mutableListOf() + reasonList.forEachIndexed { index, reason -> + val checkBox = CheckBox(context) + checkBox.text = reason + checkBox.setOnCheckedChangeListener { _, isChecked -> + if (isChecked) { + mUserReason.add(index) + } else { + mUserReason.remove(index) + } + updateOkButtonState() + } + checkBoxes.add(checkBox) + checkboxContainer.addView(checkBox) } + // Add text watcher for other notes field + otherNotesEditText?.addTextChangedListener(object : TextWatcher { + override fun afterTextChanged(s: Editable?) { + updateOkButtonState() + } + + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} + }) + + alert.setView(customView) + alert.setPositiveButton(context.getString(R.string.ok)) { _, _ -> reviewCallback.disableButtons() @@ -281,12 +317,21 @@ class DeleteHelper @Inject constructor( ) append(" ") - mUserReason.forEachIndexed { index, position -> + mUserReason.sorted().forEachIndexed { index, position -> append(reasonListEnglish[position]) if (index != mUserReason.lastIndex) { append(", ") } } + + // Add other notes if provided + val otherNotes = otherNotesEditText?.text?.toString()?.trim() + if (!otherNotes.isNullOrBlank()) { + if (mUserReason.isNotEmpty()) { + append(". ") + } + append(otherNotes) + } } Timber.d("thread is askReasonAndExecute %s", Thread.currentThread().name) diff --git a/app/src/main/res/layout/dialog_deletion_reason.xml b/app/src/main/res/layout/dialog_deletion_reason.xml new file mode 100644 index 0000000000..78a169b7a6 --- /dev/null +++ b/app/src/main/res/layout/dialog_deletion_reason.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 950ffd0754..8558c6026b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -568,6 +568,8 @@ Upload your first media by tapping on the add button. a selfie which is not used in any article totally blurry nonsense, absolutely unusable in any article + Other: specify below + Justification Press photo Random photo from internet Logo diff --git a/app/src/test/kotlin/fr/free/nrw/commons/delete/DeleteHelperTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/delete/DeleteHelperTest.kt index 2300998108..c254ec4352 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/delete/DeleteHelperTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/delete/DeleteHelperTest.kt @@ -2,6 +2,9 @@ package fr.free.nrw.commons.delete import android.app.AlertDialog import android.content.Context +import android.widget.CheckBox +import android.widget.LinearLayout +import com.google.android.material.textfield.TextInputEditText import com.nhaarman.mockitokotlin2.eq import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.verify @@ -181,7 +184,7 @@ class DeleteHelperTest { } @Test - fun alertDialogPositiveButtonDisableTest() { + fun buttonRemainsDisabledWhenCheckboxCheckedButNoText() { val mContext = RuntimeEnvironment.getApplication().applicationContext deleteHelper.askReasonAndExecute( media, @@ -190,23 +193,34 @@ class DeleteHelperTest { ReviewController.DeleteReason.COPYRIGHT_VIOLATION, callback ) - deleteHelper.getListener()?.onClick( - deleteHelper.getDialog(), - 1, - true - ) - assertEquals( - true, - deleteHelper.getDialog()?.getButton(AlertDialog.BUTTON_POSITIVE)?.isEnabled - ) + val dialog = deleteHelper.getDialog() + val container = dialog?.findViewById(R.id.checkboxContainer) + val checkbox = container?.getChildAt(0) as? CheckBox + checkbox?.isChecked = true + + assertEquals(false, dialog?.getButton(AlertDialog.BUTTON_POSITIVE)?.isEnabled) } @Test - fun alertDialogPositiveButtonEnableTest() { + fun buttonEnabledWhenCheckboxCheckedAndTextEntered() { val mContext = RuntimeEnvironment.getApplication().applicationContext - deleteHelper.askReasonAndExecute(media, mContext, "My Question", ReviewController.DeleteReason.COPYRIGHT_VIOLATION, callback) - deleteHelper.getListener()?.onClick(deleteHelper.getDialog(), 1, true) - assertEquals(true, deleteHelper.getDialog()?.getButton(AlertDialog.BUTTON_POSITIVE)?.isEnabled) + deleteHelper.askReasonAndExecute( + media, + mContext, + "My Question", + ReviewController.DeleteReason.SPAM, + callback + ) + + val dialog = deleteHelper.getDialog() + val container = dialog?.findViewById(R.id.checkboxContainer) + val checkbox = container?.getChildAt(0) as? CheckBox + checkbox?.isChecked = true + + val editText = dialog?.findViewById(R.id.otherNotesEditText) + editText?.setText("some justification") + + assertEquals(true, dialog?.getButton(AlertDialog.BUTTON_POSITIVE)?.isEnabled) } @Test(expected = RuntimeException::class)