Skip to content

Commit 1b01c65

Browse files
ashishkumar468misaochan
authored andcommitted
Show campaigns (commons-app#2113)
* Show campaigns * Added a ui util class SwipableCardView which passes the onSwipe event to its children * NearbyCardView & CampaignView extend SwipableCardView * Fetch campaigns in ContributionsFragment * Added an option to enable disable campaign in Settings/Preferences * synced strings with master * removed duplicate initialsation of CampaignPresenter
1 parent 707c52c commit 1b01c65

22 files changed

+608
-47
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package fr.free.nrw.commons;
2+
3+
/**
4+
* Base presenter, enforcing contracts to atach and detach view
5+
*/
6+
public interface BasePresenter {
7+
/**
8+
* Until a view is attached, it is open to listen events from the presenter
9+
*/
10+
void onAttachView(MvpView view);
11+
12+
/**
13+
* Detaching a view makes sure that the view no more receives events from the presenter
14+
*/
15+
void onDetachView();
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package fr.free.nrw.commons;
2+
3+
/**
4+
* Base interface for all the views
5+
*/
6+
public interface MvpView {
7+
void showMessage(String message);
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package fr.free.nrw.commons.campaigns;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
5+
/**
6+
* A data class to hold a campaign
7+
*/
8+
public class Campaign {
9+
10+
@SerializedName("title") private String title;
11+
@SerializedName("description") private String description;
12+
@SerializedName("startDate") private String startDate;
13+
@SerializedName("endDate") private String endDate;
14+
@SerializedName("link") private String link;
15+
16+
public String getTitle() {
17+
return title;
18+
}
19+
20+
public void setTitle(String title) {
21+
this.title = title;
22+
}
23+
24+
public String getDescription() {
25+
return description;
26+
}
27+
28+
public void setDescription(String description) {
29+
this.description = description;
30+
}
31+
32+
public String getStartDate() {
33+
return startDate;
34+
}
35+
36+
public void setStartDate(String startDate) {
37+
this.startDate = startDate;
38+
}
39+
40+
public String getEndDate() {
41+
return endDate;
42+
}
43+
44+
public void setEndDate(String endDate) {
45+
this.endDate = endDate;
46+
}
47+
48+
public String getLink() {
49+
return link;
50+
}
51+
52+
public void setLink(String link) {
53+
this.link = link;
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package fr.free.nrw.commons.campaigns;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
5+
/**
6+
* A data class to hold the campaign configs
7+
*/
8+
class CampaignConfig {
9+
10+
@SerializedName("showOnlyLiveCampaigns") private boolean showOnlyLiveCampaigns;
11+
@SerializedName("sortBy") private String sortBy;
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package fr.free.nrw.commons.campaigns;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
import java.util.List;
5+
6+
/**
7+
* Data class to hold the response from the campaigns api
8+
*/
9+
public class CampaignResponseDTO {
10+
11+
@SerializedName("config")
12+
private CampaignConfig campaignConfig;
13+
14+
@SerializedName("campaigns")
15+
private List<Campaign> campaigns;
16+
17+
public CampaignConfig getCampaignConfig() {
18+
return campaignConfig;
19+
}
20+
21+
public List<Campaign> getCampaigns() {
22+
return campaigns;
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package fr.free.nrw.commons.campaigns;
2+
3+
import android.content.Context;
4+
import android.content.Intent;
5+
import android.net.Uri;
6+
import android.support.annotation.NonNull;
7+
import android.support.annotation.Nullable;
8+
import android.util.AttributeSet;
9+
import android.view.View;
10+
import android.widget.TextView;
11+
import butterknife.BindView;
12+
import butterknife.ButterKnife;
13+
import fr.free.nrw.commons.R;
14+
import fr.free.nrw.commons.contributions.MainActivity;
15+
import fr.free.nrw.commons.utils.SwipableCardView;
16+
import fr.free.nrw.commons.utils.ViewUtil;
17+
import java.text.ParseException;
18+
import java.text.SimpleDateFormat;
19+
import java.util.Date;
20+
21+
/**
22+
* A view which represents a single campaign
23+
*/
24+
public class CampaignView extends SwipableCardView {
25+
Campaign campaign = null;
26+
private ViewHolder viewHolder;
27+
28+
public CampaignView(@NonNull Context context) {
29+
super(context);
30+
init();
31+
}
32+
33+
public CampaignView(@NonNull Context context, @Nullable AttributeSet attrs) {
34+
super(context, attrs);
35+
init();
36+
}
37+
38+
public CampaignView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
39+
super(context, attrs, defStyleAttr);
40+
init();
41+
}
42+
43+
public void setCampaign(Campaign campaign) {
44+
this.campaign = campaign;
45+
if (campaign != null) {
46+
this.setVisibility(View.VISIBLE);
47+
viewHolder.init();
48+
} else {
49+
this.setVisibility(View.GONE);
50+
}
51+
}
52+
53+
@Override public boolean onSwipe(View view) {
54+
view.setVisibility(View.GONE);
55+
((MainActivity) getContext()).prefs.edit()
56+
.putBoolean("displayCampaignsCardView", false)
57+
.apply();
58+
ViewUtil.showLongToast(getContext(),
59+
getResources().getString(R.string.nearby_campaign_dismiss_message));
60+
return true;
61+
}
62+
63+
private void init() {
64+
View rootView = inflate(getContext(), R.layout.layout_campagin, this);
65+
viewHolder = new ViewHolder(rootView);
66+
setOnClickListener(view -> {
67+
if (campaign != null) {
68+
showCampaignInBrowser(campaign.getLink());
69+
}
70+
});
71+
}
72+
73+
/**
74+
* open the url associated with the campaign in the system's default browser
75+
*/
76+
private void showCampaignInBrowser(String link) {
77+
Intent view = new Intent();
78+
view.setAction(Intent.ACTION_VIEW);
79+
view.setData(Uri.parse(link));
80+
getContext().startActivity(view);
81+
}
82+
83+
public class ViewHolder {
84+
85+
@BindView(R.id.tv_title) TextView tvTitle;
86+
@BindView(R.id.tv_description) TextView tvDescription;
87+
@BindView(R.id.tv_dates) TextView tvDates;
88+
89+
public ViewHolder(View itemView) {
90+
ButterKnife.bind(this, itemView);
91+
}
92+
93+
public void init() {
94+
if (campaign != null) {
95+
tvTitle.setText(campaign.getTitle());
96+
tvDescription.setText(campaign.getDescription());
97+
SimpleDateFormat inputDateFormat = new SimpleDateFormat("yyyy-MM-dd");
98+
SimpleDateFormat outputDateFormat = new SimpleDateFormat("dd MMM");
99+
try {
100+
Date startDate = inputDateFormat.parse(campaign.getStartDate());
101+
Date endDate = inputDateFormat.parse(campaign.getEndDate());
102+
tvDates.setText(String.format("%1s - %2s", outputDateFormat.format(startDate),
103+
outputDateFormat.format(endDate)));
104+
} catch (ParseException e) {
105+
e.printStackTrace();
106+
}
107+
}
108+
}
109+
}
110+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package fr.free.nrw.commons.campaigns;
2+
3+
import android.util.Log;
4+
import fr.free.nrw.commons.BasePresenter;
5+
import fr.free.nrw.commons.MvpView;
6+
import fr.free.nrw.commons.mwapi.MediaWikiApi;
7+
import io.reactivex.Single;
8+
import io.reactivex.SingleObserver;
9+
import io.reactivex.android.schedulers.AndroidSchedulers;
10+
import io.reactivex.disposables.Disposable;
11+
import io.reactivex.schedulers.Schedulers;
12+
import java.text.ParseException;
13+
import java.text.SimpleDateFormat;
14+
import java.util.Collections;
15+
import java.util.Date;
16+
import java.util.List;
17+
18+
/**
19+
* The presenter for the campaigns view, fetches the campaigns from the api and informs the view on
20+
* success and error
21+
*/
22+
public class CampaignsPresenter implements BasePresenter {
23+
private final String TAG = "#CampaignsPresenter#";
24+
private ICampaignsView view;
25+
private MediaWikiApi mediaWikiApi;
26+
private Disposable disposable;
27+
private Campaign campaign;
28+
29+
@Override public void onAttachView(MvpView view) {
30+
this.view = (ICampaignsView) view;
31+
this.mediaWikiApi = ((ICampaignsView) view).getMediaWikiApi();
32+
}
33+
34+
@Override public void onDetachView() {
35+
this.view = null;
36+
disposable.dispose();
37+
}
38+
39+
/**
40+
* make the api call to fetch the campaigns
41+
*/
42+
public void getCampaigns() {
43+
if (view != null && mediaWikiApi != null) {
44+
//If we already have a campaign, lets not make another call
45+
if (this.campaign != null) {
46+
view.showCampaigns(campaign);
47+
return;
48+
}
49+
Single<CampaignResponseDTO> campaigns = mediaWikiApi.getCampaigns();
50+
campaigns.observeOn(AndroidSchedulers.mainThread())
51+
.subscribeOn(Schedulers.io())
52+
.subscribeWith(new SingleObserver<CampaignResponseDTO>() {
53+
54+
@Override public void onSubscribe(Disposable d) {
55+
disposable = d;
56+
}
57+
58+
@Override public void onSuccess(CampaignResponseDTO campaignResponseDTO) {
59+
List<Campaign> campaigns = campaignResponseDTO.getCampaigns();
60+
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
61+
if (campaigns == null || campaigns.isEmpty()) {
62+
Log.e(TAG, "The campaigns list is empty");
63+
view.showCampaigns(null);
64+
}
65+
Collections.sort(campaigns, (campaign, t1) -> {
66+
Date date1, date2;
67+
try {
68+
date1 = dateFormat.parse(campaign.getStartDate());
69+
date2 = dateFormat.parse(t1.getStartDate());
70+
} catch (ParseException e) {
71+
e.printStackTrace();
72+
return -1;
73+
}
74+
return date1.compareTo(date2);
75+
});
76+
Date campaignEndDate = null;
77+
try {
78+
campaignEndDate = dateFormat.parse(campaigns.get(0).getEndDate());
79+
} catch (ParseException e) {
80+
e.printStackTrace();
81+
}
82+
if (campaignEndDate == null) {
83+
view.showCampaigns(null);
84+
} else if (campaignEndDate.compareTo(new Date()) > 0) {
85+
campaign = campaigns.get(0);
86+
view.showCampaigns(campaign);
87+
} else {
88+
Log.e(TAG, "The campaigns has already finished");
89+
view.showCampaigns(null);
90+
}
91+
}
92+
93+
@Override public void onError(Throwable e) {
94+
Log.e(TAG, "could not fetch campaigns: " + e.getMessage());
95+
}
96+
});
97+
}
98+
}
99+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package fr.free.nrw.commons.campaigns;
2+
3+
import fr.free.nrw.commons.MvpView;
4+
import fr.free.nrw.commons.mwapi.MediaWikiApi;
5+
6+
/**
7+
* Interface which defines the view contracts of the campaign view
8+
*/
9+
public interface ICampaignsView extends MvpView {
10+
MediaWikiApi getMediaWikiApi();
11+
12+
void showCampaigns(Campaign campaign);
13+
}

0 commit comments

Comments
 (0)