/* Copyright 2017 Andrew Dawson
*
* This file is part of Tusky.
*
* Tusky is free software: you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky. If not, see
* . */
package com.keylesspalace.tusky;
import android.text.Spanned;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class Status {
enum Visibility {
PUBLIC,
UNLISTED,
PRIVATE,
}
private String id;
private String accountId;
private String displayName;
/** the username with the remote domain appended, like @domain.name, if it's a remote account */
private String username;
/** the main text of the status, marked up with style for links & mentions, etc */
private Spanned content;
/** the fully-qualified url of the avatar image */
private String avatar;
private String rebloggedByUsername;
/** when the status was initially created */
private Date createdAt;
/** whether the authenticated user has reblogged this status */
private boolean reblogged;
/** whether the authenticated user has favourited this status */
private boolean favourited;
private boolean sensitive;
private String spoilerText;
private Visibility visibility;
private MediaAttachment[] attachments;
private Mention[] mentions;
public static final int MAX_MEDIA_ATTACHMENTS = 4;
public Status(String id, String accountId, String displayName, String username, Spanned content,
String avatar, Date createdAt, boolean reblogged, boolean favourited,
String visibility) {
this.id = id;
this.accountId = accountId;
this.displayName = displayName;
this.username = username;
this.content = content;
this.avatar = avatar;
this.createdAt = createdAt;
this.reblogged = reblogged;
this.favourited = favourited;
this.spoilerText = "";
this.visibility = Visibility.valueOf(visibility.toUpperCase());
this.attachments = new MediaAttachment[0];
this.mentions = new Mention[0];
}
public String getId() {
return id;
}
public String getAccountId() {
return accountId;
}
public String getDisplayName() {
return displayName;
}
public String getUsername() {
return username;
}
public Spanned getContent() {
return content;
}
public String getAvatar() {
return avatar;
}
public Date getCreatedAt() {
return createdAt;
}
public String getRebloggedByUsername() {
return rebloggedByUsername;
}
public boolean getReblogged() {
return reblogged;
}
public boolean getFavourited() {
return favourited;
}
public boolean getSensitive() {
return sensitive;
}
public String getSpoilerText() {
return spoilerText;
}
public Visibility getVisibility() {
return visibility;
}
public MediaAttachment[] getAttachments() {
return attachments;
}
public Mention[] getMentions() {
return mentions;
}
public void setRebloggedByUsername(String name) {
rebloggedByUsername = name;
}
public void setReblogged(boolean reblogged) {
this.reblogged = reblogged;
}
public void setFavourited(boolean favourited) {
this.favourited = favourited;
}
public void setSpoilerText(String spoilerText) {
this.spoilerText = spoilerText;
}
public void setMentions(Mention[] mentions) {
this.mentions = mentions;
}
public void setAttachments(MediaAttachment[] attachments, boolean sensitive) {
this.attachments = attachments;
this.sensitive = sensitive;
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public boolean equals(Object other) {
if (this.id == null) {
return this == other;
} else if (!(other instanceof Status)) {
return false;
}
Status status = (Status) other;
return status.id.equals(this.id);
}
private static Date parseDate(String dateTime) {
Date date;
String s = dateTime.replace("Z", "+00:00");
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
try {
date = format.parse(s);
} catch (ParseException e) {
e.printStackTrace();
return null;
}
return date;
}
public static Status parse(JSONObject object, boolean isReblog) throws JSONException {
String id = object.getString("id");
String content = object.getString("content");
Date createdAt = parseDate(object.getString("created_at"));
boolean reblogged = object.optBoolean("reblogged");
boolean favourited = object.optBoolean("favourited");
String spoilerText = object.getString("spoiler_text");
boolean sensitive = object.optBoolean("sensitive");
String visibility = object.getString("visibility");
JSONObject account = object.getJSONObject("account");
String accountId = account.getString("id");
String displayName = account.getString("display_name");
if (displayName.isEmpty()) {
displayName = account.getString("username");
}
String username = account.getString("acct");
String avatarUrl = account.getString("avatar");
String avatar;
if (!avatarUrl.equals("/avatars/original/missing.png")) {
avatar = avatarUrl;
} else {
avatar = "";
}
JSONArray mentionsArray = object.getJSONArray("mentions");
Mention[] mentions = null;
if (mentionsArray != null) {
int n = mentionsArray.length();
mentions = new Mention[n];
for (int i = 0; i < n; i++) {
JSONObject mention = mentionsArray.getJSONObject(i);
String url = mention.getString("url");
String mentionedUsername = mention.getString("acct");
String mentionedAccountId = mention.getString("id");
mentions[i] = new Mention(url, mentionedUsername, mentionedAccountId);
}
}
JSONArray mediaAttachments = object.getJSONArray("media_attachments");
MediaAttachment[] attachments = null;
if (mediaAttachments != null) {
int n = mediaAttachments.length();
attachments = new MediaAttachment[n];
for (int i = 0; i < n; i++) {
JSONObject attachment = mediaAttachments.getJSONObject(i);
String url = attachment.getString("url");
String previewUrl = attachment.getString("preview_url");
String type = attachment.getString("type");
attachments[i] = new MediaAttachment(url, previewUrl,
MediaAttachment.Type.valueOf(type.toUpperCase()));
}
}
Status reblog = null;
/* This case shouldn't be hit after the first recursion at all. But if this method is
* passed unusual data this check will prevent extra recursion */
if (!isReblog) {
JSONObject reblogObject = object.optJSONObject("reblog");
if (reblogObject != null) {
reblog = parse(reblogObject, true);
}
}
Status status;
if (reblog != null) {
status = reblog;
status.setRebloggedByUsername(username);
} else {
Spanned contentPlus = HtmlUtils.fromHtml(content);
status = new Status(
id, accountId, displayName, username, contentPlus, avatar, createdAt,
reblogged, favourited, visibility);
if (mentions != null) {
status.setMentions(mentions);
}
if (attachments != null) {
status.setAttachments(attachments, sensitive);
}
if (!spoilerText.isEmpty()) {
status.setSpoilerText(spoilerText);
}
}
return status;
}
public static List parse(JSONArray array) throws JSONException {
List statuses = new ArrayList<>();
for (int i = 0; i < array.length(); i++) {
JSONObject object = array.getJSONObject(i);
statuses.add(parse(object, false));
}
return statuses;
}
public static class MediaAttachment {
enum Type {
IMAGE,
VIDEO,
}
private String url;
private String previewUrl;
private Type type;
public MediaAttachment(String url, String previewUrl, Type type) {
this.url = url;
this.previewUrl = previewUrl;
this.type = type;
}
public String getUrl() {
return url;
}
public String getPreviewUrl() {
return previewUrl;
}
public Type getType() {
return type;
}
}
public static class Mention {
private String url;
private String username;
private String id;
public Mention(String url, String username, String id) {
this.url = url;
this.username = username;
this.id = id;
}
public String getUrl() {
return url;
}
public String getUsername() {
return username;
}
public String getId() {
return id;
}
}
}