mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 20:33:53 +01:00
Convert wikidata/mwapi to kotlin (part 3) (#6004)
* Convert Edit to kotlin along with deleting unused class * Converted ExtMetadata to kotlin * Convert ImageInfo to kotlin * Removed unused class * Convert Notification to kotlin * Convert PageProperties to kotlin * Convert PageTitle to kotlin * Convert Namespace to kotlin
This commit is contained in:
parent
64354fb9e4
commit
015c5d5c63
22 changed files with 832 additions and 1133 deletions
|
|
@ -11,7 +11,6 @@ import fr.free.nrw.commons.wikidata.model.Entities
|
|||
import fr.free.nrw.commons.wikidata.model.gallery.ExtMetadata
|
||||
import fr.free.nrw.commons.wikidata.model.gallery.ImageInfo
|
||||
import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.text.ParseException
|
||||
import java.util.Date
|
||||
import javax.inject.Inject
|
||||
|
|
@ -24,7 +23,7 @@ class MediaConverter
|
|||
entity: Entities.Entity,
|
||||
imageInfo: ImageInfo,
|
||||
): Media {
|
||||
val metadata = imageInfo.metadata
|
||||
val metadata = imageInfo.getMetadata()
|
||||
requireNotNull(metadata) { "No metadata" }
|
||||
// Stores mapping of title attribute to hidden attribute of each category
|
||||
val myMap = mutableMapOf<String, Boolean>()
|
||||
|
|
@ -32,8 +31,8 @@ class MediaConverter
|
|||
|
||||
return Media(
|
||||
page.pageId().toString(),
|
||||
imageInfo.thumbUrl.takeIf { it.isNotBlank() } ?: imageInfo.originalUrl,
|
||||
imageInfo.originalUrl,
|
||||
imageInfo.getThumbUrl().takeIf { it.isNotBlank() } ?: imageInfo.getOriginalUrl(),
|
||||
imageInfo.getOriginalUrl(),
|
||||
page.title(),
|
||||
metadata.imageDescription(),
|
||||
safeParseDate(metadata.dateTime()),
|
||||
|
|
@ -41,7 +40,7 @@ class MediaConverter
|
|||
metadata.prefixedLicenseUrl,
|
||||
getAuthor(metadata),
|
||||
getAuthor(metadata),
|
||||
MediaDataExtractorUtil.extractCategoriesFromList(metadata.categories),
|
||||
MediaDataExtractorUtil.extractCategoriesFromList(metadata.categories()),
|
||||
metadata.latLng,
|
||||
entity.labels().mapValues { it.value.value() },
|
||||
entity.descriptions().mapValues { it.value.value() },
|
||||
|
|
@ -104,9 +103,5 @@ private val ExtMetadata.prefixedLicenseUrl: String
|
|||
}
|
||||
|
||||
private val ExtMetadata.latLng: LatLng?
|
||||
get() =
|
||||
if (!StringUtils.isBlank(gpsLatitude) && !StringUtils.isBlank(gpsLongitude)) {
|
||||
LatLng(gpsLatitude.toDouble(), gpsLongitude.toDouble(), 0.0f)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
get() = LatLng.latLongOrNull(gpsLatitude(), gpsLongitude())
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,13 @@ data class LatLng(
|
|||
* Accepts a non-null [Location] and converts it to a [LatLng].
|
||||
*/
|
||||
companion object {
|
||||
fun latLongOrNull(latitude: String?, longitude: String?): LatLng? =
|
||||
if (!latitude.isNullOrBlank() && !longitude.isNullOrBlank()) {
|
||||
LatLng(latitude.toDouble(), longitude.toDouble(), 0.0f)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the latitude and longitude of a given non-null location
|
||||
* @param location the non-null location of the user
|
||||
|
|
|
|||
|
|
@ -64,8 +64,8 @@ class NotificationClient
|
|||
return Notification(
|
||||
notificationType = notificationType,
|
||||
notificationText = notificationText,
|
||||
date = DateUtil.getMonthOnlyDateString(timestamp),
|
||||
link = contents?.links?.primary?.url ?: "",
|
||||
date = DateUtil.getMonthOnlyDateString(getTimestamp()),
|
||||
link = contents?.links?.getPrimary()?.url ?: "",
|
||||
iconUrl = "",
|
||||
notificationId = id().toString(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
package fr.free.nrw.commons.wikidata.model.edit;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import fr.free.nrw.commons.wikidata.mwapi.MwPostResponse;
|
||||
|
||||
public class Edit extends MwPostResponse {
|
||||
@Nullable private Result edit;
|
||||
|
||||
@Nullable public Result edit() {
|
||||
return edit;
|
||||
}
|
||||
|
||||
public class Result {
|
||||
@Nullable private String result;
|
||||
@Nullable private String code;
|
||||
@Nullable private String info;
|
||||
@Nullable private String warning;
|
||||
|
||||
public boolean editSucceeded() {
|
||||
return "Success".equals(result);
|
||||
}
|
||||
|
||||
@Nullable public String code() {
|
||||
return code;
|
||||
}
|
||||
|
||||
@Nullable public String info() {
|
||||
return info;
|
||||
}
|
||||
|
||||
@Nullable public String warning() {
|
||||
return warning;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package fr.free.nrw.commons.wikidata.model.edit
|
||||
|
||||
import fr.free.nrw.commons.wikidata.mwapi.MwPostResponse
|
||||
|
||||
class Edit : MwPostResponse() {
|
||||
private val edit: Result? = null
|
||||
|
||||
fun edit(): Result? = edit
|
||||
|
||||
class Result {
|
||||
private val result: String? = null
|
||||
private val code: String? = null
|
||||
private val info: String? = null
|
||||
private val warning: String? = null
|
||||
|
||||
fun editSucceeded(): Boolean =
|
||||
"Success" == result
|
||||
|
||||
fun code(): String? = code
|
||||
|
||||
fun info(): String? = info
|
||||
|
||||
fun warning(): String? = warning
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package fr.free.nrw.commons.wikidata.model.edit;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import fr.free.nrw.commons.wikidata.model.BaseModel;
|
||||
|
||||
public abstract class EditResult extends BaseModel implements Parcelable {
|
||||
private final String result;
|
||||
|
||||
public EditResult(String result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
protected EditResult(Parcel in) {
|
||||
this.result = in.readString();
|
||||
}
|
||||
|
||||
public String getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
package fr.free.nrw.commons.wikidata.model.gallery;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
|
||||
public class ExtMetadata {
|
||||
@SerializedName("DateTime") @Nullable private Values dateTime;
|
||||
@SerializedName("ObjectName") @Nullable private Values objectName;
|
||||
@SerializedName("CommonsMetadataExtension") @Nullable private Values commonsMetadataExtension;
|
||||
@SerializedName("Categories") @Nullable private Values categories;
|
||||
@SerializedName("Assessments") @Nullable private Values assessments;
|
||||
@SerializedName("GPSLatitude") @Nullable private Values gpsLatitude;
|
||||
@SerializedName("GPSLongitude") @Nullable private Values gpsLongitude;
|
||||
@SerializedName("ImageDescription") @Nullable private Values imageDescription;
|
||||
@SerializedName("DateTimeOriginal") @Nullable private Values dateTimeOriginal;
|
||||
@SerializedName("Artist") @Nullable private Values artist;
|
||||
@SerializedName("Credit") @Nullable private Values credit;
|
||||
@SerializedName("Permission") @Nullable private Values permission;
|
||||
@SerializedName("AuthorCount") @Nullable private Values authorCount;
|
||||
@SerializedName("LicenseShortName") @Nullable private Values licenseShortName;
|
||||
@SerializedName("UsageTerms") @Nullable private Values usageTerms;
|
||||
@SerializedName("LicenseUrl") @Nullable private Values licenseUrl;
|
||||
@SerializedName("AttributionRequired") @Nullable private Values attributionRequired;
|
||||
@SerializedName("Copyrighted") @Nullable private Values copyrighted;
|
||||
@SerializedName("Restrictions") @Nullable private Values restrictions;
|
||||
@SerializedName("License") @Nullable private Values license;
|
||||
|
||||
@NonNull public String licenseShortName() {
|
||||
return StringUtils.defaultString(licenseShortName == null ? null : licenseShortName.value());
|
||||
}
|
||||
|
||||
@NonNull public String licenseUrl() {
|
||||
return StringUtils.defaultString(licenseUrl == null ? null : licenseUrl.value());
|
||||
}
|
||||
|
||||
@NonNull public String license() {
|
||||
return StringUtils.defaultString(license == null ? null : license.value());
|
||||
}
|
||||
|
||||
@NonNull public String imageDescription() {
|
||||
return StringUtils.defaultString(imageDescription == null ? null : imageDescription.value());
|
||||
}
|
||||
|
||||
@NonNull public String imageDescriptionSource() {
|
||||
return StringUtils.defaultString(imageDescription == null ? null : imageDescription.source());
|
||||
}
|
||||
|
||||
@NonNull public String objectName() {
|
||||
return StringUtils.defaultString(objectName == null ? null : objectName.value());
|
||||
}
|
||||
|
||||
@NonNull public String usageTerms() {
|
||||
return StringUtils.defaultString(usageTerms == null ? null : usageTerms.value());
|
||||
}
|
||||
|
||||
@NonNull public String dateTimeOriginal() {
|
||||
return StringUtils.defaultString(dateTimeOriginal == null ? null : dateTimeOriginal.value());
|
||||
}
|
||||
|
||||
@NonNull public String dateTime() {
|
||||
return StringUtils.defaultString(dateTime == null ? null : dateTime.value());
|
||||
}
|
||||
|
||||
@NonNull public String artist() {
|
||||
return StringUtils.defaultString(artist == null ? null : artist.value());
|
||||
}
|
||||
|
||||
@NonNull public String getCategories() {
|
||||
return StringUtils.defaultString(categories == null ? null : categories.value());
|
||||
}
|
||||
|
||||
@NonNull public String getGpsLatitude() {
|
||||
return StringUtils.defaultString(gpsLatitude == null ? null : gpsLatitude.value());
|
||||
}
|
||||
|
||||
@NonNull public String getGpsLongitude() {
|
||||
return StringUtils.defaultString(gpsLongitude == null ? null : gpsLongitude.value());
|
||||
}
|
||||
|
||||
@NonNull public String credit() {
|
||||
return StringUtils.defaultString(credit == null ? null : credit.value());
|
||||
}
|
||||
|
||||
public class Values {
|
||||
@Nullable private String value;
|
||||
@Nullable private String source;
|
||||
@Nullable private String hidden;
|
||||
|
||||
@Nullable public String value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable public String source() {
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package fr.free.nrw.commons.wikidata.model.gallery
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
||||
class ExtMetadata {
|
||||
@SerializedName("DateTime") private val dateTime: Values? = null
|
||||
@SerializedName("ObjectName") private val objectName: Values? = null
|
||||
@SerializedName("CommonsMetadataExtension") private val commonsMetadataExtension: Values? = null
|
||||
@SerializedName("Categories") private val categories: Values? = null
|
||||
@SerializedName("Assessments") private val assessments: Values? = null
|
||||
@SerializedName("GPSLatitude") private val gpsLatitude: Values? = null
|
||||
@SerializedName("GPSLongitude") private val gpsLongitude: Values? = null
|
||||
@SerializedName("ImageDescription") private val imageDescription: Values? = null
|
||||
@SerializedName("DateTimeOriginal") private val dateTimeOriginal: Values? = null
|
||||
@SerializedName("Artist") private val artist: Values? = null
|
||||
@SerializedName("Credit") private val credit: Values? = null
|
||||
@SerializedName("Permission") private val permission: Values? = null
|
||||
@SerializedName("AuthorCount") private val authorCount: Values? = null
|
||||
@SerializedName("LicenseShortName") private val licenseShortName: Values? = null
|
||||
@SerializedName("UsageTerms") private val usageTerms: Values? = null
|
||||
@SerializedName("LicenseUrl") private val licenseUrl: Values? = null
|
||||
@SerializedName("AttributionRequired") private val attributionRequired: Values? = null
|
||||
@SerializedName("Copyrighted") private val copyrighted: Values? = null
|
||||
@SerializedName("Restrictions") private val restrictions: Values? = null
|
||||
@SerializedName("License") private val license: Values? = null
|
||||
|
||||
fun licenseShortName(): String = licenseShortName?.value ?: ""
|
||||
|
||||
fun licenseUrl(): String = licenseUrl?.value ?: ""
|
||||
|
||||
fun license(): String = license?.value ?: ""
|
||||
|
||||
fun imageDescription(): String = imageDescription?.value ?: ""
|
||||
|
||||
fun imageDescriptionSource(): String = imageDescription?.source ?: ""
|
||||
|
||||
fun objectName(): String = objectName?.value ?: ""
|
||||
|
||||
fun usageTerms(): String = usageTerms?.value ?: ""
|
||||
|
||||
fun dateTimeOriginal(): String = dateTimeOriginal?.value ?: ""
|
||||
|
||||
fun dateTime(): String = dateTime?.value ?: ""
|
||||
|
||||
fun artist(): String = artist?.value ?: ""
|
||||
|
||||
fun categories(): String = categories?.value ?: ""
|
||||
|
||||
fun gpsLatitude(): String = gpsLatitude?.value ?: ""
|
||||
|
||||
fun gpsLongitude(): String = gpsLongitude?.value ?: ""
|
||||
|
||||
fun credit(): String = credit?.value ?: ""
|
||||
|
||||
class Values {
|
||||
val value: String? = null
|
||||
val source: String? = null
|
||||
val hidden: String? = null
|
||||
}
|
||||
}
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
package fr.free.nrw.commons.wikidata.model.gallery;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Gson POJO for a standard image info object as returned by the API ImageInfo module
|
||||
*/
|
||||
|
||||
public class ImageInfo implements Serializable {
|
||||
private int size;
|
||||
private int width;
|
||||
private int height;
|
||||
@Nullable private String source;
|
||||
@SerializedName("thumburl") @Nullable private String thumbUrl;
|
||||
@SerializedName("thumbwidth") private int thumbWidth;
|
||||
@SerializedName("thumbheight") private int thumbHeight;
|
||||
@SerializedName("url") @Nullable private String originalUrl;
|
||||
@SerializedName("descriptionurl") @Nullable private String descriptionUrl;
|
||||
@SerializedName("descriptionshorturl") @Nullable private String descriptionShortUrl;
|
||||
@SerializedName("mime") @Nullable private String mimeType;
|
||||
@SerializedName("extmetadata")@Nullable private ExtMetadata metadata;
|
||||
@Nullable private String user;
|
||||
@Nullable private String timestamp;
|
||||
|
||||
/**
|
||||
* Query width, default width parameter of the API query in pixels.
|
||||
*/
|
||||
final private static int QUERY_WIDTH = 640;
|
||||
|
||||
/**
|
||||
* Threshold height, the minimum height of the image in pixels.
|
||||
*/
|
||||
final private static int THRESHOLD_HEIGHT = 220;
|
||||
|
||||
@NonNull
|
||||
public String getSource() {
|
||||
return StringUtils.defaultString(source);
|
||||
}
|
||||
|
||||
public void setSource(@Nullable String source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the thumbnail width.
|
||||
* @return
|
||||
*/
|
||||
public int getThumbWidth() { return thumbWidth; }
|
||||
|
||||
/**
|
||||
* Get the thumbnail height.
|
||||
* @return
|
||||
*/
|
||||
public int getThumbHeight() { return thumbHeight; }
|
||||
|
||||
@NonNull public String getMimeType() {
|
||||
return StringUtils.defaultString(mimeType, "*/*");
|
||||
}
|
||||
|
||||
@NonNull public String getThumbUrl() {
|
||||
updateThumbUrl();
|
||||
return StringUtils.defaultString(thumbUrl);
|
||||
}
|
||||
|
||||
@NonNull public String getOriginalUrl() {
|
||||
return StringUtils.defaultString(originalUrl);
|
||||
}
|
||||
|
||||
@NonNull public String getUser() {
|
||||
return StringUtils.defaultString(user);
|
||||
}
|
||||
|
||||
@NonNull public String getTimestamp() {
|
||||
return StringUtils.defaultString(timestamp);
|
||||
}
|
||||
|
||||
@Nullable public ExtMetadata getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the ThumbUrl if image dimensions are not sufficient.
|
||||
* Specifically, in panoramic images the height retrieved is less than required due to large width to height ratio,
|
||||
* so we update the thumb url keeping a minimum height threshold.
|
||||
*/
|
||||
private void updateThumbUrl() {
|
||||
// If thumbHeight retrieved from API is less than THRESHOLD_HEIGHT
|
||||
if(getThumbHeight() < THRESHOLD_HEIGHT){
|
||||
// If thumbWidthRetrieved is same as queried width ( If not tells us that the image has no larger dimensions. )
|
||||
if(getThumbWidth() == QUERY_WIDTH){
|
||||
// Calculate new width depending on the aspect ratio.
|
||||
final int finalWidth = (int)(THRESHOLD_HEIGHT * getThumbWidth() * 1.0 / getThumbHeight());
|
||||
thumbHeight = THRESHOLD_HEIGHT;
|
||||
thumbWidth = finalWidth;
|
||||
final String toReplace = "/" + QUERY_WIDTH + "px";
|
||||
final int position = thumbUrl.lastIndexOf(toReplace);
|
||||
thumbUrl = (new StringBuilder(thumbUrl)).replace(position, position + toReplace.length(), "/" + thumbWidth + "px").toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
package fr.free.nrw.commons.wikidata.model.gallery
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.io.Serializable
|
||||
|
||||
/**
|
||||
* Gson POJO for a standard image info object as returned by the API ImageInfo module
|
||||
*/
|
||||
open class ImageInfo : Serializable {
|
||||
private val size = 0
|
||||
private val width = 0
|
||||
private val height = 0
|
||||
private var source: String? = null
|
||||
|
||||
@SerializedName("thumburl")
|
||||
private var thumbUrl: String? = null
|
||||
|
||||
@SerializedName("thumbwidth")
|
||||
private var thumbWidth = 0
|
||||
|
||||
@SerializedName("thumbheight")
|
||||
private var thumbHeight = 0
|
||||
|
||||
@SerializedName("url")
|
||||
private val originalUrl: String? = null
|
||||
|
||||
@SerializedName("descriptionurl")
|
||||
private val descriptionUrl: String? = null
|
||||
|
||||
@SerializedName("descriptionshorturl")
|
||||
private val descriptionShortUrl: String? = null
|
||||
|
||||
@SerializedName("mime")
|
||||
private val mimeType: String? = null
|
||||
|
||||
@SerializedName("extmetadata")
|
||||
private val metadata: ExtMetadata? = null
|
||||
private val user: String? = null
|
||||
private val timestamp: String? = null
|
||||
|
||||
fun getSource(): String {
|
||||
return source ?: ""
|
||||
}
|
||||
|
||||
fun setSource(source: String?) {
|
||||
this.source = source
|
||||
}
|
||||
|
||||
fun getSize(): Int {
|
||||
return size
|
||||
}
|
||||
|
||||
fun getWidth(): Int {
|
||||
return width
|
||||
}
|
||||
|
||||
fun getHeight(): Int {
|
||||
return height
|
||||
}
|
||||
|
||||
fun getThumbWidth(): Int {
|
||||
return thumbWidth
|
||||
}
|
||||
|
||||
fun getThumbHeight(): Int {
|
||||
return thumbHeight
|
||||
}
|
||||
|
||||
fun getMimeType(): String {
|
||||
return mimeType ?: "*/*"
|
||||
}
|
||||
|
||||
fun getThumbUrl(): String {
|
||||
updateThumbUrl()
|
||||
return thumbUrl ?: ""
|
||||
}
|
||||
|
||||
fun getOriginalUrl(): String {
|
||||
return originalUrl ?: ""
|
||||
}
|
||||
|
||||
fun getUser(): String {
|
||||
return user ?: ""
|
||||
}
|
||||
|
||||
fun getTimestamp(): String {
|
||||
return timestamp ?: ""
|
||||
}
|
||||
|
||||
fun getMetadata(): ExtMetadata? = metadata
|
||||
|
||||
/**
|
||||
* Updates the ThumbUrl if image dimensions are not sufficient. Specifically, in panoramic
|
||||
* images the height retrieved is less than required due to large width to height ratio, so we
|
||||
* update the thumb url keeping a minimum height threshold.
|
||||
*/
|
||||
private fun updateThumbUrl() {
|
||||
// If thumbHeight retrieved from API is less than THRESHOLD_HEIGHT
|
||||
if (getThumbHeight() < THRESHOLD_HEIGHT) {
|
||||
// If thumbWidthRetrieved is same as queried width ( If not tells us that the image has no larger dimensions. )
|
||||
if (getThumbWidth() == QUERY_WIDTH) {
|
||||
// Calculate new width depending on the aspect ratio.
|
||||
val finalWidth = (THRESHOLD_HEIGHT * getThumbWidth() * 1.0
|
||||
/ getThumbHeight()).toInt()
|
||||
thumbHeight = THRESHOLD_HEIGHT
|
||||
thumbWidth = finalWidth
|
||||
val toReplace = "/" + QUERY_WIDTH + "px"
|
||||
val position = thumbUrl!!.lastIndexOf(toReplace)
|
||||
thumbUrl = (StringBuilder(thumbUrl ?: "")).replace(
|
||||
position,
|
||||
position + toReplace.length, "/" + thumbWidth + "px"
|
||||
).toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Query width, default width parameter of the API query in pixels.
|
||||
*/
|
||||
private const val QUERY_WIDTH = 640
|
||||
|
||||
/**
|
||||
* Threshold height, the minimum height of the image in pixels.
|
||||
*/
|
||||
private const val THRESHOLD_HEIGHT = 220
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
package fr.free.nrw.commons.wikidata.model.gallery;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Gson POJO for a standard video info object as returned by the API VideoInfo module
|
||||
*/
|
||||
public class VideoInfo extends ImageInfo {
|
||||
@Nullable private List<String> codecs;
|
||||
@SuppressWarnings("unused,NullableProblems") @Nullable private String name;
|
||||
@SuppressWarnings("unused,NullableProblems") @Nullable @SerializedName("short_name") private String shortName;
|
||||
}
|
||||
|
|
@ -1,190 +0,0 @@
|
|||
package fr.free.nrw.commons.wikidata.model.notifications;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import fr.free.nrw.commons.utils.DateUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import fr.free.nrw.commons.wikidata.GsonUtil;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class Notification {
|
||||
@Nullable private String wiki;
|
||||
private long id;
|
||||
@Nullable private String type;
|
||||
@Nullable private String category;
|
||||
|
||||
@Nullable private Title title;
|
||||
@Nullable private Timestamp timestamp;
|
||||
@SerializedName("*") @Nullable private Contents contents;
|
||||
|
||||
@NonNull public String wiki() {
|
||||
return StringUtils.defaultString(wiki);
|
||||
}
|
||||
|
||||
public long id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public long key() {
|
||||
return id + wiki().hashCode();
|
||||
}
|
||||
|
||||
@NonNull public String type() {
|
||||
return StringUtils.defaultString(type);
|
||||
}
|
||||
|
||||
@Nullable public Title title() {
|
||||
return title;
|
||||
}
|
||||
|
||||
@Nullable public Contents getContents() {
|
||||
return contents;
|
||||
}
|
||||
|
||||
public void setContents(@Nullable final Contents contents) {
|
||||
this.contents = contents;
|
||||
}
|
||||
|
||||
@NonNull public Date getTimestamp() {
|
||||
return timestamp != null ? timestamp.date() : new Date();
|
||||
}
|
||||
|
||||
public void setTimestamp(@Nullable final Timestamp timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
@NonNull String getUtcIso8601() {
|
||||
return StringUtils.defaultString(timestamp != null ? timestamp.utciso8601 : null);
|
||||
}
|
||||
|
||||
public boolean isFromWikidata() {
|
||||
return wiki().equals("wikidatawiki");
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return Long.toString(id);
|
||||
}
|
||||
|
||||
public static class Title {
|
||||
@Nullable private String full;
|
||||
@Nullable private String text;
|
||||
|
||||
@NonNull public String text() {
|
||||
return StringUtils.defaultString(text);
|
||||
}
|
||||
|
||||
@NonNull public String full() {
|
||||
return StringUtils.defaultString(full);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Timestamp {
|
||||
@Nullable private String utciso8601;
|
||||
|
||||
public void setUtciso8601(@Nullable final String utciso8601) {
|
||||
this.utciso8601 = utciso8601;
|
||||
}
|
||||
|
||||
public Date date() {
|
||||
try {
|
||||
return DateUtil.iso8601DateParse(utciso8601);
|
||||
} catch (ParseException e) {
|
||||
Timber.e(e);
|
||||
return new Date();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Link {
|
||||
@Nullable private String url;
|
||||
@Nullable private String label;
|
||||
@Nullable private String tooltip;
|
||||
@Nullable private String description;
|
||||
@Nullable private String icon;
|
||||
|
||||
@NonNull public String getUrl() {
|
||||
return StringUtils.defaultString(url);
|
||||
}
|
||||
|
||||
public void setUrl(@Nullable final String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@NonNull public String getTooltip() {
|
||||
return StringUtils.defaultString(tooltip);
|
||||
}
|
||||
|
||||
@NonNull public String getLabel() {
|
||||
return StringUtils.defaultString(label);
|
||||
}
|
||||
|
||||
@NonNull public String getIcon() {
|
||||
return StringUtils.defaultString(icon);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Links {
|
||||
@Nullable private JsonElement primary;
|
||||
private Link primaryLink;
|
||||
|
||||
public void setPrimary(@Nullable final JsonElement primary) {
|
||||
this.primary = primary;
|
||||
}
|
||||
|
||||
@Nullable public Link getPrimary() {
|
||||
if (primary == null) {
|
||||
return null;
|
||||
}
|
||||
if (primaryLink == null && primary instanceof JsonObject) {
|
||||
primaryLink = GsonUtil.INSTANCE.getDefaultGson().fromJson(primary, Link.class);
|
||||
}
|
||||
return primaryLink;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Contents {
|
||||
@Nullable private String header;
|
||||
@Nullable private String compactHeader;
|
||||
@Nullable private String body;
|
||||
@Nullable private String icon;
|
||||
@Nullable private Links links;
|
||||
|
||||
@NonNull public String getHeader() {
|
||||
return StringUtils.defaultString(header);
|
||||
}
|
||||
|
||||
@NonNull public String getCompactHeader() {
|
||||
return StringUtils.defaultString(compactHeader);
|
||||
}
|
||||
|
||||
public void setCompactHeader(@Nullable final String compactHeader) {
|
||||
this.compactHeader = compactHeader;
|
||||
}
|
||||
|
||||
@NonNull public String getBody() {
|
||||
return StringUtils.defaultString(body);
|
||||
}
|
||||
|
||||
@Nullable public Links getLinks() {
|
||||
return links;
|
||||
}
|
||||
|
||||
public void setLinks(@Nullable final Links links) {
|
||||
this.links = links;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
package fr.free.nrw.commons.wikidata.model.notifications
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import fr.free.nrw.commons.utils.DateUtil.iso8601DateParse
|
||||
import fr.free.nrw.commons.wikidata.GsonUtil.defaultGson
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import timber.log.Timber
|
||||
import java.text.ParseException
|
||||
import java.util.Date
|
||||
|
||||
class Notification {
|
||||
private val wiki: String? = null
|
||||
private var id: Long = 0
|
||||
private val type: String? = null
|
||||
private val category: String? = null
|
||||
|
||||
private val title: Title? = null
|
||||
private var timestamp: Timestamp? = null
|
||||
|
||||
@SerializedName("*")
|
||||
var contents: Contents? = null
|
||||
|
||||
fun wiki(): String = wiki ?: ""
|
||||
|
||||
fun id(): Long = id
|
||||
|
||||
fun setId(id: Long) {
|
||||
this.id = id
|
||||
}
|
||||
|
||||
fun key(): Long =
|
||||
id + wiki().hashCode()
|
||||
|
||||
fun type(): String =
|
||||
type ?: ""
|
||||
|
||||
fun title(): Title? = title
|
||||
|
||||
fun getTimestamp(): Date =
|
||||
timestamp?.date() ?: Date()
|
||||
|
||||
fun setTimestamp(timestamp: Timestamp?) {
|
||||
this.timestamp = timestamp
|
||||
}
|
||||
|
||||
val utcIso8601: String
|
||||
get() = timestamp?.utciso8601 ?: ""
|
||||
|
||||
val isFromWikidata: Boolean
|
||||
get() = wiki() == "wikidatawiki"
|
||||
|
||||
override fun toString(): String =
|
||||
id.toString()
|
||||
|
||||
class Title {
|
||||
private val full: String? = null
|
||||
private val text: String? = null
|
||||
|
||||
fun text(): String = text ?: ""
|
||||
|
||||
fun full(): String = full ?: ""
|
||||
}
|
||||
|
||||
class Timestamp {
|
||||
internal var utciso8601: String? = null
|
||||
|
||||
fun setUtciso8601(utciso8601: String?) {
|
||||
this.utciso8601 = utciso8601
|
||||
}
|
||||
|
||||
fun date(): Date {
|
||||
try {
|
||||
return iso8601DateParse(utciso8601 ?: "")
|
||||
} catch (e: ParseException) {
|
||||
Timber.e(e)
|
||||
return Date()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Link {
|
||||
var url: String? = null
|
||||
get() = field ?: ""
|
||||
val label: String? = null
|
||||
get() = field ?: ""
|
||||
val tooltip: String? = null
|
||||
get() = field ?: ""
|
||||
private val description: String? = null
|
||||
val icon: String? = null
|
||||
get() = field ?: ""
|
||||
}
|
||||
|
||||
class Links {
|
||||
private var primary: JsonElement? = null
|
||||
private var primaryLink: Link? = null
|
||||
|
||||
fun setPrimary(primary: JsonElement?) {
|
||||
this.primary = primary
|
||||
}
|
||||
|
||||
fun getPrimary(): Link? {
|
||||
if (primary == null) {
|
||||
return null
|
||||
}
|
||||
if (primaryLink == null && primary is JsonObject) {
|
||||
primaryLink = defaultGson.fromJson(primary, Link::class.java)
|
||||
}
|
||||
return primaryLink
|
||||
}
|
||||
}
|
||||
|
||||
class Contents {
|
||||
val header: String? = null
|
||||
get() = field ?: ""
|
||||
var compactHeader: String? = null
|
||||
get() = field ?: ""
|
||||
val body: String? = null
|
||||
get() = field ?: ""
|
||||
private val icon: String? = null
|
||||
var links: Links? = null
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
package fr.free.nrw.commons.wikidata.model.page;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public final class GeoMarshaller {
|
||||
@Nullable
|
||||
public static String marshal(@Nullable Location object) {
|
||||
if (object == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
JSONObject jsonObj = new JSONObject();
|
||||
try {
|
||||
jsonObj.put(GeoUnmarshaller.LATITUDE, object.getLatitude());
|
||||
jsonObj.put(GeoUnmarshaller.LONGITUDE, object.getLongitude());
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return jsonObj.toString();
|
||||
}
|
||||
|
||||
private GeoMarshaller() { }
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
package fr.free.nrw.commons.wikidata.model.page;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public final class GeoUnmarshaller {
|
||||
static final String LATITUDE = "latitude";
|
||||
static final String LONGITUDE = "longitude";
|
||||
|
||||
@Nullable
|
||||
public static Location unmarshal(@Nullable String json) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
JSONObject jsonObj;
|
||||
try {
|
||||
jsonObj = new JSONObject(json);
|
||||
} catch (JSONException e) {
|
||||
return null;
|
||||
}
|
||||
return unmarshal(jsonObj);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Location unmarshal(@NonNull JSONObject jsonObj) {
|
||||
Location ret = new Location((String) null);
|
||||
ret.setLatitude(jsonObj.optDouble(LATITUDE));
|
||||
ret.setLongitude(jsonObj.optDouble(LONGITUDE));
|
||||
return ret;
|
||||
}
|
||||
|
||||
private GeoUnmarshaller() { }
|
||||
}
|
||||
|
|
@ -1,34 +1,28 @@
|
|||
package fr.free.nrw.commons.wikidata.model.page;
|
||||
package fr.free.nrw.commons.wikidata.model.page
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import fr.free.nrw.commons.wikidata.model.EnumCode;
|
||||
import fr.free.nrw.commons.wikidata.model.EnumCodeMap;
|
||||
import fr.free.nrw.commons.wikidata.model.EnumCode
|
||||
import fr.free.nrw.commons.wikidata.model.EnumCodeMap
|
||||
|
||||
/** An enumeration describing the different possible namespace codes. Do not attempt to use this
|
||||
* class to preserve URL path information such as Talk: or User: or localization.
|
||||
* @see <a href='https://en.wikipedia.org/wiki/Wikipedia:Namespace'>Wikipedia:Namespace</a>
|
||||
* @see <a href='https://www.mediawiki.org/wiki/Extension_default_namespaces'>Extension default namespaces</a>
|
||||
* @see <a href='https://github.com/wikimedia/wikipedia-ios/blob/master/Wikipedia/Code/NSNumber+MWKTitleNamespace.h'>NSNumber+MWKTitleNamespace.h (iOS implementation)</a>
|
||||
* @see <a href='https://www.mediawiki.org/wiki/Manual:Namespace#Built-in_namespaces'>Manual:Namespace</a>
|
||||
* @see <a href='https://en.wikipedia.org/w/api.php?action=query&meta=siteinfo&siprop=namespaces|namespacealiases'>Namespaces reported by API</a>
|
||||
* class to preserve URL path information such as Talk: or User: or localization.
|
||||
*
|
||||
* @see [Wikipedia:Namespace](https://en.wikipedia.org/wiki/Wikipedia:Namespace)
|
||||
* @see [Extension default namespaces](https://www.mediawiki.org/wiki/Extension_default_namespaces)
|
||||
* @see [NSNumber+MWKTitleNamespace.h
|
||||
* @see [Manual:Namespace](https://www.mediawiki.org/wiki/Manual:Namespace.Built-in_namespaces)
|
||||
* @see [Namespaces reported by API](https://en.wikipedia.org/w/api.php?action=query&meta=siteinfo&siprop=namespaces|namespacealiases)](https://github.com/wikimedia/wikipedia-ios/blob/master/Wikipedia/Code/NSNumber+MWKTitleNamespace.h)
|
||||
*/
|
||||
public enum Namespace implements EnumCode {
|
||||
enum class Namespace(private val code: Int) : EnumCode {
|
||||
MEDIA(-2),
|
||||
SPECIAL(-1) {
|
||||
@Override
|
||||
public boolean talk() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
MAIN(0), // Main or Article
|
||||
SPECIAL(-1) { override fun talk(): Boolean = false },
|
||||
MAIN(0), // Main or Article
|
||||
TALK(1),
|
||||
USER(2),
|
||||
USER_TALK(3),
|
||||
PROJECT(4), // WP alias
|
||||
PROJECT_TALK(5), // WT alias
|
||||
FILE(6), // Image alias
|
||||
FILE_TALK(7), // Image talk alias
|
||||
PROJECT(4), // WP alias
|
||||
PROJECT_TALK(5), // WT alias
|
||||
FILE(6), // Image alias
|
||||
FILE_TALK(7), // Image talk alias
|
||||
MEDIAWIKI(8),
|
||||
MEDIAWIKI_TALK(9),
|
||||
TEMPLATE(10),
|
||||
|
|
@ -137,38 +131,20 @@ public enum Namespace implements EnumCode {
|
|||
GADGET_DEFINITION_TALK(2303),
|
||||
TOPIC(2600);
|
||||
|
||||
private static final int TALK_MASK = 0x1;
|
||||
private static final EnumCodeMap<Namespace> MAP = new EnumCodeMap<>(Namespace.class);
|
||||
override fun code(): Int = code
|
||||
|
||||
private final int code;
|
||||
fun special(): Boolean = this === SPECIAL
|
||||
|
||||
@NonNull
|
||||
public static Namespace of(int code) {
|
||||
return MAP.get(code);
|
||||
}
|
||||
fun main(): Boolean = this === MAIN
|
||||
|
||||
@Override
|
||||
public int code() {
|
||||
return code;
|
||||
}
|
||||
fun file(): Boolean = this === FILE
|
||||
|
||||
public boolean special() {
|
||||
return this == SPECIAL;
|
||||
}
|
||||
open fun talk(): Boolean = (code and TALK_MASK) == TALK_MASK
|
||||
|
||||
public boolean main() {
|
||||
return this == MAIN;
|
||||
}
|
||||
companion object {
|
||||
private const val TALK_MASK = 0x1
|
||||
private val MAP = EnumCodeMap(Namespace::class.java)
|
||||
|
||||
public boolean file() {
|
||||
return this == FILE;
|
||||
}
|
||||
|
||||
public boolean talk() {
|
||||
return (code & TALK_MASK) == TALK_MASK;
|
||||
}
|
||||
|
||||
Namespace(int code) {
|
||||
this.code = code;
|
||||
fun of(code: Int): Namespace = MAP[code]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
package fr.free.nrw.commons.wikidata.model.page;
|
||||
|
||||
import android.location.Location;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Immutable class that contains metadata associated with a PageTitle.
|
||||
*/
|
||||
public class PageProperties implements Parcelable {
|
||||
private final int pageId;
|
||||
@NonNull private final Namespace namespace;
|
||||
private final long revisionId;
|
||||
private final Date lastModified;
|
||||
private final String displayTitleText;
|
||||
private final String editProtectionStatus;
|
||||
private final int languageCount;
|
||||
private final boolean isMainPage;
|
||||
private final boolean isDisambiguationPage;
|
||||
/** Nullable URL with no scheme. For example, foo.bar.com/ instead of http://foo.bar.com/. */
|
||||
@Nullable private final String leadImageUrl;
|
||||
@Nullable private final String leadImageName;
|
||||
@Nullable private final String titlePronunciationUrl;
|
||||
@Nullable private final Location geo;
|
||||
@Nullable private final String wikiBaseItem;
|
||||
@Nullable private final String descriptionSource;
|
||||
|
||||
/**
|
||||
* True if the user who first requested this page can edit this page
|
||||
* FIXME: This is not a true page property, since it depends on current user.
|
||||
*/
|
||||
private final boolean canEdit;
|
||||
|
||||
public int getPageId() {
|
||||
return pageId;
|
||||
}
|
||||
|
||||
public boolean isMainPage() {
|
||||
return isMainPage;
|
||||
}
|
||||
|
||||
public boolean isDisambiguationPage() {
|
||||
return isDisambiguationPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel parcel, int flags) {
|
||||
parcel.writeInt(pageId);
|
||||
parcel.writeInt(namespace.code());
|
||||
parcel.writeLong(revisionId);
|
||||
parcel.writeLong(lastModified.getTime());
|
||||
parcel.writeString(displayTitleText);
|
||||
parcel.writeString(titlePronunciationUrl);
|
||||
parcel.writeString(GeoMarshaller.marshal(geo));
|
||||
parcel.writeString(editProtectionStatus);
|
||||
parcel.writeInt(languageCount);
|
||||
parcel.writeInt(canEdit ? 1 : 0);
|
||||
parcel.writeInt(isMainPage ? 1 : 0);
|
||||
parcel.writeInt(isDisambiguationPage ? 1 : 0);
|
||||
parcel.writeString(leadImageUrl);
|
||||
parcel.writeString(leadImageName);
|
||||
parcel.writeString(wikiBaseItem);
|
||||
parcel.writeString(descriptionSource);
|
||||
}
|
||||
|
||||
private PageProperties(Parcel in) {
|
||||
pageId = in.readInt();
|
||||
namespace = Namespace.of(in.readInt());
|
||||
revisionId = in.readLong();
|
||||
lastModified = new Date(in.readLong());
|
||||
displayTitleText = in.readString();
|
||||
titlePronunciationUrl = in.readString();
|
||||
geo = GeoUnmarshaller.unmarshal(in.readString());
|
||||
editProtectionStatus = in.readString();
|
||||
languageCount = in.readInt();
|
||||
canEdit = in.readInt() == 1;
|
||||
isMainPage = in.readInt() == 1;
|
||||
isDisambiguationPage = in.readInt() == 1;
|
||||
leadImageUrl = in.readString();
|
||||
leadImageName = in.readString();
|
||||
wikiBaseItem = in.readString();
|
||||
descriptionSource = in.readString();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<PageProperties> CREATOR
|
||||
= new Parcelable.Creator<PageProperties>() {
|
||||
@Override
|
||||
public PageProperties createFromParcel(Parcel in) {
|
||||
return new PageProperties(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageProperties[] newArray(int size) {
|
||||
return new PageProperties[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PageProperties that = (PageProperties) o;
|
||||
|
||||
return pageId == that.pageId
|
||||
&& namespace == that.namespace
|
||||
&& revisionId == that.revisionId
|
||||
&& lastModified.equals(that.lastModified)
|
||||
&& displayTitleText.equals(that.displayTitleText)
|
||||
&& TextUtils.equals(titlePronunciationUrl, that.titlePronunciationUrl)
|
||||
&& (geo == that.geo || geo != null && geo.equals(that.geo))
|
||||
&& languageCount == that.languageCount
|
||||
&& canEdit == that.canEdit
|
||||
&& isMainPage == that.isMainPage
|
||||
&& isDisambiguationPage == that.isDisambiguationPage
|
||||
&& TextUtils.equals(editProtectionStatus, that.editProtectionStatus)
|
||||
&& TextUtils.equals(leadImageUrl, that.leadImageUrl)
|
||||
&& TextUtils.equals(leadImageName, that.leadImageName)
|
||||
&& TextUtils.equals(wikiBaseItem, that.wikiBaseItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = lastModified.hashCode();
|
||||
result = 31 * result + displayTitleText.hashCode();
|
||||
result = 31 * result + (titlePronunciationUrl != null ? titlePronunciationUrl.hashCode() : 0);
|
||||
result = 31 * result + (geo != null ? geo.hashCode() : 0);
|
||||
result = 31 * result + (editProtectionStatus != null ? editProtectionStatus.hashCode() : 0);
|
||||
result = 31 * result + languageCount;
|
||||
result = 31 * result + (isMainPage ? 1 : 0);
|
||||
result = 31 * result + (isDisambiguationPage ? 1 : 0);
|
||||
result = 31 * result + (leadImageUrl != null ? leadImageUrl.hashCode() : 0);
|
||||
result = 31 * result + (leadImageName != null ? leadImageName.hashCode() : 0);
|
||||
result = 31 * result + (wikiBaseItem != null ? wikiBaseItem.hashCode() : 0);
|
||||
result = 31 * result + (canEdit ? 1 : 0);
|
||||
result = 31 * result + pageId;
|
||||
result = 31 * result + namespace.code();
|
||||
result = 31 * result + (int) revisionId;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
package fr.free.nrw.commons.wikidata.model.page
|
||||
|
||||
import android.location.Location
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.text.TextUtils
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import java.util.Date
|
||||
|
||||
/**
|
||||
* Immutable class that contains metadata associated with a PageTitle.
|
||||
*/
|
||||
class PageProperties private constructor(parcel: Parcel) : Parcelable {
|
||||
val pageId: Int = parcel.readInt()
|
||||
private val namespace = Namespace.of(parcel.readInt())
|
||||
private val revisionId = parcel.readLong()
|
||||
private val lastModified = Date(parcel.readLong())
|
||||
private val displayTitleText = parcel.readString()
|
||||
private val editProtectionStatus = parcel.readString()
|
||||
private val languageCount = parcel.readInt()
|
||||
val isMainPage: Boolean = parcel.readInt() == 1
|
||||
val isDisambiguationPage: Boolean = parcel.readInt() == 1
|
||||
|
||||
/** Nullable URL with no scheme. For example, foo.bar.com/ instead of http://foo.bar.com/. */
|
||||
private val leadImageUrl = parcel.readString()
|
||||
private val leadImageName = parcel.readString()
|
||||
private val titlePronunciationUrl = parcel.readString()
|
||||
private val geo = unmarshal(parcel.readString())
|
||||
private val wikiBaseItem = parcel.readString()
|
||||
private val descriptionSource = parcel.readString()
|
||||
|
||||
/**
|
||||
* True if the user who first requested this page can edit this page
|
||||
* FIXME: This is not a true page property, since it depends on current user.
|
||||
*/
|
||||
private val canEdit = parcel.readInt() == 1
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeInt(pageId)
|
||||
parcel.writeInt(namespace.code())
|
||||
parcel.writeLong(revisionId)
|
||||
parcel.writeLong(lastModified.time)
|
||||
parcel.writeString(displayTitleText)
|
||||
parcel.writeString(titlePronunciationUrl)
|
||||
parcel.writeString(marshal(geo))
|
||||
parcel.writeString(editProtectionStatus)
|
||||
parcel.writeInt(languageCount)
|
||||
parcel.writeInt(if (canEdit) 1 else 0)
|
||||
parcel.writeInt(if (isMainPage) 1 else 0)
|
||||
parcel.writeInt(if (isDisambiguationPage) 1 else 0)
|
||||
parcel.writeString(leadImageUrl)
|
||||
parcel.writeString(leadImageName)
|
||||
parcel.writeString(wikiBaseItem)
|
||||
parcel.writeString(descriptionSource)
|
||||
}
|
||||
|
||||
override fun equals(o: Any?): Boolean {
|
||||
if (this === o) {
|
||||
return true
|
||||
}
|
||||
if (o == null || javaClass != o.javaClass) {
|
||||
return false
|
||||
}
|
||||
|
||||
val that = o as PageProperties
|
||||
|
||||
return pageId == that.pageId &&
|
||||
namespace === that.namespace &&
|
||||
revisionId == that.revisionId &&
|
||||
lastModified == that.lastModified &&
|
||||
displayTitleText == that.displayTitleText &&
|
||||
TextUtils.equals(titlePronunciationUrl, that.titlePronunciationUrl) &&
|
||||
(geo === that.geo || geo != null && geo == that.geo) &&
|
||||
languageCount == that.languageCount &&
|
||||
canEdit == that.canEdit &&
|
||||
isMainPage == that.isMainPage &&
|
||||
isDisambiguationPage == that.isDisambiguationPage &&
|
||||
TextUtils.equals(editProtectionStatus, that.editProtectionStatus) &&
|
||||
TextUtils.equals(leadImageUrl, that.leadImageUrl) &&
|
||||
TextUtils.equals(leadImageName, that.leadImageName) &&
|
||||
TextUtils.equals(wikiBaseItem, that.wikiBaseItem)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = lastModified.hashCode()
|
||||
result = 31 * result + displayTitleText.hashCode()
|
||||
result = 31 * result + (titlePronunciationUrl?.hashCode() ?: 0)
|
||||
result = 31 * result + (geo?.hashCode() ?: 0)
|
||||
result = 31 * result + (editProtectionStatus?.hashCode() ?: 0)
|
||||
result = 31 * result + languageCount
|
||||
result = 31 * result + (if (isMainPage) 1 else 0)
|
||||
result = 31 * result + (if (isDisambiguationPage) 1 else 0)
|
||||
result = 31 * result + (leadImageUrl?.hashCode() ?: 0)
|
||||
result = 31 * result + (leadImageName?.hashCode() ?: 0)
|
||||
result = 31 * result + (wikiBaseItem?.hashCode() ?: 0)
|
||||
result = 31 * result + (if (canEdit) 1 else 0)
|
||||
result = 31 * result + pageId
|
||||
result = 31 * result + namespace.code()
|
||||
result = 31 * result + revisionId.toInt()
|
||||
return result
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val CREATOR: Parcelable.Creator<PageProperties> = object : Parcelable.Creator<PageProperties> {
|
||||
override fun createFromParcel(parcel: Parcel): PageProperties {
|
||||
return PageProperties(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<PageProperties?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val LATITUDE: String = "latitude"
|
||||
private const val LONGITUDE: String = "longitude"
|
||||
|
||||
private fun marshal(location: Location?): String? {
|
||||
if (location == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val jsonObj = JSONObject().apply {
|
||||
try {
|
||||
put(LATITUDE, location.latitude)
|
||||
put(LONGITUDE, location.longitude)
|
||||
} catch (e: JSONException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
|
||||
return jsonObj.toString()
|
||||
}
|
||||
|
||||
private fun unmarshal(json: String?): Location? {
|
||||
if (json == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return try {
|
||||
val jsonObject = JSONObject(json)
|
||||
Location(null as String?).apply {
|
||||
latitude = jsonObject.optDouble(LATITUDE)
|
||||
longitude = jsonObject.optDouble(LONGITUDE)
|
||||
}
|
||||
} catch (e: JSONException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
@ -1,339 +0,0 @@
|
|||
package fr.free.nrw.commons.wikidata.model.page;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import fr.free.nrw.commons.wikidata.model.WikiSite;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.text.Normalizer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Represents certain vital information about a page, including the title, namespace,
|
||||
* and fragment (section anchor target). It can also contain a thumbnail URL for the
|
||||
* page, and a short description retrieved from Wikidata.
|
||||
*
|
||||
* WARNING: This class is not immutable! Specifically, the thumbnail URL and the Wikidata
|
||||
* description can be altered after construction. Therefore do NOT rely on all the fields
|
||||
* of a PageTitle to remain constant for the lifetime of the object.
|
||||
*/
|
||||
public class PageTitle implements Parcelable {
|
||||
|
||||
public static final Parcelable.Creator<PageTitle> CREATOR
|
||||
= new Parcelable.Creator<PageTitle>() {
|
||||
@Override
|
||||
public PageTitle createFromParcel(Parcel in) {
|
||||
return new PageTitle(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageTitle[] newArray(int size) {
|
||||
return new PageTitle[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The localised namespace of the page as a string, or null if the page is in mainspace.
|
||||
*
|
||||
* This field contains the prefix of the page's title, as opposed to the namespace ID used by
|
||||
* MediaWiki. Therefore, mainspace pages always have a null namespace, as they have no prefix,
|
||||
* and the namespace of a page will depend on the language of the wiki the user is currently
|
||||
* looking at.
|
||||
*
|
||||
* Examples:
|
||||
* * [[Manchester]] on enwiki will have a namespace of null
|
||||
* * [[Deutschland]] on dewiki will have a namespace of null
|
||||
* * [[User:Deskana]] on enwiki will have a namespace of "User"
|
||||
* * [[Utilisateur:Deskana]] on frwiki will have a namespace of "Utilisateur", even if you got
|
||||
* to the page by going to [[User:Deskana]] and having MediaWiki automatically redirect you.
|
||||
*/
|
||||
// TODO: remove. This legacy code is the localized namespace name (File, Special, Talk, etc) but
|
||||
// isn't consistent across titles. e.g., articles with colons, such as RTÉ News: Six One,
|
||||
// are broken.
|
||||
@Nullable private final String namespace;
|
||||
@NonNull private final String text;
|
||||
@Nullable private final String fragment;
|
||||
@Nullable private String thumbUrl;
|
||||
@SerializedName("site") @NonNull private final WikiSite wiki;
|
||||
@Nullable private String description;
|
||||
@Nullable private final PageProperties properties;
|
||||
// TODO: remove after the restbase endpoint supports ZH variants.
|
||||
@Nullable private String convertedText;
|
||||
|
||||
/**
|
||||
* Creates a new PageTitle object.
|
||||
* Use this if you want to pass in a fragment portion separately from the title.
|
||||
*
|
||||
* @param prefixedText title of the page with optional namespace prefix
|
||||
* @param fragment optional fragment portion
|
||||
* @param wiki the wiki site the page belongs to
|
||||
* @return a new PageTitle object matching the given input parameters
|
||||
*/
|
||||
public static PageTitle withSeparateFragment(@NonNull String prefixedText,
|
||||
@Nullable String fragment, @NonNull WikiSite wiki) {
|
||||
if (TextUtils.isEmpty(fragment)) {
|
||||
return new PageTitle(prefixedText, wiki, null, (PageProperties) null);
|
||||
} else {
|
||||
// TODO: this class needs some refactoring to allow passing in a fragment
|
||||
// without having to do string manipulations.
|
||||
return new PageTitle(prefixedText + "#" + fragment, wiki, null, (PageProperties) null);
|
||||
}
|
||||
}
|
||||
|
||||
public PageTitle(@Nullable final String namespace, @NonNull String text, @Nullable String fragment, @Nullable String thumbUrl, @NonNull WikiSite wiki) {
|
||||
this.namespace = namespace;
|
||||
this.text = text;
|
||||
this.fragment = fragment;
|
||||
this.wiki = wiki;
|
||||
this.thumbUrl = thumbUrl;
|
||||
properties = null;
|
||||
}
|
||||
|
||||
public PageTitle(@Nullable String text, @NonNull WikiSite wiki, @Nullable String thumbUrl, @Nullable String description, @Nullable PageProperties properties) {
|
||||
this(text, wiki, thumbUrl, properties);
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public PageTitle(@Nullable String text, @NonNull WikiSite wiki, @Nullable String thumbUrl, @Nullable String description) {
|
||||
this(text, wiki, thumbUrl);
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public PageTitle(@Nullable String namespace, @NonNull String text, @NonNull WikiSite wiki) {
|
||||
this(namespace, text, null, null, wiki);
|
||||
}
|
||||
|
||||
public PageTitle(@Nullable String text, @NonNull WikiSite wiki, @Nullable String thumbUrl) {
|
||||
this(text, wiki, thumbUrl, (PageProperties) null);
|
||||
}
|
||||
|
||||
public PageTitle(@Nullable String text, @NonNull WikiSite wiki) {
|
||||
this(text, wiki, null);
|
||||
}
|
||||
|
||||
private PageTitle(@Nullable String text, @NonNull WikiSite wiki, @Nullable String thumbUrl,
|
||||
@Nullable PageProperties properties) {
|
||||
if (text == null) {
|
||||
text = "";
|
||||
}
|
||||
// FIXME: Does not handle mainspace articles with a colon in the title well at all
|
||||
String[] fragParts = text.split("#", -1);
|
||||
text = fragParts[0];
|
||||
if (fragParts.length > 1) {
|
||||
this.fragment = decodeURL(fragParts[1]).replace(" ", "_");
|
||||
} else {
|
||||
this.fragment = null;
|
||||
}
|
||||
|
||||
String[] parts = text.split(":", -1);
|
||||
if (parts.length > 1) {
|
||||
String namespaceOrLanguage = parts[0];
|
||||
if (Arrays.asList(Locale.getISOLanguages()).contains(namespaceOrLanguage)) {
|
||||
this.namespace = null;
|
||||
this.wiki = new WikiSite(wiki.authority(), namespaceOrLanguage);
|
||||
} else {
|
||||
this.wiki = wiki;
|
||||
this.namespace = namespaceOrLanguage;
|
||||
}
|
||||
this.text = TextUtils.join(":", Arrays.copyOfRange(parts, 1, parts.length));
|
||||
} else {
|
||||
this.wiki = wiki;
|
||||
this.namespace = null;
|
||||
this.text = parts[0];
|
||||
}
|
||||
|
||||
this.thumbUrl = thumbUrl;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a URL-encoded string into its UTF-8 equivalent. If the string cannot be decoded, the
|
||||
* original string is returned.
|
||||
* @param url The URL-encoded string that you wish to decode.
|
||||
* @return The decoded string, or the input string if the decoding failed.
|
||||
*/
|
||||
@NonNull private String decodeURL(@NonNull String url) {
|
||||
try {
|
||||
return URLDecoder.decode(url, "UTF-8");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Swallow IllegalArgumentException (can happen with malformed encoding), and just
|
||||
// return the original string.
|
||||
Timber.d("URL decoding failed. String was: %s", url);
|
||||
return url;
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull public WikiSite getWikiSite() {
|
||||
return wiki;
|
||||
}
|
||||
|
||||
@NonNull public String getText() {
|
||||
return text.replace(" ", "_");
|
||||
}
|
||||
|
||||
@Nullable public String getFragment() {
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Nullable public String getThumbUrl() {
|
||||
return thumbUrl;
|
||||
}
|
||||
|
||||
public void setThumbUrl(@Nullable String thumbUrl) {
|
||||
this.thumbUrl = thumbUrl;
|
||||
}
|
||||
|
||||
@Nullable public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(@Nullable String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getConvertedText() {
|
||||
return convertedText == null ? getPrefixedText() : convertedText;
|
||||
}
|
||||
|
||||
public void setConvertedText(@Nullable String convertedText) {
|
||||
this.convertedText = convertedText;
|
||||
}
|
||||
|
||||
@NonNull public String getDisplayText() {
|
||||
return getPrefixedText().replace("_", " ");
|
||||
}
|
||||
|
||||
@NonNull public String getDisplayTextWithoutNamespace() {
|
||||
return text.replace("_", " ");
|
||||
}
|
||||
|
||||
public boolean hasProperties() {
|
||||
return properties != null;
|
||||
}
|
||||
|
||||
@Nullable public PageProperties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public boolean isMainPage() {
|
||||
return properties != null && properties.isMainPage();
|
||||
}
|
||||
|
||||
public boolean isDisambiguationPage() {
|
||||
return properties != null && properties.isDisambiguationPage();
|
||||
}
|
||||
|
||||
public String getCanonicalUri() {
|
||||
return getUriForDomain(getWikiSite().authority());
|
||||
}
|
||||
|
||||
public String getMobileUri() {
|
||||
return getUriForDomain(getWikiSite().mobileAuthority());
|
||||
}
|
||||
|
||||
public String getUriForAction(String action) {
|
||||
try {
|
||||
return String.format(
|
||||
"%1$s://%2$s/w/index.php?title=%3$s&action=%4$s",
|
||||
getWikiSite().scheme(),
|
||||
getWikiSite().authority(),
|
||||
URLEncoder.encode(getPrefixedText(), "utf-8"),
|
||||
action
|
||||
);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getPrefixedText() {
|
||||
|
||||
// TODO: find a better way to check if the namespace is a ISO Alpha2 Code (two digits country code)
|
||||
return namespace == null ? getText() : addUnderscores(namespace) + ":" + getText();
|
||||
}
|
||||
|
||||
private String addUnderscores(@NonNull String text) {
|
||||
return text.replace(" ", "_");
|
||||
}
|
||||
|
||||
@Override public void writeToParcel(Parcel parcel, int flags) {
|
||||
parcel.writeString(namespace);
|
||||
parcel.writeString(text);
|
||||
parcel.writeString(fragment);
|
||||
parcel.writeParcelable(wiki, flags);
|
||||
parcel.writeParcelable(properties, flags);
|
||||
parcel.writeString(thumbUrl);
|
||||
parcel.writeString(description);
|
||||
parcel.writeString(convertedText);
|
||||
}
|
||||
|
||||
@Override public boolean equals(Object o) {
|
||||
if (!(o instanceof PageTitle)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PageTitle other = (PageTitle)o;
|
||||
// Not using namespace directly since that can be null
|
||||
return normalizedEquals(other.getPrefixedText(), getPrefixedText()) && other.wiki.equals(wiki);
|
||||
}
|
||||
|
||||
// Compare two strings based on their normalized form, using the Unicode Normalization Form C.
|
||||
// This should be used when comparing or verifying strings that will be exchanged between
|
||||
// different platforms (iOS, desktop, etc) that may encode strings using inconsistent
|
||||
// composition, especially for accents, diacritics, etc.
|
||||
private boolean normalizedEquals(@Nullable String str1, @Nullable String str2) {
|
||||
if (str1 == null || str2 == null) {
|
||||
return (str1 == null && str2 == null);
|
||||
}
|
||||
return Normalizer.normalize(str1, Normalizer.Form.NFC)
|
||||
.equals(Normalizer.normalize(str2, Normalizer.Form.NFC));
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
int result = getPrefixedText().hashCode();
|
||||
result = 31 * result + wiki.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return getPrefixedText();
|
||||
}
|
||||
|
||||
@Override public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private String getUriForDomain(String domain) {
|
||||
try {
|
||||
return String.format(
|
||||
"%1$s://%2$s/wiki/%3$s%4$s",
|
||||
getWikiSite().scheme(),
|
||||
domain,
|
||||
URLEncoder.encode(getPrefixedText(), "utf-8"),
|
||||
(this.fragment != null && this.fragment.length() > 0) ? ("#" + this.fragment) : ""
|
||||
);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private PageTitle(Parcel in) {
|
||||
namespace = in.readString();
|
||||
text = in.readString();
|
||||
fragment = in.readString();
|
||||
wiki = in.readParcelable(WikiSite.class.getClassLoader());
|
||||
properties = in.readParcelable(PageProperties.class.getClassLoader());
|
||||
thumbUrl = in.readString();
|
||||
description = in.readString();
|
||||
convertedText = in.readString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
package fr.free.nrw.commons.wikidata.model.page
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.text.TextUtils
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import fr.free.nrw.commons.wikidata.model.WikiSite
|
||||
import timber.log.Timber
|
||||
import java.io.UnsupportedEncodingException
|
||||
import java.net.URLDecoder
|
||||
import java.net.URLEncoder
|
||||
import java.text.Normalizer
|
||||
import java.util.Arrays
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Represents certain vital information about a page, including the title, namespace,
|
||||
* and fragment (section anchor target). It can also contain a thumbnail URL for the
|
||||
* page, and a short description retrieved from Wikidata.
|
||||
*
|
||||
* WARNING: This class is not immutable! Specifically, the thumbnail URL and the Wikidata
|
||||
* description can be altered after construction. Therefore do NOT rely on all the fields
|
||||
* of a PageTitle to remain constant for the lifetime of the object.
|
||||
*/
|
||||
class PageTitle : Parcelable {
|
||||
/**
|
||||
* The localised namespace of the page as a string, or null if the page is in mainspace.
|
||||
*
|
||||
* This field contains the prefix of the page's title, as opposed to the namespace ID used by
|
||||
* MediaWiki. Therefore, mainspace pages always have a null namespace, as they have no prefix,
|
||||
* and the namespace of a page will depend on the language of the wiki the user is currently
|
||||
* looking at.
|
||||
*
|
||||
* Examples:
|
||||
* * [[Manchester]] on enwiki will have a namespace of null
|
||||
* * [[Deutschland]] on dewiki will have a namespace of null
|
||||
* * [[User:Deskana]] on enwiki will have a namespace of "User"
|
||||
* * [[Utilisateur:Deskana]] on frwiki will have a namespace of "Utilisateur", even if you got
|
||||
* to the page by going to [[User:Deskana]] and having MediaWiki automatically redirect you.
|
||||
*/
|
||||
// TODO: remove. This legacy code is the localized namespace name (File, Special, Talk, etc) but
|
||||
// isn't consistent across titles. e.g., articles with colons, such as RTÉ News: Six One,
|
||||
// are broken.
|
||||
private val namespace: String?
|
||||
private val text: String
|
||||
val fragment: String?
|
||||
var thumbUrl: String?
|
||||
|
||||
@SerializedName("site")
|
||||
val wikiSite: WikiSite
|
||||
var description: String? = null
|
||||
private val properties: PageProperties?
|
||||
|
||||
// TODO: remove after the restbase endpoint supports ZH variants.
|
||||
private var convertedText: String? = null
|
||||
|
||||
constructor(namespace: String?, text: String, fragment: String?, thumbUrl: String?, wiki: WikiSite) {
|
||||
this.namespace = namespace
|
||||
this.text = text
|
||||
this.fragment = fragment
|
||||
this.thumbUrl = thumbUrl
|
||||
wikiSite = wiki
|
||||
properties = null
|
||||
}
|
||||
|
||||
constructor(text: String?, wiki: WikiSite, thumbUrl: String?, description: String?, properties: PageProperties?) : this(text, wiki, thumbUrl, properties) {
|
||||
this.description = description
|
||||
}
|
||||
|
||||
constructor(text: String?, wiki: WikiSite, thumbUrl: String?, description: String?) : this(text, wiki, thumbUrl) {
|
||||
this.description = description
|
||||
}
|
||||
|
||||
constructor(namespace: String?, text: String, wiki: WikiSite) : this(namespace, text, null, null, wiki)
|
||||
|
||||
@JvmOverloads
|
||||
constructor(text: String?, wiki: WikiSite, thumbUrl: String? = null) : this(text, wiki, thumbUrl, null as PageProperties?)
|
||||
|
||||
private constructor(input: String?, wiki: WikiSite, thumbUrl: String?, properties: PageProperties?) {
|
||||
var text = input ?: ""
|
||||
// FIXME: Does not handle mainspace articles with a colon in the title well at all
|
||||
val fragParts = text.split("#".toRegex()).toTypedArray()
|
||||
text = fragParts[0]
|
||||
fragment = if (fragParts.size > 1) {
|
||||
decodeURL(fragParts[1]).replace(" ", "_")
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val parts = text.split(":".toRegex()).toTypedArray()
|
||||
if (parts.size > 1) {
|
||||
val namespaceOrLanguage = parts[0]
|
||||
if (Arrays.asList(*Locale.getISOLanguages()).contains(namespaceOrLanguage)) {
|
||||
namespace = null
|
||||
wikiSite = WikiSite(wiki.authority(), namespaceOrLanguage)
|
||||
} else {
|
||||
wikiSite = wiki
|
||||
namespace = namespaceOrLanguage
|
||||
}
|
||||
this.text = TextUtils.join(":", Arrays.copyOfRange(parts, 1, parts.size))
|
||||
} else {
|
||||
wikiSite = wiki
|
||||
namespace = null
|
||||
this.text = parts[0]
|
||||
}
|
||||
|
||||
this.thumbUrl = thumbUrl
|
||||
this.properties = properties
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a URL-encoded string into its UTF-8 equivalent. If the string cannot be decoded, the
|
||||
* original string is returned.
|
||||
* @param url The URL-encoded string that you wish to decode.
|
||||
* @return The decoded string, or the input string if the decoding failed.
|
||||
*/
|
||||
private fun decodeURL(url: String): String {
|
||||
try {
|
||||
return URLDecoder.decode(url, "UTF-8")
|
||||
} catch (e: IllegalArgumentException) {
|
||||
// Swallow IllegalArgumentException (can happen with malformed encoding), and just
|
||||
// return the original string.
|
||||
Timber.d("URL decoding failed. String was: %s", url)
|
||||
return url
|
||||
} catch (e: UnsupportedEncodingException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTextWithoutSpaces(): String =
|
||||
text.replace(" ", "_")
|
||||
|
||||
fun getConvertedText(): String =
|
||||
if (convertedText == null) prefixedText else convertedText!!
|
||||
|
||||
fun setConvertedText(convertedText: String?) {
|
||||
this.convertedText = convertedText
|
||||
}
|
||||
|
||||
val displayText: String
|
||||
get() = prefixedText.replace("_", " ")
|
||||
|
||||
val displayTextWithoutNamespace: String
|
||||
get() = text.replace("_", " ")
|
||||
|
||||
fun hasProperties(): Boolean =
|
||||
properties != null
|
||||
|
||||
val isMainPage: Boolean
|
||||
get() = properties != null && properties.isMainPage
|
||||
|
||||
val isDisambiguationPage: Boolean
|
||||
get() = properties != null && properties.isDisambiguationPage
|
||||
|
||||
val canonicalUri: String
|
||||
get() = getUriForDomain(wikiSite.authority())
|
||||
|
||||
val mobileUri: String
|
||||
get() = getUriForDomain(wikiSite.mobileAuthority())
|
||||
|
||||
fun getUriForAction(action: String?): String {
|
||||
try {
|
||||
return String.format(
|
||||
"%1\$s://%2\$s/w/index.php?title=%3\$s&action=%4\$s",
|
||||
wikiSite.scheme(),
|
||||
wikiSite.authority(),
|
||||
URLEncoder.encode(prefixedText, "utf-8"),
|
||||
action
|
||||
)
|
||||
} catch (e: UnsupportedEncodingException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: find a better way to check if the namespace is a ISO Alpha2 Code (two digits country code)
|
||||
val prefixedText: String
|
||||
get() = namespace?.let { addUnderscores(it) + ":" + getTextWithoutSpaces() }
|
||||
?: getTextWithoutSpaces()
|
||||
|
||||
private fun addUnderscores(text: String): String =
|
||||
text.replace(" ", "_")
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeString(namespace)
|
||||
parcel.writeString(text)
|
||||
parcel.writeString(fragment)
|
||||
parcel.writeParcelable(wikiSite, flags)
|
||||
parcel.writeParcelable(properties, flags)
|
||||
parcel.writeString(thumbUrl)
|
||||
parcel.writeString(description)
|
||||
parcel.writeString(convertedText)
|
||||
}
|
||||
|
||||
override fun equals(o: Any?): Boolean {
|
||||
if (o !is PageTitle) {
|
||||
return false
|
||||
}
|
||||
|
||||
val other = o
|
||||
// Not using namespace directly since that can be null
|
||||
return normalizedEquals(other.prefixedText, prefixedText) && other.wikiSite.equals(wikiSite)
|
||||
}
|
||||
|
||||
// Compare two strings based on their normalized form, using the Unicode Normalization Form C.
|
||||
// This should be used when comparing or verifying strings that will be exchanged between
|
||||
// different platforms (iOS, desktop, etc) that may encode strings using inconsistent
|
||||
// composition, especially for accents, diacritics, etc.
|
||||
private fun normalizedEquals(str1: String?, str2: String?): Boolean {
|
||||
if (str1 == null || str2 == null) {
|
||||
return (str1 == null && str2 == null)
|
||||
}
|
||||
return (Normalizer.normalize(str1, Normalizer.Form.NFC)
|
||||
== Normalizer.normalize(str2, Normalizer.Form.NFC))
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = prefixedText.hashCode()
|
||||
result = 31 * result + wikiSite.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String =
|
||||
prefixedText
|
||||
|
||||
override fun describeContents(): Int = 0
|
||||
|
||||
private fun getUriForDomain(domain: String): String = try {
|
||||
String.format(
|
||||
"%1\$s://%2\$s/wiki/%3\$s%4\$s",
|
||||
wikiSite.scheme(),
|
||||
domain,
|
||||
URLEncoder.encode(prefixedText, "utf-8"),
|
||||
if ((fragment != null && fragment.length > 0)) ("#$fragment") else ""
|
||||
)
|
||||
} catch (e: UnsupportedEncodingException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
|
||||
private constructor(parcel: Parcel) {
|
||||
namespace = parcel.readString()
|
||||
text = parcel.readString()!!
|
||||
fragment = parcel.readString()
|
||||
wikiSite = parcel.readParcelable(WikiSite::class.java.classLoader)!!
|
||||
properties = parcel.readParcelable(PageProperties::class.java.classLoader)
|
||||
thumbUrl = parcel.readString()
|
||||
description = parcel.readString()
|
||||
convertedText = parcel.readString()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val CREATOR: Parcelable.Creator<PageTitle> = object : Parcelable.Creator<PageTitle> {
|
||||
override fun createFromParcel(parcel: Parcel): PageTitle {
|
||||
return PageTitle(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<PageTitle?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new PageTitle object.
|
||||
* Use this if you want to pass in a fragment portion separately from the title.
|
||||
*
|
||||
* @param prefixedText title of the page with optional namespace prefix
|
||||
* @param fragment optional fragment portion
|
||||
* @param wiki the wiki site the page belongs to
|
||||
* @return a new PageTitle object matching the given input parameters
|
||||
*/
|
||||
fun withSeparateFragment(
|
||||
prefixedText: String,
|
||||
fragment: String?, wiki: WikiSite
|
||||
): PageTitle {
|
||||
return if (TextUtils.isEmpty(fragment)) {
|
||||
PageTitle(prefixedText, wiki, null, null as PageProperties?)
|
||||
} else {
|
||||
// TODO: this class needs some refactoring to allow passing in a fragment
|
||||
// without having to do string manipulations.
|
||||
PageTitle("$prefixedText#$fragment", wiki, null, null as PageProperties?)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -42,22 +42,22 @@ class MediaConverterTest {
|
|||
|
||||
@Test
|
||||
fun testConvertIfThumbUrlBlank() {
|
||||
Mockito.`when`(imageInfo.metadata).thenReturn(metadata)
|
||||
Mockito.`when`(imageInfo.thumbUrl).thenReturn("")
|
||||
Mockito.`when`(imageInfo.originalUrl).thenReturn("originalUrl")
|
||||
Mockito.`when`(imageInfo.metadata?.licenseUrl()).thenReturn("licenseUrl")
|
||||
Mockito.`when`(imageInfo.metadata?.dateTime()).thenReturn("yyyy-MM-dd HH:mm:ss")
|
||||
Mockito.`when`(imageInfo.getMetadata()).thenReturn(metadata)
|
||||
Mockito.`when`(imageInfo.getThumbUrl()).thenReturn("")
|
||||
Mockito.`when`(imageInfo.getOriginalUrl()).thenReturn("originalUrl")
|
||||
Mockito.`when`(imageInfo.getMetadata()?.licenseUrl()).thenReturn("licenseUrl")
|
||||
Mockito.`when`(imageInfo.getMetadata()?.dateTime()).thenReturn("yyyy-MM-dd HH:mm:ss")
|
||||
media = mediaConverter.convert(page, entity, imageInfo)
|
||||
assertEquals(media.thumbUrl, media.imageUrl, "originalUrl")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testConvertIfThumbUrlNotBlank() {
|
||||
Mockito.`when`(imageInfo.metadata).thenReturn(metadata)
|
||||
Mockito.`when`(imageInfo.thumbUrl).thenReturn("thumbUrl")
|
||||
Mockito.`when`(imageInfo.originalUrl).thenReturn("originalUrl")
|
||||
Mockito.`when`(imageInfo.metadata?.licenseUrl()).thenReturn("licenseUrl")
|
||||
Mockito.`when`(imageInfo.metadata?.dateTime()).thenReturn("yyyy-MM-dd HH:mm:ss")
|
||||
Mockito.`when`(imageInfo.getMetadata()).thenReturn(metadata)
|
||||
Mockito.`when`(imageInfo.getThumbUrl()).thenReturn("thumbUrl")
|
||||
Mockito.`when`(imageInfo.getOriginalUrl()).thenReturn("originalUrl")
|
||||
Mockito.`when`(imageInfo.getMetadata()?.licenseUrl()).thenReturn("licenseUrl")
|
||||
Mockito.`when`(imageInfo.getMetadata()?.dateTime()).thenReturn("yyyy-MM-dd HH:mm:ss")
|
||||
media = mediaConverter.convert(page, entity, imageInfo)
|
||||
assertEquals(media.thumbUrl, "thumbUrl")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,11 +123,11 @@ class NotificationClientTest {
|
|||
setTimestamp(Notification.Timestamp().apply { setUtciso8601(timestamp) })
|
||||
|
||||
contents = Notification.Contents().apply {
|
||||
setCompactHeader(compactHeader)
|
||||
this.compactHeader = compactHeader
|
||||
|
||||
links = Notification.Links().apply {
|
||||
setPrimary(GsonUtil.defaultGson.toJsonTree(
|
||||
Notification.Link().apply { setUrl(primaryUrl) }
|
||||
Notification.Link().apply { url = primaryUrl }
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue