mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-28 21:33:53 +01:00
Migrated helper modules to kotlin (#6007)
* Rename .java to .kt * Migrated delete and description module to kotlin (WIP) * Fix: Unit tests * Fix: Unit tests * Rename .java to .kt * Migrated data, db, and converter module to kotlin * Fix: Unit tests * Fix: Unit tests --------- Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com>
This commit is contained in:
parent
73311970c5
commit
3030a6fca7
18 changed files with 1168 additions and 973 deletions
|
|
@ -1,278 +0,0 @@
|
|||
package fr.free.nrw.commons.delete;
|
||||
|
||||
import static fr.free.nrw.commons.notification.NotificationHelper.NOTIFICATION_DELETE;
|
||||
import static fr.free.nrw.commons.utils.LangCodeUtils.getLocalizedResources;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.actions.PageEditClient;
|
||||
import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException;
|
||||
import fr.free.nrw.commons.notification.NotificationHelper;
|
||||
import fr.free.nrw.commons.review.ReviewController;
|
||||
import fr.free.nrw.commons.utils.LangCodeUtils;
|
||||
import fr.free.nrw.commons.utils.ViewUtilWrapper;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.SingleSource;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.Callable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Refactored async task to Rx
|
||||
*/
|
||||
@Singleton
|
||||
public class DeleteHelper {
|
||||
private final NotificationHelper notificationHelper;
|
||||
private final PageEditClient pageEditClient;
|
||||
private final ViewUtilWrapper viewUtil;
|
||||
private final String username;
|
||||
private AlertDialog d;
|
||||
private DialogInterface.OnMultiChoiceClickListener listener;
|
||||
|
||||
@Inject
|
||||
public DeleteHelper(NotificationHelper notificationHelper,
|
||||
@Named("commons-page-edit") PageEditClient pageEditClient,
|
||||
ViewUtilWrapper viewUtil,
|
||||
@Named("username") String username) {
|
||||
this.notificationHelper = notificationHelper;
|
||||
this.pageEditClient = pageEditClient;
|
||||
this.viewUtil = viewUtil;
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public interface to nominate a particular media file for deletion
|
||||
* @param context
|
||||
* @param media
|
||||
* @param reason
|
||||
* @return
|
||||
*/
|
||||
public Single<Boolean> makeDeletion(Context context, Media media, String reason) {
|
||||
viewUtil.showShortToast(context, "Trying to nominate " + media.getDisplayTitle() + " for deletion");
|
||||
|
||||
return delete(media, reason)
|
||||
.flatMapSingle(result -> Single.just(showDeletionNotification(context, media, result)))
|
||||
.firstOrError()
|
||||
.onErrorResumeNext(throwable -> {
|
||||
if (throwable instanceof InvalidLoginTokenException) {
|
||||
return Single.error(throwable);
|
||||
}
|
||||
return Single.error(throwable);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes several API calls to nominate the file for deletion
|
||||
* @param media
|
||||
* @param reason
|
||||
* @return
|
||||
*/
|
||||
private Observable<Boolean> delete(Media media, String reason) {
|
||||
Timber.d("thread is delete %s", Thread.currentThread().getName());
|
||||
String summary = "Nominating " + media.getFilename() + " for deletion.";
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
String fileDeleteString = "{{delete|reason=" + reason +
|
||||
"|subpage=" + media.getFilename() +
|
||||
"|day=" + calendar.get(Calendar.DAY_OF_MONTH) +
|
||||
"|month=" + calendar.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.ENGLISH) +
|
||||
"|year=" + calendar.get(Calendar.YEAR) +
|
||||
"}}";
|
||||
|
||||
String subpageString = "=== [[:" + media.getFilename() + "]] ===\n" +
|
||||
reason +
|
||||
" ~~~~";
|
||||
|
||||
String logPageString = "\n{{Commons:Deletion requests/" + media.getFilename() +
|
||||
"}}\n";
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd", Locale.getDefault());
|
||||
String date = sdf.format(calendar.getTime());
|
||||
|
||||
String userPageString = "\n{{subst:idw|" + media.getFilename() +
|
||||
"}} ~~~~";
|
||||
|
||||
String creator = media.getAuthor();
|
||||
if (creator == null || creator.isEmpty()) {
|
||||
throw new RuntimeException("Failed to nominate for deletion");
|
||||
}
|
||||
|
||||
return pageEditClient.prependEdit(media.getFilename(), fileDeleteString + "\n", summary)
|
||||
.onErrorResumeNext(throwable -> {
|
||||
if (throwable instanceof InvalidLoginTokenException) {
|
||||
return Observable.error(throwable);
|
||||
}
|
||||
return Observable.error(throwable);
|
||||
})
|
||||
.flatMap(result -> {
|
||||
if (result) {
|
||||
return pageEditClient.edit("Commons:Deletion_requests/" + media.getFilename(), subpageString + "\n", summary);
|
||||
}
|
||||
return Observable.error(new RuntimeException("Failed to nominate for deletion"));
|
||||
})
|
||||
.flatMap(result -> {
|
||||
if (result) {
|
||||
return pageEditClient.appendEdit("Commons:Deletion_requests/" + date, logPageString + "\n", summary);
|
||||
}
|
||||
return Observable.error(new RuntimeException("Failed to nominate for deletion"));
|
||||
})
|
||||
.flatMap(result -> {
|
||||
if (result) {
|
||||
return pageEditClient.appendEdit("User_Talk:" + creator, userPageString + "\n", summary);
|
||||
}
|
||||
return Observable.error(new RuntimeException("Failed to nominate for deletion"));
|
||||
});
|
||||
}
|
||||
|
||||
private boolean showDeletionNotification(Context context, Media media, boolean result) {
|
||||
String message;
|
||||
String title = context.getString(R.string.delete_helper_show_deletion_title);
|
||||
|
||||
if (result) {
|
||||
title += ": " + context.getString(R.string.delete_helper_show_deletion_title_success);
|
||||
message = context.getString((R.string.delete_helper_show_deletion_message_if),media.getDisplayTitle());
|
||||
} else {
|
||||
title += ": " + context.getString(R.string.delete_helper_show_deletion_title_failed);
|
||||
message = context.getString(R.string.delete_helper_show_deletion_message_else) ;
|
||||
}
|
||||
|
||||
String urlForDelete = BuildConfig.COMMONS_URL + "/wiki/Commons:Deletion_requests/" + media.getFilename();
|
||||
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(urlForDelete));
|
||||
notificationHelper.showNotification(context, title, message, NOTIFICATION_DELETE, browserIntent);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a reason needs to be asked before nominating for deletion
|
||||
* @param media
|
||||
* @param context
|
||||
* @param question
|
||||
* @param problem
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
public void askReasonAndExecute(Media media,
|
||||
Context context,
|
||||
String question,
|
||||
ReviewController.DeleteReason problem,
|
||||
ReviewController.ReviewCallback reviewCallback) {
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(context);
|
||||
alert.setTitle(question);
|
||||
|
||||
boolean[] checkedItems = {false, false, false, false};
|
||||
ArrayList<Integer> mUserReason = new ArrayList<>();
|
||||
|
||||
final String[] reasonList;
|
||||
final String[] reasonListEnglish;
|
||||
|
||||
if (problem == ReviewController.DeleteReason.SPAM) {
|
||||
reasonList = new String[] {
|
||||
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)
|
||||
};
|
||||
reasonListEnglish = new String[] {
|
||||
getLocalizedResources(context, Locale.ENGLISH).getString(R.string.delete_helper_ask_spam_selfie),
|
||||
getLocalizedResources(context, Locale.ENGLISH).getString(R.string.delete_helper_ask_spam_blurry),
|
||||
getLocalizedResources(context, Locale.ENGLISH).getString(R.string.delete_helper_ask_spam_nonsense)
|
||||
};
|
||||
} else if (problem == ReviewController.DeleteReason.COPYRIGHT_VIOLATION) {
|
||||
reasonList = new String[] {
|
||||
context.getString(R.string.delete_helper_ask_reason_copyright_press_photo),
|
||||
context.getString(R.string.delete_helper_ask_reason_copyright_internet_photo),
|
||||
context.getString(R.string.delete_helper_ask_reason_copyright_logo),
|
||||
context.getString(R.string.delete_helper_ask_reason_copyright_no_freedom_of_panorama)
|
||||
};
|
||||
reasonListEnglish = new String[] {
|
||||
getLocalizedResources(context, Locale.ENGLISH).getString(R.string.delete_helper_ask_reason_copyright_press_photo),
|
||||
getLocalizedResources(context, Locale.ENGLISH).getString(R.string.delete_helper_ask_reason_copyright_internet_photo),
|
||||
getLocalizedResources(context, Locale.ENGLISH).getString(R.string.delete_helper_ask_reason_copyright_logo),
|
||||
getLocalizedResources(context, Locale.ENGLISH).getString(R.string.delete_helper_ask_reason_copyright_no_freedom_of_panorama)
|
||||
};
|
||||
} else {
|
||||
reasonList = new String[] {};
|
||||
reasonListEnglish = new String[] {};
|
||||
}
|
||||
|
||||
alert.setMultiChoiceItems(reasonList, checkedItems, listener = (dialogInterface, position, isChecked) -> {
|
||||
|
||||
if (isChecked) {
|
||||
mUserReason.add(position);
|
||||
} else {
|
||||
mUserReason.remove((Integer.valueOf(position)));
|
||||
}
|
||||
|
||||
// disable the OK button if no reason selected
|
||||
((AlertDialog) dialogInterface).getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(
|
||||
!mUserReason.isEmpty());
|
||||
});
|
||||
|
||||
alert.setPositiveButton(context.getString(R.string.ok), (dialogInterface, i) -> {
|
||||
reviewCallback.disableButtons();
|
||||
|
||||
|
||||
String reason = getLocalizedResources(context, Locale.ENGLISH).getString(R.string.delete_helper_ask_alert_set_positive_button_reason) + " ";
|
||||
|
||||
for (int j = 0; j < mUserReason.size(); j++) {
|
||||
reason = reason + reasonListEnglish[mUserReason.get(j)];
|
||||
if (j != mUserReason.size() - 1) {
|
||||
reason = reason + ", ";
|
||||
}
|
||||
}
|
||||
|
||||
Timber.d("thread is askReasonAndExecute %s", Thread.currentThread().getName());
|
||||
|
||||
String finalReason = reason;
|
||||
|
||||
Single.defer((Callable<SingleSource<Boolean>>) () ->
|
||||
makeDeletion(context, media, finalReason))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(aBoolean -> {
|
||||
reviewCallback.onSuccess();
|
||||
}, throwable -> {
|
||||
if (throwable instanceof InvalidLoginTokenException) {
|
||||
reviewCallback.onTokenException((InvalidLoginTokenException) throwable);
|
||||
} else {
|
||||
reviewCallback.onFailure();
|
||||
}
|
||||
reviewCallback.enableButtons();
|
||||
});
|
||||
});
|
||||
alert.setNegativeButton(context.getString(R.string.cancel), (dialog, which) -> reviewCallback.onFailure());
|
||||
d = alert.create();
|
||||
d.show();
|
||||
|
||||
// disable the OK button by default
|
||||
d.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the instance of shown AlertDialog,
|
||||
* used for taking reference during unit test
|
||||
* */
|
||||
public AlertDialog getDialog(){
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the instance of shown DialogInterface.OnMultiChoiceClickListener,
|
||||
* used for taking reference during unit test
|
||||
* */
|
||||
public DialogInterface.OnMultiChoiceClickListener getListener(){
|
||||
return listener;
|
||||
}
|
||||
}
|
||||
333
app/src/main/java/fr/free/nrw/commons/delete/DeleteHelper.kt
Normal file
333
app/src/main/java/fr/free/nrw/commons/delete/DeleteHelper.kt
Normal file
|
|
@ -0,0 +1,333 @@
|
|||
package fr.free.nrw.commons.delete
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import fr.free.nrw.commons.BuildConfig
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.actions.PageEditClient
|
||||
import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException
|
||||
import fr.free.nrw.commons.notification.NotificationHelper
|
||||
import fr.free.nrw.commons.notification.NotificationHelper.Companion.NOTIFICATION_DELETE
|
||||
import fr.free.nrw.commons.review.ReviewController
|
||||
import fr.free.nrw.commons.utils.LangCodeUtils.getLocalizedResources
|
||||
import fr.free.nrw.commons.utils.ViewUtilWrapper
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import timber.log.Timber
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Refactored async task to Rx
|
||||
*/
|
||||
@Singleton
|
||||
class DeleteHelper @Inject constructor(
|
||||
private val notificationHelper: NotificationHelper,
|
||||
@Named("commons-page-edit") private val pageEditClient: PageEditClient,
|
||||
private val viewUtil: ViewUtilWrapper,
|
||||
@Named("username") private val username: String
|
||||
) {
|
||||
private var d: AlertDialog? = null
|
||||
private var listener: DialogInterface.OnMultiChoiceClickListener? = null
|
||||
|
||||
/**
|
||||
* Public interface to nominate a particular media file for deletion
|
||||
* @param context
|
||||
* @param media
|
||||
* @param reason
|
||||
* @return
|
||||
*/
|
||||
fun makeDeletion(
|
||||
context: Context?,
|
||||
media: Media?,
|
||||
reason: String?
|
||||
): Single<Boolean>? {
|
||||
|
||||
if(context == null && media == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
viewUtil.showShortToast(
|
||||
context!!,
|
||||
"Trying to nominate ${media?.displayTitle} for deletion"
|
||||
)
|
||||
|
||||
return reason?.let {
|
||||
delete(media!!, it)
|
||||
.flatMapSingle { result ->
|
||||
Single.just(showDeletionNotification(context, media, result))
|
||||
}
|
||||
.firstOrError()
|
||||
.onErrorResumeNext { throwable ->
|
||||
if (throwable is InvalidLoginTokenException) {
|
||||
Single.error(throwable)
|
||||
} else {
|
||||
Single.error(throwable)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes several API calls to nominate the file for deletion
|
||||
* @param media
|
||||
* @param reason
|
||||
* @return
|
||||
*/
|
||||
private fun delete(media: Media, reason: String): Observable<Boolean> {
|
||||
Timber.d("thread is delete %s", Thread.currentThread().name)
|
||||
val summary = "Nominating ${media.filename} for deletion."
|
||||
val calendar = Calendar.getInstance()
|
||||
val fileDeleteString = """
|
||||
{{delete|reason=$reason|subpage=${media.filename}|day=
|
||||
${calendar.get(Calendar.DAY_OF_MONTH)}|month=${
|
||||
calendar.getDisplayName(
|
||||
Calendar.MONTH,
|
||||
Calendar.LONG,
|
||||
Locale.ENGLISH
|
||||
)
|
||||
}|year=${calendar.get(Calendar.YEAR)}}}
|
||||
""".trimIndent()
|
||||
|
||||
val subpageString = """
|
||||
=== [[:${media.filename}]] ===
|
||||
$reason ~~~~
|
||||
""".trimIndent()
|
||||
|
||||
val logPageString = "\n{{Commons:Deletion requests/${media.filename}}}\n"
|
||||
val sdf = SimpleDateFormat("yyyy/MM/dd", Locale.getDefault())
|
||||
val date = sdf.format(calendar.time)
|
||||
|
||||
val userPageString = "\n{{subst:idw|${media.filename}}} ~~~~"
|
||||
|
||||
val creator = media.author
|
||||
?: throw RuntimeException("Failed to nominate for deletion")
|
||||
|
||||
return pageEditClient.prependEdit(
|
||||
media.filename!!,
|
||||
"$fileDeleteString\n",
|
||||
summary
|
||||
)
|
||||
.onErrorResumeNext { throwable: Throwable ->
|
||||
if (throwable is InvalidLoginTokenException) {
|
||||
Observable.error(throwable)
|
||||
} else {
|
||||
Observable.error(throwable)
|
||||
}
|
||||
}
|
||||
.flatMap { result: Boolean ->
|
||||
if (result) {
|
||||
pageEditClient.edit(
|
||||
"Commons:Deletion_requests/${media.filename}",
|
||||
"$subpageString\n",
|
||||
summary
|
||||
)
|
||||
} else {
|
||||
Observable.error(RuntimeException("Failed to nominate for deletion"))
|
||||
}
|
||||
}
|
||||
.flatMap { result: Boolean ->
|
||||
if (result) {
|
||||
pageEditClient.appendEdit(
|
||||
"Commons:Deletion_requests/$date",
|
||||
"$logPageString\n",
|
||||
summary
|
||||
)
|
||||
} else {
|
||||
Observable.error(RuntimeException("Failed to nominate for deletion"))
|
||||
}
|
||||
}
|
||||
.flatMap { result: Boolean ->
|
||||
if (result) {
|
||||
pageEditClient.appendEdit("User_Talk:$creator", "$userPageString\n", summary)
|
||||
} else {
|
||||
Observable.error(RuntimeException("Failed to nominate for deletion"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("StringFormatInvalid")
|
||||
private fun showDeletionNotification(
|
||||
context: Context,
|
||||
media: Media,
|
||||
result: Boolean
|
||||
): Boolean {
|
||||
val title: String
|
||||
val message: String
|
||||
var baseTitle = context.getString(R.string.delete_helper_show_deletion_title)
|
||||
|
||||
if (result) {
|
||||
baseTitle += ": ${
|
||||
context.getString(R.string.delete_helper_show_deletion_title_success)
|
||||
}"
|
||||
title = baseTitle
|
||||
message = context
|
||||
.getString(R.string.delete_helper_show_deletion_message_if, media.displayTitle)
|
||||
} else {
|
||||
baseTitle += ": ${context.getString(R.string.delete_helper_show_deletion_title_failed)}"
|
||||
title = baseTitle
|
||||
message = context.getString(R.string.delete_helper_show_deletion_message_else)
|
||||
}
|
||||
|
||||
val urlForDelete = "${BuildConfig.COMMONS_URL}/wiki/Commons:Deletion_requests/${
|
||||
media.filename
|
||||
}"
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(urlForDelete))
|
||||
notificationHelper
|
||||
.showNotification(context, title, message, NOTIFICATION_DELETE, browserIntent)
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a reason needs to be asked before nominating for deletion
|
||||
* @param media
|
||||
* @param context
|
||||
* @param question
|
||||
* @param problem
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
fun askReasonAndExecute(
|
||||
media: Media?,
|
||||
context: Context,
|
||||
question: String,
|
||||
problem: ReviewController.DeleteReason,
|
||||
reviewCallback: ReviewController.ReviewCallback
|
||||
) {
|
||||
val alert = AlertDialog.Builder(context)
|
||||
alert.setTitle(question)
|
||||
|
||||
val checkedItems = booleanArrayOf(false, false, false, false)
|
||||
val mUserReason = arrayListOf<Int>()
|
||||
|
||||
val reasonList: Array<String>
|
||||
val reasonListEnglish: Array<String>
|
||||
|
||||
when (problem) {
|
||||
ReviewController.DeleteReason.SPAM -> {
|
||||
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)
|
||||
)
|
||||
reasonListEnglish = arrayOf(
|
||||
getLocalizedResources(context, Locale.ENGLISH)
|
||||
.getString(R.string.delete_helper_ask_spam_selfie),
|
||||
getLocalizedResources(context, Locale.ENGLISH)
|
||||
.getString(R.string.delete_helper_ask_spam_blurry),
|
||||
getLocalizedResources(context, Locale.ENGLISH)
|
||||
.getString(R.string.delete_helper_ask_spam_nonsense)
|
||||
)
|
||||
}
|
||||
ReviewController.DeleteReason.COPYRIGHT_VIOLATION -> {
|
||||
reasonList = arrayOf(
|
||||
context.getString(R.string.delete_helper_ask_reason_copyright_press_photo),
|
||||
context.getString(R.string.delete_helper_ask_reason_copyright_internet_photo),
|
||||
context.getString(R.string.delete_helper_ask_reason_copyright_logo),
|
||||
context.getString(
|
||||
R.string.delete_helper_ask_reason_copyright_no_freedom_of_panorama
|
||||
)
|
||||
)
|
||||
reasonListEnglish = arrayOf(
|
||||
getLocalizedResources(context, Locale.ENGLISH)
|
||||
.getString(R.string.delete_helper_ask_reason_copyright_press_photo),
|
||||
getLocalizedResources(context, Locale.ENGLISH)
|
||||
.getString(R.string.delete_helper_ask_reason_copyright_internet_photo),
|
||||
getLocalizedResources(context, Locale.ENGLISH)
|
||||
.getString(R.string.delete_helper_ask_reason_copyright_logo),
|
||||
getLocalizedResources(context, Locale.ENGLISH)
|
||||
.getString(
|
||||
R.string.delete_helper_ask_reason_copyright_no_freedom_of_panorama
|
||||
)
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
reasonList = emptyArray()
|
||||
reasonListEnglish = emptyArray()
|
||||
}
|
||||
}
|
||||
|
||||
alert.setMultiChoiceItems(
|
||||
reasonList,
|
||||
checkedItems
|
||||
) { dialogInterface, position, isChecked ->
|
||||
if (isChecked) {
|
||||
mUserReason.add(position)
|
||||
} else {
|
||||
mUserReason.remove(position)
|
||||
}
|
||||
|
||||
// Safely enable or disable the OK button based on selection
|
||||
val dialog = dialogInterface as? AlertDialog
|
||||
dialog?.getButton(AlertDialog.BUTTON_POSITIVE)?.isEnabled = mUserReason.isNotEmpty()
|
||||
}
|
||||
|
||||
alert.setPositiveButton(context.getString(R.string.ok)) { _, _ ->
|
||||
reviewCallback.disableButtons()
|
||||
|
||||
val reason = buildString {
|
||||
append(
|
||||
getLocalizedResources(context, Locale.ENGLISH)
|
||||
.getString(R.string.delete_helper_ask_alert_set_positive_button_reason)
|
||||
)
|
||||
append(" ")
|
||||
|
||||
mUserReason.forEachIndexed { index, position ->
|
||||
append(reasonListEnglish[position])
|
||||
if (index != mUserReason.lastIndex) {
|
||||
append(", ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timber.d("thread is askReasonAndExecute %s", Thread.currentThread().name)
|
||||
|
||||
if (media != null) {
|
||||
Single.defer { makeDeletion(context, media, reason) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ reviewCallback.onSuccess() }, { throwable ->
|
||||
when (throwable) {
|
||||
is InvalidLoginTokenException ->
|
||||
reviewCallback.onTokenException(throwable)
|
||||
else -> reviewCallback.onFailure()
|
||||
}
|
||||
reviewCallback.enableButtons()
|
||||
})
|
||||
}
|
||||
}
|
||||
alert.setNegativeButton(
|
||||
context.getString(R.string.cancel)
|
||||
) { _, _ -> reviewCallback.onFailure() }
|
||||
|
||||
d = alert.create()
|
||||
d?.setOnShowListener {
|
||||
// Safely initialize the OK button state after the dialog is fully shown
|
||||
d?.getButton(AlertDialog.BUTTON_POSITIVE)?.isEnabled = false
|
||||
}
|
||||
d?.show()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* returns the instance of shown AlertDialog,
|
||||
* used for taking reference during unit test
|
||||
*/
|
||||
fun getDialog(): AlertDialog? = d
|
||||
|
||||
/**
|
||||
* returns the instance of shown DialogInterface.OnMultiChoiceClickListener,
|
||||
* used for taking reference during unit test
|
||||
*/
|
||||
fun getListener(): DialogInterface.OnMultiChoiceClickListener? = listener
|
||||
}
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
package fr.free.nrw.commons.delete;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import fr.free.nrw.commons.utils.DateUtil;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.profile.achievements.FeedbackResponse;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
|
||||
import fr.free.nrw.commons.utils.ViewUtilWrapper;
|
||||
import io.reactivex.Single;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* This class handles the reason for deleting a Media object
|
||||
*/
|
||||
@Singleton
|
||||
public class ReasonBuilder {
|
||||
|
||||
private SessionManager sessionManager;
|
||||
private OkHttpJsonApiClient okHttpJsonApiClient;
|
||||
private Context context;
|
||||
private ViewUtilWrapper viewUtilWrapper;
|
||||
|
||||
@Inject
|
||||
public ReasonBuilder(Context context,
|
||||
SessionManager sessionManager,
|
||||
OkHttpJsonApiClient okHttpJsonApiClient,
|
||||
ViewUtilWrapper viewUtilWrapper) {
|
||||
this.context = context;
|
||||
this.sessionManager = sessionManager;
|
||||
this.okHttpJsonApiClient = okHttpJsonApiClient;
|
||||
this.viewUtilWrapper = viewUtilWrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* To process the reason and append the media's upload date and uploaded_by_me string
|
||||
* @param media
|
||||
* @param reason
|
||||
* @return
|
||||
*/
|
||||
public Single<String> getReason(Media media, String reason) {
|
||||
return fetchArticleNumber(media, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* get upload date for the passed Media
|
||||
*/
|
||||
private String prettyUploadedDate(Media media) {
|
||||
Date date = media.getDateUploaded();
|
||||
if (date == null || date.toString() == null || date.toString().isEmpty()) {
|
||||
return "Uploaded date not available";
|
||||
}
|
||||
return DateUtil.getDateStringWithSkeletonPattern(date,"dd MMM yyyy");
|
||||
}
|
||||
|
||||
private Single<String> fetchArticleNumber(Media media, String reason) {
|
||||
if (checkAccount()) {
|
||||
return okHttpJsonApiClient
|
||||
.getAchievements(sessionManager.getUserName())
|
||||
.map(feedbackResponse -> appendArticlesUsed(feedbackResponse, media, reason));
|
||||
}
|
||||
return Single.just("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the uploaded_by_me string, the upload date, name of articles using images
|
||||
* and appends it to the received reason
|
||||
* @param feedBack object
|
||||
* @param media whose upload data is to be fetched
|
||||
* @param reason
|
||||
*/
|
||||
private String appendArticlesUsed(FeedbackResponse feedBack, Media media, String reason) {
|
||||
String reason1Template = context.getString(R.string.uploaded_by_myself);
|
||||
reason += String.format(Locale.getDefault(), reason1Template, prettyUploadedDate(media), feedBack.getArticlesUsingImages());
|
||||
Timber.i("New Reason %s", reason);
|
||||
return reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* check to ensure that user is logged in
|
||||
* @return
|
||||
*/
|
||||
private boolean checkAccount(){
|
||||
if (!sessionManager.doesAccountExist()) {
|
||||
Timber.d("Current account is null");
|
||||
viewUtilWrapper.showLongToast(context, context.getResources().getString(R.string.user_not_logged_in));
|
||||
sessionManager.forceLogin(context);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
package fr.free.nrw.commons.delete
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
|
||||
import fr.free.nrw.commons.utils.DateUtil
|
||||
import java.util.Locale
|
||||
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.profile.achievements.FeedbackResponse
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
|
||||
import fr.free.nrw.commons.utils.ViewUtilWrapper
|
||||
import io.reactivex.Single
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* This class handles the reason for deleting a Media object
|
||||
*/
|
||||
@Singleton
|
||||
class ReasonBuilder @Inject constructor(
|
||||
private val context: Context,
|
||||
private val sessionManager: SessionManager,
|
||||
private val okHttpJsonApiClient: OkHttpJsonApiClient,
|
||||
private val viewUtilWrapper: ViewUtilWrapper
|
||||
) {
|
||||
|
||||
/**
|
||||
* To process the reason and append the media's upload date and uploaded_by_me string
|
||||
* @param media
|
||||
* @param reason
|
||||
* @return
|
||||
*/
|
||||
fun getReason(media: Media?, reason: String?): Single<String> {
|
||||
if (media == null || reason == null) {
|
||||
return Single.just("Not known")
|
||||
}
|
||||
return fetchArticleNumber(media, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* get upload date for the passed Media
|
||||
*/
|
||||
private fun prettyUploadedDate(media: Media): String {
|
||||
val date = media.dateUploaded
|
||||
return if (date == null || date.toString().isEmpty()) {
|
||||
"Uploaded date not available"
|
||||
} else {
|
||||
DateUtil.getDateStringWithSkeletonPattern(date, "dd MMM yyyy")
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchArticleNumber(media: Media, reason: String): Single<String> {
|
||||
return if (checkAccount()) {
|
||||
okHttpJsonApiClient
|
||||
.getAchievements(sessionManager.userName)
|
||||
.map { feedbackResponse -> appendArticlesUsed(feedbackResponse, media, reason) }
|
||||
} else {
|
||||
Single.just("")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the uploaded_by_me string, the upload date, name of articles using images
|
||||
* and appends it to the received reason
|
||||
* @param feedBack object
|
||||
* @param media whose upload data is to be fetched
|
||||
* @param reason
|
||||
*/
|
||||
@SuppressLint("StringFormatInvalid")
|
||||
private fun appendArticlesUsed(feedBack: FeedbackResponse, media: Media, reason: String): String {
|
||||
val reason1Template = context.getString(R.string.uploaded_by_myself)
|
||||
return reason + String.format(Locale.getDefault(), reason1Template, prettyUploadedDate(media), feedBack.articlesUsingImages)
|
||||
.also { Timber.i("New Reason %s", it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* check to ensure that user is logged in
|
||||
* @return
|
||||
*/
|
||||
private fun checkAccount(): Boolean {
|
||||
return if (!sessionManager.doesAccountExist()) {
|
||||
Timber.d("Current account is null")
|
||||
viewUtilWrapper.showLongToast(context, context.getString(R.string.user_not_logged_in))
|
||||
sessionManager.forceLogin(context)
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue