Skip to content

Commit 0269894

Browse files
authored
Fix 4615: Option for editing caption and description (#4672)
* DescriptionEditHelper implemented * Description extracted * Description editable * No description condition handled * Code cleanup * Added javadocs * toolbar added * API call done * Caption edit available * Progress dialog added * Log * Problem with ButterKnife * Caption is editable * Removed unused import * Manifest file reverted * Manifest file reverted * Manifest file reverted * View binding added * Post operation test added * Java docs added * Java docs added * MediaDetailFragment unit tests added * Test added
1 parent e910b1d commit 0269894

20 files changed

+855
-14
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
android:requestLegacyExternalStorage = "true"
3737
tools:ignore="GoogleAppIndexingWarning">
3838

39+
<activity
40+
android:name=".description.DescriptionEditActivity"
41+
android:exported="true" />
42+
3943
<activity android:name="org.acra.dialog.CrashReportDialog"
4044
android:process=":acra"
4145
android:launchMode="singleInstance"

app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,9 @@ class MediaDataExtractor @Inject constructor(private val mediaClient: MediaClien
5050
}
5151

5252
fun getHtmlOfPage(title: String) = mediaClient.getPageHtml(title);
53+
54+
/**
55+
* Fetches wikitext from mediaClient
56+
*/
57+
fun getCurrentWikiText(title: String) = mediaClient.getCurrentWikiText(title);
5358
}

app/src/main/java/fr/free/nrw/commons/actions/PageEditClient.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,24 @@ class PageEditClient(
6464
}
6565
}
6666

67+
/**
68+
* Set new labels to Wikibase server of commons
69+
* @param summary Edit summary
70+
* @param title Title of the page to edit
71+
* @param language Corresponding language of label
72+
* @param value label
73+
* @return 1 when the edit was successful
74+
*/
75+
fun setCaptions(summary: String, title: String,
76+
language: String, value: String) : Observable<Int>{
77+
return try {
78+
pageEditInterface.postCaptions(summary, title, language,
79+
value, csrfTokenClient.tokenBlocking).map { it.success }
80+
} catch (throwable: Throwable) {
81+
Observable.just(0)
82+
}
83+
}
84+
6785
/**
6886
* Get whole WikiText of required file
6987
* @param title : Name of the file

app/src/main/java/fr/free/nrw/commons/actions/PageEditInterface.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import io.reactivex.Single
55
import org.wikipedia.dataclient.Service
66
import org.wikipedia.dataclient.mwapi.MwQueryResponse
77
import org.wikipedia.edit.Edit
8+
import org.wikipedia.wikidata.Entities
89
import retrofit2.http.*
910

1011
/**
@@ -73,6 +74,18 @@ interface PageEditInterface {
7374
@Field("token") token: String
7475
): Observable<Edit>
7576

77+
78+
@FormUrlEncoded
79+
@Headers("Cache-Control: no-cache")
80+
@POST(Service.MW_API_PREFIX + "action=wbsetlabel&format=json&site=commonswiki&formatversion=2")
81+
fun postCaptions(
82+
@Field("summary") summary: String,
83+
@Field("title") title: String,
84+
@Field("language") language: String,
85+
@Field("value") value: String,
86+
@Field("token") token: String
87+
): Observable<Entities>
88+
7689
/**
7790
* Get wiki text for provided file names
7891
* @param titles : Name of the file
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
package fr.free.nrw.commons.description
2+
3+
import android.app.ProgressDialog
4+
import android.content.Intent
5+
import android.os.Bundle
6+
import android.os.Parcelable
7+
import android.view.View
8+
import androidx.appcompat.app.AppCompatActivity
9+
import androidx.recyclerview.widget.LinearLayoutManager
10+
import androidx.recyclerview.widget.RecyclerView
11+
import fr.free.nrw.commons.R
12+
import fr.free.nrw.commons.databinding.ActivityDescriptionEditBinding
13+
import fr.free.nrw.commons.description.EditDescriptionConstants.LIST_OF_DESCRIPTION_AND_CAPTION
14+
import fr.free.nrw.commons.description.EditDescriptionConstants.UPDATED_WIKITEXT
15+
import fr.free.nrw.commons.description.EditDescriptionConstants.WIKITEXT
16+
import fr.free.nrw.commons.kvstore.JsonKvStore
17+
import fr.free.nrw.commons.settings.Prefs
18+
import fr.free.nrw.commons.upload.UploadMediaDetail
19+
import fr.free.nrw.commons.upload.UploadMediaDetailAdapter
20+
import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog
21+
import java.util.*
22+
import javax.inject.Inject
23+
import javax.inject.Named
24+
25+
/**
26+
* Activity for populating and editing existing description and caption
27+
*/
28+
class DescriptionEditActivity : AppCompatActivity(), UploadMediaDetailAdapter.EventListener {
29+
/**
30+
* Adapter for showing UploadMediaDetail in the activity
31+
*/
32+
private lateinit var uploadMediaDetailAdapter: UploadMediaDetailAdapter
33+
34+
/**
35+
* For getting default preference
36+
*/
37+
@JvmField
38+
@Inject
39+
@Named("default_preferences")
40+
var defaultKvStore: JsonKvStore? = null
41+
42+
/**
43+
* Recyclerview for recycling data in views
44+
*/
45+
@JvmField
46+
var rvDescriptions: RecyclerView? = null
47+
48+
/**
49+
* Current wikitext
50+
*/
51+
var wikiText: String? = null
52+
53+
/**
54+
* For showing progress dialog
55+
*/
56+
private var progressDialog: ProgressDialog? = null
57+
58+
private lateinit var binding: ActivityDescriptionEditBinding
59+
60+
override fun onCreate(savedInstanceState: Bundle?) {
61+
super.onCreate(savedInstanceState)
62+
63+
binding = ActivityDescriptionEditBinding.inflate(layoutInflater)
64+
setContentView(binding.root)
65+
66+
val bundle = intent.extras
67+
val descriptionAndCaptions: ArrayList<UploadMediaDetail> =
68+
bundle!!.getParcelableArrayList(LIST_OF_DESCRIPTION_AND_CAPTION)!!
69+
wikiText = bundle.getString(WIKITEXT)
70+
initRecyclerView(descriptionAndCaptions)
71+
72+
binding.btnAddDescription.setOnClickListener(::onButtonAddDescriptionClicked)
73+
binding.btnEditSubmit.setOnClickListener(::onSubmitButtonClicked)
74+
binding.toolbarBackButton.setOnClickListener(::onBackButtonClicked)
75+
}
76+
77+
/**
78+
* Initializes the RecyclerView
79+
* @param descriptionAndCaptions list of description and caption
80+
*/
81+
private fun initRecyclerView(descriptionAndCaptions: ArrayList<UploadMediaDetail>?) {
82+
uploadMediaDetailAdapter = UploadMediaDetailAdapter(
83+
defaultKvStore?.getString(Prefs.DESCRIPTION_LANGUAGE, ""),
84+
descriptionAndCaptions)
85+
uploadMediaDetailAdapter.setCallback { titleStringID: Int, messageStringId: Int ->
86+
showInfoAlert(
87+
titleStringID,
88+
messageStringId
89+
)
90+
}
91+
uploadMediaDetailAdapter.setEventListener(this)
92+
rvDescriptions = binding.rvDescriptionsCaptions
93+
rvDescriptions!!.layoutManager = LinearLayoutManager(this)
94+
rvDescriptions!!.adapter = uploadMediaDetailAdapter
95+
}
96+
97+
/**
98+
* show dialog with info
99+
* @param titleStringID Title ID
100+
* @param messageStringId Message ID
101+
*/
102+
private fun showInfoAlert(titleStringID: Int, messageStringId: Int) {
103+
showAlertDialog(
104+
this, getString(titleStringID),
105+
getString(messageStringId), getString(android.R.string.ok),
106+
null, true
107+
)
108+
}
109+
110+
override fun onPrimaryCaptionTextChange(isNotEmpty: Boolean) {}
111+
112+
private fun onBackButtonClicked(view: View) {
113+
onBackPressed()
114+
}
115+
116+
private fun onButtonAddDescriptionClicked(view: View) {
117+
val uploadMediaDetail = UploadMediaDetail()
118+
uploadMediaDetail.isManuallyAdded = true //This was manually added by the user
119+
uploadMediaDetailAdapter.addDescription(uploadMediaDetail)
120+
rvDescriptions!!.smoothScrollToPosition(uploadMediaDetailAdapter.itemCount - 1)
121+
}
122+
123+
private fun onSubmitButtonClicked(view: View) {
124+
showLoggingProgressBar()
125+
val uploadMediaDetails = uploadMediaDetailAdapter.items
126+
updateDescription(uploadMediaDetails)
127+
finish()
128+
}
129+
130+
/**
131+
* Updates newly added descriptions in the wikiText and send to calling fragment
132+
* @param uploadMediaDetails descriptions and captions
133+
*/
134+
private fun updateDescription(uploadMediaDetails: List<UploadMediaDetail?>) {
135+
var descriptionIndex = wikiText!!.indexOf("description=")
136+
if (descriptionIndex == -1) {
137+
descriptionIndex = wikiText!!.indexOf("Description=")
138+
}
139+
val buffer = StringBuilder()
140+
if (descriptionIndex != -1) {
141+
val descriptionStart = wikiText!!.substring(0, descriptionIndex + 12)
142+
val descriptionToEnd = wikiText!!.substring(descriptionIndex + 12)
143+
val descriptionEndIndex = descriptionToEnd.indexOf("\n")
144+
val descriptionEnd = wikiText!!.substring(
145+
descriptionStart.length
146+
+ descriptionEndIndex
147+
)
148+
buffer.append(descriptionStart)
149+
for (i in uploadMediaDetails.indices) {
150+
val uploadDetails = uploadMediaDetails[i]
151+
if (uploadDetails!!.descriptionText != "") {
152+
buffer.append("{{")
153+
buffer.append(uploadDetails.languageCode)
154+
buffer.append("|1=")
155+
buffer.append(uploadDetails.descriptionText)
156+
buffer.append("}}, ")
157+
}
158+
}
159+
buffer.deleteCharAt(buffer.length - 1)
160+
buffer.deleteCharAt(buffer.length - 1)
161+
buffer.append(descriptionEnd)
162+
}
163+
val returningIntent = Intent()
164+
returningIntent.putExtra(UPDATED_WIKITEXT, buffer.toString())
165+
returningIntent.putParcelableArrayListExtra(
166+
LIST_OF_DESCRIPTION_AND_CAPTION,
167+
uploadMediaDetails as ArrayList<out Parcelable?>
168+
)
169+
setResult(RESULT_OK, returningIntent)
170+
finish()
171+
}
172+
173+
private fun showLoggingProgressBar() {
174+
progressDialog = ProgressDialog(this)
175+
progressDialog!!.isIndeterminate = true
176+
progressDialog!!.setTitle(getString(R.string.updating_caption_title))
177+
progressDialog!!.setMessage(getString(R.string.updating_caption_message))
178+
progressDialog!!.setCanceledOnTouchOutside(false)
179+
progressDialog!!.show()
180+
}
181+
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package fr.free.nrw.commons.description;
2+
3+
import static fr.free.nrw.commons.notification.NotificationHelper.NOTIFICATION_EDIT_DESCRIPTION;
4+
5+
import android.content.Context;
6+
import android.content.Intent;
7+
import android.net.Uri;
8+
import fr.free.nrw.commons.BuildConfig;
9+
import fr.free.nrw.commons.Media;
10+
import fr.free.nrw.commons.R;
11+
import fr.free.nrw.commons.actions.PageEditClient;
12+
import fr.free.nrw.commons.notification.NotificationHelper;
13+
import io.reactivex.Single;
14+
import java.util.Objects;
15+
import javax.inject.Inject;
16+
import javax.inject.Named;
17+
import timber.log.Timber;
18+
19+
/**
20+
* Helper class for edit and update given descriptions and showing notification upgradation
21+
*/
22+
public class DescriptionEditHelper {
23+
24+
/**
25+
* notificationHelper: helps creating notification
26+
*/
27+
private final NotificationHelper notificationHelper;
28+
/**
29+
* * pageEditClient: methods provided by this member posts the edited descriptions
30+
* to the Media wiki api
31+
*/
32+
public final PageEditClient pageEditClient;
33+
34+
@Inject
35+
public DescriptionEditHelper(final NotificationHelper notificationHelper,
36+
@Named("commons-page-edit") final PageEditClient pageEditClient) {
37+
this.notificationHelper = notificationHelper;
38+
this.pageEditClient = pageEditClient;
39+
}
40+
41+
/**
42+
* Replaces new descriptions
43+
*
44+
* @param context context
45+
* @param media to be added
46+
* @param appendText to be added
47+
* @return Observable<Boolean>
48+
*/
49+
public Single<Boolean> addDescription(final Context context, final Media media,
50+
final String appendText) {
51+
Timber.d("thread is description adding %s", Thread.currentThread().getName());
52+
final String summary = "Updating Description";
53+
54+
return pageEditClient.edit(Objects.requireNonNull(media.getFilename()),
55+
appendText, summary)
56+
.flatMapSingle(result -> Single.just(showDescriptionEditNotification(context,
57+
media, result)))
58+
.firstOrError();
59+
}
60+
61+
/**
62+
* Adds new captions
63+
*
64+
* @param context context
65+
* @param media to be added
66+
* @param language to be added
67+
* @param value to be added
68+
* @return Observable<Boolean>
69+
*/
70+
public Single<Boolean> addCaption(final Context context, final Media media,
71+
final String language, final String value) {
72+
Timber.d("thread is caption adding %s", Thread.currentThread().getName());
73+
final String summary = "Updating Caption";
74+
75+
return pageEditClient.setCaptions(summary, Objects.requireNonNull(media.getFilename()),
76+
language, value)
77+
.flatMapSingle(result -> Single.just(showCaptionEditNotification(context,
78+
media, result)))
79+
.firstOrError();
80+
}
81+
82+
/**
83+
* Update captions and shows notification about captions update
84+
* @param context to be added
85+
* @param media to be added
86+
* @param result to be added
87+
* @return boolean
88+
*/
89+
private boolean showCaptionEditNotification(final Context context, final Media media,
90+
final int result) {
91+
final String message;
92+
String title = context.getString(R.string.caption_edit_helper_show_edit_title);
93+
94+
if (result == 1) {
95+
title += ": " + context
96+
.getString(R.string.coordinates_edit_helper_show_edit_title_success);
97+
message = context.getString(R.string.caption_edit_helper_show_edit_message);
98+
} else {
99+
title += ": " + context.getString(R.string.caption_edit_helper_show_edit_title);
100+
message = context.getString(R.string.caption_edit_helper_edit_message_else) ;
101+
}
102+
103+
final String urlForFile = BuildConfig.COMMONS_URL + "/wiki/" + media.getFilename();
104+
final Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(urlForFile));
105+
notificationHelper.showNotification(context, title, message, NOTIFICATION_EDIT_DESCRIPTION,
106+
browserIntent);
107+
return result == 1;
108+
}
109+
110+
/**
111+
* Update descriptions and shows notification about descriptions update
112+
* @param context to be added
113+
* @param media to be added
114+
* @param result to be added
115+
* @return boolean
116+
*/
117+
private boolean showDescriptionEditNotification(final Context context, final Media media,
118+
final boolean result) {
119+
final String message;
120+
String title = context.getString(R.string.description_edit_helper_show_edit_title);
121+
122+
if (result) {
123+
title += ": " + context
124+
.getString(R.string.coordinates_edit_helper_show_edit_title_success);
125+
message = context.getString(R.string.description_edit_helper_show_edit_message);
126+
} else {
127+
title += ": " + context.getString(R.string.description_edit_helper_show_edit_title);
128+
message = context.getString(R.string.description_edit_helper_edit_message_else) ;
129+
}
130+
131+
final String urlForFile = BuildConfig.COMMONS_URL + "/wiki/" + media.getFilename();
132+
final Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(urlForFile));
133+
notificationHelper.showNotification(context, title, message, NOTIFICATION_EDIT_DESCRIPTION,
134+
browserIntent);
135+
return result;
136+
}
137+
}

0 commit comments

Comments
 (0)