mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 12:23:58 +01:00
Migrate campaigns package to kotlin (#5969)
* Convert ICampaignsView to kotlin along with simple fix * Convert CampaignView to Kotlin * Convert CampaignsPresenter to Kotlin * Convert CampaignsPresenter to kotlin --------- Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com>
This commit is contained in:
parent
a6152f937e
commit
1afff73c24
7 changed files with 247 additions and 261 deletions
|
|
@ -1,118 +0,0 @@
|
|||
package fr.free.nrw.commons.campaigns;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import fr.free.nrw.commons.campaigns.models.Campaign;
|
||||
import fr.free.nrw.commons.databinding.LayoutCampaginBinding;
|
||||
import fr.free.nrw.commons.theme.BaseActivity;
|
||||
import fr.free.nrw.commons.utils.CommonsDateUtil;
|
||||
|
||||
import fr.free.nrw.commons.utils.DateUtil;
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.contributions.MainActivity;
|
||||
import fr.free.nrw.commons.utils.SwipableCardView;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
|
||||
/**
|
||||
* A view which represents a single campaign
|
||||
*/
|
||||
public class CampaignView extends SwipableCardView {
|
||||
Campaign campaign;
|
||||
private LayoutCampaginBinding binding;
|
||||
private ViewHolder viewHolder;
|
||||
|
||||
public static final String CAMPAIGNS_DEFAULT_PREFERENCE = "displayCampaignsCardView";
|
||||
public static final String WLM_CARD_PREFERENCE = "displayWLMCardView";
|
||||
|
||||
private String campaignPreference = CAMPAIGNS_DEFAULT_PREFERENCE;
|
||||
|
||||
public CampaignView(@NonNull Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public CampaignView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public CampaignView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
public void setCampaign(final Campaign campaign) {
|
||||
this.campaign = campaign;
|
||||
if (campaign != null) {
|
||||
if (campaign.isWLMCampaign()) {
|
||||
campaignPreference = WLM_CARD_PREFERENCE;
|
||||
}
|
||||
setVisibility(View.VISIBLE);
|
||||
viewHolder.init();
|
||||
} else {
|
||||
this.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public boolean onSwipe(final View view) {
|
||||
view.setVisibility(View.GONE);
|
||||
((BaseActivity) getContext()).defaultKvStore
|
||||
.putBoolean(CAMPAIGNS_DEFAULT_PREFERENCE, false);
|
||||
ViewUtil.showLongToast(getContext(),
|
||||
getResources().getString(R.string.nearby_campaign_dismiss_message));
|
||||
return true;
|
||||
}
|
||||
|
||||
private void init() {
|
||||
binding = LayoutCampaginBinding.inflate(LayoutInflater.from(getContext()), this, true);
|
||||
viewHolder = new ViewHolder();
|
||||
setOnClickListener(view -> {
|
||||
if (campaign != null) {
|
||||
if (campaign.isWLMCampaign()) {
|
||||
((MainActivity)(getContext())).showNearby();
|
||||
} else {
|
||||
Utils.handleWebUrl(getContext(), Uri.parse(campaign.getLink()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public class ViewHolder {
|
||||
public void init() {
|
||||
if (campaign != null) {
|
||||
binding.ivCampaign.setImageDrawable(
|
||||
getResources().getDrawable(R.drawable.ic_campaign));
|
||||
|
||||
binding.tvTitle.setText(campaign.getTitle());
|
||||
binding.tvDescription.setText(campaign.getDescription());
|
||||
try {
|
||||
if (campaign.isWLMCampaign()) {
|
||||
binding.tvDates.setText(
|
||||
String.format("%1s - %2s", campaign.getStartDate(),
|
||||
campaign.getEndDate()));
|
||||
} else {
|
||||
final Date startDate = CommonsDateUtil.getIso8601DateFormatShort()
|
||||
.parse(campaign.getStartDate());
|
||||
final Date endDate = CommonsDateUtil.getIso8601DateFormatShort()
|
||||
.parse(campaign.getEndDate());
|
||||
binding.tvDates.setText(String.format("%1s - %2s", DateUtil.getExtraShortDateString(startDate),
|
||||
DateUtil.getExtraShortDateString(endDate)));
|
||||
}
|
||||
} catch (final ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
121
app/src/main/java/fr/free/nrw/commons/campaigns/CampaignView.kt
Normal file
121
app/src/main/java/fr/free/nrw/commons/campaigns/CampaignView.kt
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
package fr.free.nrw.commons.campaigns
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.campaigns.models.Campaign
|
||||
import fr.free.nrw.commons.contributions.MainActivity
|
||||
import fr.free.nrw.commons.databinding.LayoutCampaginBinding
|
||||
import fr.free.nrw.commons.theme.BaseActivity
|
||||
import fr.free.nrw.commons.utils.CommonsDateUtil.getIso8601DateFormatShort
|
||||
import fr.free.nrw.commons.utils.DateUtil.getExtraShortDateString
|
||||
import fr.free.nrw.commons.utils.SwipableCardView
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showLongToast
|
||||
import timber.log.Timber
|
||||
import java.text.ParseException
|
||||
|
||||
/**
|
||||
* A view which represents a single campaign
|
||||
*/
|
||||
class CampaignView : SwipableCardView {
|
||||
private var campaign: Campaign? = null
|
||||
private var binding: LayoutCampaginBinding? = null
|
||||
private var viewHolder: ViewHolder? = null
|
||||
private var campaignPreference = CAMPAIGNS_DEFAULT_PREFERENCE
|
||||
|
||||
constructor(context: Context) : super(context) {
|
||||
init()
|
||||
}
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
|
||||
init()
|
||||
}
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
|
||||
context, attrs, defStyleAttr) {
|
||||
init()
|
||||
}
|
||||
|
||||
fun setCampaign(campaign: Campaign?) {
|
||||
this.campaign = campaign
|
||||
if (campaign != null) {
|
||||
if (campaign.isWLMCampaign) {
|
||||
campaignPreference = WLM_CARD_PREFERENCE
|
||||
}
|
||||
visibility = VISIBLE
|
||||
viewHolder!!.init()
|
||||
} else {
|
||||
visibility = GONE
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSwipe(view: View): Boolean {
|
||||
view.visibility = GONE
|
||||
(context as BaseActivity).defaultKvStore.putBoolean(CAMPAIGNS_DEFAULT_PREFERENCE, false)
|
||||
showLongToast(
|
||||
context,
|
||||
resources.getString(R.string.nearby_campaign_dismiss_message)
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun init() {
|
||||
binding = LayoutCampaginBinding.inflate(
|
||||
LayoutInflater.from(context), this, true
|
||||
)
|
||||
viewHolder = ViewHolder()
|
||||
setOnClickListener {
|
||||
campaign?.let {
|
||||
if (it.isWLMCampaign) {
|
||||
((context) as MainActivity).showNearby()
|
||||
} else {
|
||||
Utils.handleWebUrl(context, Uri.parse(it.link))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class ViewHolder {
|
||||
fun init() {
|
||||
if (campaign != null) {
|
||||
binding!!.ivCampaign.setImageDrawable(
|
||||
ContextCompat.getDrawable(binding!!.root.context, R.drawable.ic_campaign)
|
||||
)
|
||||
binding!!.tvTitle.text = campaign!!.title
|
||||
binding!!.tvDescription.text = campaign!!.description
|
||||
try {
|
||||
if (campaign!!.isWLMCampaign) {
|
||||
binding!!.tvDates.text = String.format(
|
||||
"%1s - %2s", campaign!!.startDate,
|
||||
campaign!!.endDate
|
||||
)
|
||||
} else {
|
||||
val startDate = getIso8601DateFormatShort().parse(
|
||||
campaign?.startDate
|
||||
)
|
||||
val endDate = getIso8601DateFormatShort().parse(
|
||||
campaign?.endDate
|
||||
)
|
||||
binding!!.tvDates.text = String.format(
|
||||
"%1s - %2s", getExtraShortDateString(
|
||||
startDate!!
|
||||
), getExtraShortDateString(endDate!!)
|
||||
)
|
||||
}
|
||||
} catch (e: ParseException) {
|
||||
Timber.e(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CAMPAIGNS_DEFAULT_PREFERENCE: String = "displayCampaignsCardView"
|
||||
const val WLM_CARD_PREFERENCE: String = "displayWLMCardView"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
package fr.free.nrw.commons.campaigns;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
import fr.free.nrw.commons.campaigns.models.Campaign;
|
||||
import fr.free.nrw.commons.utils.CommonsDateUtil;
|
||||
import java.text.ParseException;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import fr.free.nrw.commons.BasePresenter;
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
|
||||
import io.reactivex.Scheduler;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.SingleObserver;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static fr.free.nrw.commons.di.CommonsApplicationModule.IO_THREAD;
|
||||
import static fr.free.nrw.commons.di.CommonsApplicationModule.MAIN_THREAD;
|
||||
|
||||
/**
|
||||
* The presenter for the campaigns view, fetches the campaigns from the api and informs the view on
|
||||
* success and error
|
||||
*/
|
||||
@Singleton
|
||||
public class CampaignsPresenter implements BasePresenter<ICampaignsView> {
|
||||
private final OkHttpJsonApiClient okHttpJsonApiClient;
|
||||
private final Scheduler mainThreadScheduler;
|
||||
private final Scheduler ioScheduler;
|
||||
|
||||
private ICampaignsView view;
|
||||
private Disposable disposable;
|
||||
private Campaign campaign;
|
||||
|
||||
@Inject
|
||||
public CampaignsPresenter(OkHttpJsonApiClient okHttpJsonApiClient, @Named(IO_THREAD)Scheduler ioScheduler, @Named(MAIN_THREAD)Scheduler mainThreadScheduler) {
|
||||
this.okHttpJsonApiClient = okHttpJsonApiClient;
|
||||
this.mainThreadScheduler=mainThreadScheduler;
|
||||
this.ioScheduler=ioScheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachView(ICampaignsView view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
@Override public void onDetachView() {
|
||||
this.view = null;
|
||||
if (disposable != null) {
|
||||
disposable.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* make the api call to fetch the campaigns
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
public void getCampaigns() {
|
||||
if (view != null && okHttpJsonApiClient != null) {
|
||||
//If we already have a campaign, lets not make another call
|
||||
if (this.campaign != null) {
|
||||
view.showCampaigns(campaign);
|
||||
return;
|
||||
}
|
||||
Single<CampaignResponseDTO> campaigns = okHttpJsonApiClient.getCampaigns();
|
||||
campaigns.observeOn(mainThreadScheduler)
|
||||
.subscribeOn(ioScheduler)
|
||||
.subscribeWith(new SingleObserver<CampaignResponseDTO>() {
|
||||
|
||||
@Override public void onSubscribe(Disposable d) {
|
||||
disposable = d;
|
||||
}
|
||||
|
||||
@Override public void onSuccess(CampaignResponseDTO campaignResponseDTO) {
|
||||
List<Campaign> campaigns = campaignResponseDTO.getCampaigns();
|
||||
if (campaigns == null || campaigns.isEmpty()) {
|
||||
Timber.e("The campaigns list is empty");
|
||||
view.showCampaigns(null);
|
||||
return;
|
||||
}
|
||||
Collections.sort(campaigns, (campaign, t1) -> {
|
||||
Date date1, date2;
|
||||
try {
|
||||
|
||||
date1 = CommonsDateUtil.getIso8601DateFormatShort().parse(campaign.getStartDate());
|
||||
date2 = CommonsDateUtil.getIso8601DateFormatShort().parse(t1.getStartDate());
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
return date1.compareTo(date2);
|
||||
});
|
||||
Date campaignEndDate, campaignStartDate;
|
||||
Date currentDate = new Date();
|
||||
try {
|
||||
for (Campaign aCampaign : campaigns) {
|
||||
campaignEndDate = CommonsDateUtil.getIso8601DateFormatShort().parse(aCampaign.getEndDate());
|
||||
campaignStartDate = CommonsDateUtil.getIso8601DateFormatShort().parse(aCampaign.getStartDate());
|
||||
if (campaignEndDate.compareTo(currentDate) >= 0
|
||||
&& campaignStartDate.compareTo(currentDate) <= 0) {
|
||||
campaign = aCampaign;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
view.showCampaigns(campaign);
|
||||
}
|
||||
|
||||
@Override public void onError(Throwable e) {
|
||||
Timber.e(e, "could not fetch campaigns");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
package fr.free.nrw.commons.campaigns
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import fr.free.nrw.commons.BasePresenter
|
||||
import fr.free.nrw.commons.campaigns.models.Campaign
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule.IO_THREAD
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule.MAIN_THREAD
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
|
||||
import fr.free.nrw.commons.utils.CommonsDateUtil.getIso8601DateFormatShort
|
||||
import io.reactivex.Scheduler
|
||||
import io.reactivex.disposables.Disposable
|
||||
import timber.log.Timber
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* The presenter for the campaigns view, fetches the campaigns from the api and informs the view on
|
||||
* success and error
|
||||
*/
|
||||
@Singleton
|
||||
class CampaignsPresenter @Inject constructor(
|
||||
private val okHttpJsonApiClient: OkHttpJsonApiClient?,
|
||||
@param:Named(IO_THREAD) private val ioScheduler: Scheduler,
|
||||
@param:Named(MAIN_THREAD) private val mainThreadScheduler: Scheduler
|
||||
) : BasePresenter<ICampaignsView?> {
|
||||
private var view: ICampaignsView? = null
|
||||
private var disposable: Disposable? = null
|
||||
private var campaign: Campaign? = null
|
||||
|
||||
override fun onAttachView(view: ICampaignsView) {
|
||||
this.view = view
|
||||
}
|
||||
|
||||
override fun onDetachView() {
|
||||
view = null
|
||||
disposable?.dispose()
|
||||
}
|
||||
|
||||
/**
|
||||
* make the api call to fetch the campaigns
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
fun getCampaigns() {
|
||||
if (view != null && okHttpJsonApiClient != null) {
|
||||
//If we already have a campaign, lets not make another call
|
||||
if (campaign != null) {
|
||||
view!!.showCampaigns(campaign)
|
||||
return
|
||||
}
|
||||
|
||||
okHttpJsonApiClient.campaigns
|
||||
.observeOn(mainThreadScheduler)
|
||||
.subscribeOn(ioScheduler)
|
||||
.doOnSubscribe { disposable = it }
|
||||
.subscribe({ campaignResponseDTO ->
|
||||
val campaigns = campaignResponseDTO.campaigns?.toMutableList()
|
||||
if (campaigns.isNullOrEmpty()) {
|
||||
Timber.e("The campaigns list is empty")
|
||||
view!!.showCampaigns(null)
|
||||
} else {
|
||||
sortCampaignsByStartDate(campaigns)
|
||||
campaign = findActiveCampaign(campaigns)
|
||||
view!!.showCampaigns(campaign)
|
||||
}
|
||||
}, {
|
||||
Timber.e(it, "could not fetch campaigns")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun sortCampaignsByStartDate(campaigns: MutableList<Campaign>) {
|
||||
val dateFormat: SimpleDateFormat = getIso8601DateFormatShort()
|
||||
campaigns.sortWith(Comparator { campaign: Campaign, other: Campaign ->
|
||||
val date1: Date?
|
||||
val date2: Date?
|
||||
try {
|
||||
date1 = campaign.startDate?.let { dateFormat.parse(it) }
|
||||
date2 = other.startDate?.let { dateFormat.parse(it) }
|
||||
} catch (e: ParseException) {
|
||||
Timber.e(e)
|
||||
return@Comparator -1
|
||||
}
|
||||
if (date1 != null && date2 != null) date1.compareTo(date2) else -1
|
||||
})
|
||||
}
|
||||
|
||||
private fun findActiveCampaign(campaigns: List<Campaign>) : Campaign? {
|
||||
val dateFormat: SimpleDateFormat = getIso8601DateFormatShort()
|
||||
val currentDate = Date()
|
||||
return try {
|
||||
campaigns.firstOrNull {
|
||||
val campaignStartDate = it.startDate?.let { s -> dateFormat.parse(s) }
|
||||
val campaignEndDate = it.endDate?.let { s -> dateFormat.parse(s) }
|
||||
campaignStartDate != null && campaignEndDate != null &&
|
||||
campaignEndDate >= currentDate && campaignStartDate <= currentDate
|
||||
}
|
||||
} catch (e: ParseException) {
|
||||
Timber.e(e, "could not find active campaign")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
package fr.free.nrw.commons.campaigns;
|
||||
|
||||
import fr.free.nrw.commons.MvpView;
|
||||
import fr.free.nrw.commons.campaigns.models.Campaign;
|
||||
|
||||
/**
|
||||
* Interface which defines the view contracts of the campaign view
|
||||
*/
|
||||
public interface ICampaignsView extends MvpView {
|
||||
void showCampaigns(Campaign campaign);
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package fr.free.nrw.commons.campaigns
|
||||
|
||||
import fr.free.nrw.commons.MvpView
|
||||
import fr.free.nrw.commons.campaigns.models.Campaign
|
||||
|
||||
/**
|
||||
* Interface which defines the view contracts of the campaign view
|
||||
*/
|
||||
interface ICampaignsView : MvpView {
|
||||
fun showCampaigns(campaign: Campaign?)
|
||||
}
|
||||
|
|
@ -20,25 +20,24 @@ import kotlin.collections.ArrayList
|
|||
|
||||
class CampaignsPresenterTest {
|
||||
@Mock
|
||||
lateinit var okHttpJsonApiClient: OkHttpJsonApiClient
|
||||
|
||||
lateinit var campaignsPresenter: CampaignsPresenter
|
||||
private lateinit var okHttpJsonApiClient: OkHttpJsonApiClient
|
||||
|
||||
@Mock
|
||||
internal lateinit var view: ICampaignsView
|
||||
private lateinit var view: ICampaignsView
|
||||
|
||||
@Mock
|
||||
internal lateinit var campaignResponseDTO: CampaignResponseDTO
|
||||
lateinit var campaignsSingle: Single<CampaignResponseDTO>
|
||||
private lateinit var campaignResponseDTO: CampaignResponseDTO
|
||||
|
||||
@Mock
|
||||
lateinit var campaign: Campaign
|
||||
|
||||
lateinit var testScheduler: TestScheduler
|
||||
private lateinit var campaign: Campaign
|
||||
|
||||
@Mock
|
||||
private lateinit var disposable: Disposable
|
||||
|
||||
private lateinit var campaignsPresenter: CampaignsPresenter
|
||||
private lateinit var campaignsSingle: Single<CampaignResponseDTO>
|
||||
private lateinit var testScheduler: TestScheduler
|
||||
|
||||
/**
|
||||
* initial setup, test environment
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue