This commit is contained in:
Grishka
2022-01-14 15:02:10 +03:00
commit 42d5f52ff5
55 changed files with 2815 additions and 0 deletions

View File

@@ -0,0 +1,177 @@
package org.joinmastodon.android.model;
import org.joinmastodon.android.api.ObjectValidationException;
import org.joinmastodon.android.api.RequiredField;
import java.time.Instant;
import java.time.LocalDate;
import java.util.List;
/**
* Represents a user of Mastodon and their associated profile.
*/
public class Account extends BaseModel{
// Base attributes
/**
* The account id
*/
@RequiredField
public String id;
/**
* The username of the account, not including domain.
*/
@RequiredField
public String username;
/**
* The Webfinger account URI. Equal to username for local users, or username@domain for remote users.
*/
@RequiredField
public String acct;
/**
* The location of the user's profile page.
*/
@RequiredField
public String url;
// Display attributes
/**
* The profile's display name.
*/
@RequiredField
public String displayName;
/**
* The profile's bio / description.
*/
@RequiredField
public String note;
/**
* An image icon that is shown next to statuses and in the profile.
*/
@RequiredField
public String avatar;
/**
* A static version of the avatar. Equal to avatar if its value is a static image; different if avatar is an animated GIF.
*/
public String avatarStatic;
/**
* An image banner that is shown above the profile and in profile cards.
*/
@RequiredField
public String header;
/**
* A static version of the header. Equal to header if its value is a static image; different if header is an animated GIF.
*/
public String headerStatic;
/**
* Whether the account manually approves follow requests.
*/
public boolean locked;
/**
* Custom emoji entities to be used when rendering the profile. If none, an empty array will be returned.
*/
public List<Emoji> emojis;
/**
* Whether the account has opted into discovery features such as the profile directory.
*/
public boolean discoverable;
// Statistical attributes
/**
* When the account was created.
*/
@RequiredField
public Instant createdAt;
/**
* When the most recent status was posted.
*/
// @RequiredField
public LocalDate lastStatusAt;
/**
* How many statuses are attached to this account.
*/
public int statusesCount;
/**
* The reported followers of this profile.
*/
public int followersCount;
/**
* The reported follows of this profile.
*/
public int followingCount;
// Optional attributes
/**
* Indicates that the profile is currently inactive and that its user has moved to a new account.
*/
public Account moved;
/**
* Additional metadata attached to a profile as name-value pairs.
*/
public List<AccountField> fields;
/**
* A presentational flag. Indicates that the account may perform automated actions, may not be monitored, or identifies as a robot.
*/
public boolean bot;
/**
* An extra entity to be used with API methods to verify credentials and update credentials.
*/
public Source source;
/**
* An extra entity returned when an account is suspended.
*/
public boolean suspended;
/**
* When a timed mute will expire, if applicable.
*/
public Instant muteExpiresAt;
@Override
public void postprocess() throws ObjectValidationException{
super.postprocess();
if(fields!=null){
for(AccountField f:fields)
f.postprocess();
}
if(emojis!=null){
for(Emoji e:emojis)
e.postprocess();
}
if(moved!=null)
moved.postprocess();
}
@Override
public String toString(){
return "Account{"+
"id='"+id+'\''+
", username='"+username+'\''+
", acct='"+acct+'\''+
", url='"+url+'\''+
", displayName='"+displayName+'\''+
", note='"+note+'\''+
", avatar='"+avatar+'\''+
", avatarStatic='"+avatarStatic+'\''+
", header='"+header+'\''+
", headerStatic='"+headerStatic+'\''+
", locked="+locked+
", emojis="+emojis+
", discoverable="+discoverable+
", createdAt="+createdAt+
", lastStatusAt="+lastStatusAt+
", statusesCount="+statusesCount+
", followersCount="+followersCount+
", followingCount="+followingCount+
", moved="+moved+
", fields="+fields+
", bot="+bot+
", source="+source+
", suspended="+suspended+
", muteExpiresAt="+muteExpiresAt+
'}';
}
}

View File

@@ -0,0 +1,25 @@
package org.joinmastodon.android.model;
import org.joinmastodon.android.api.RequiredField;
import java.time.Instant;
/**
* Represents a profile field as a name-value pair with optional verification.
*/
public class AccountField extends BaseModel{
/**
* The key of a given field's key-value pair.
*/
@RequiredField
public String name;
/**
* The value associated with the name key.
*/
@RequiredField
public String value;
/**
* Timestamp of when the server verified a URL value for a rel="me” link.
*/
public Instant verifiedAt;
}

View File

@@ -0,0 +1,23 @@
package org.joinmastodon.android.model;
import org.joinmastodon.android.api.RequiredField;
public class Application extends BaseModel{
@RequiredField
public String name;
public String website;
public String vapidKey;
public String clientId;
public String clientSecret;
@Override
public String toString(){
return "Application{"+
"name='"+name+'\''+
", website='"+website+'\''+
", vapidKey='"+vapidKey+'\''+
", clientId='"+clientId+'\''+
", clientSecret='"+clientSecret+'\''+
'}';
}
}

View File

@@ -0,0 +1,26 @@
package org.joinmastodon.android.model;
import org.joinmastodon.android.api.AllFieldsAreRequired;
import org.joinmastodon.android.api.ObjectValidationException;
import org.joinmastodon.android.api.RequiredField;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import androidx.annotation.CallSuper;
public abstract class BaseModel{
@CallSuper
public void postprocess() throws ObjectValidationException{
try{
boolean allRequired=getClass().isAnnotationPresent(AllFieldsAreRequired.class);
for(Field fld:getClass().getFields()){
if(!fld.getType().isPrimitive() && !Modifier.isTransient(fld.getModifiers()) && (allRequired || fld.isAnnotationPresent(RequiredField.class))){
if(fld.get(this)==null){
throw new ObjectValidationException("Required field '"+fld.getName()+"' of type "+fld.getType().getSimpleName()+" was null in "+getClass().getSimpleName());
}
}
}
}catch(IllegalAccessException ignore){}
}
}

View File

@@ -0,0 +1,33 @@
package org.joinmastodon.android.model;
import org.joinmastodon.android.api.RequiredField;
/**
* Represents a custom emoji.
*/
public class Emoji extends BaseModel{
/**
* The name of the custom emoji.
*/
@RequiredField
public String shortcode;
/**
* A link to the custom emoji.
*/
@RequiredField
public String url;
/**
* A link to a static copy of the custom emoji.
*/
@RequiredField
public String staticUrl;
/**
* Whether this Emoji should be visible in the picker or unlisted.
*/
@RequiredField
public boolean visibleInPicker;
/**
* Used for sorting custom emoji in the picker.
*/
public String category;
}

View File

@@ -0,0 +1,215 @@
package org.joinmastodon.android.model;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.Html;
import org.joinmastodon.android.api.ObjectValidationException;
import org.joinmastodon.android.api.RequiredField;
import org.joinmastodon.android.model.catalog.CatalogInstance;
import java.net.IDN;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class Instance extends BaseModel{
/**
* The domain name of the instance.
*/
@RequiredField
public String uri;
/**
* The title of the website.
*/
@RequiredField
public String title;
/**
* Admin-defined description of the Mastodon site.
*/
@RequiredField
public String description;
/**
* A shorter description defined by the admin.
*/
@RequiredField
public String shortDescription;
/**
* An email that may be contacted for any inquiries.
*/
@RequiredField
public String email;
/**
* The version of Mastodon installed on the instance.
*/
@RequiredField
public String version;
/**
* Primary langauges of the website and its staff.
*/
// @RequiredField
public List<String> languages;
/**
* Whether registrations are enabled.
*/
public boolean registrations;
/**
* Whether registrations require moderator approval.
*/
public boolean approvalRequired;
/**
* Whether invites are enabled.
*/
public boolean invitesEnabled;
/**
* URLs of interest for clients apps.
*/
public Map<String, String> urls;
/**
* Banner image for the website.
*/
public String thumbnail;
/**
* A user that can be contacted, as an alternative to email.
*/
public Account contactAccount;
public Stats stats;
public int maxTootChars;
public List<Rule> rules;
@Override
public void postprocess() throws ObjectValidationException{
super.postprocess();
if(contactAccount!=null)
contactAccount.postprocess();
}
@Override
public String toString(){
return "Instance{"+
"uri='"+uri+'\''+
", title='"+title+'\''+
", description='"+description+'\''+
", shortDescription='"+shortDescription+'\''+
", email='"+email+'\''+
", version='"+version+'\''+
", languages="+languages+
", registrations="+registrations+
", approvalRequired="+approvalRequired+
", invitesEnabled="+invitesEnabled+
", urls="+urls+
", thumbnail='"+thumbnail+'\''+
", contactAccount="+contactAccount+
'}';
}
public CatalogInstance toCatalogInstance(){
CatalogInstance ci=new CatalogInstance();
ci.domain=uri;
ci.normalizedDomain=IDN.toUnicode(uri);
ci.description=Html.fromHtml(shortDescription).toString().trim();
if(languages!=null){
ci.language=languages.get(0);
ci.languages=languages;
}else{
ci.languages=Collections.emptyList();
ci.language="unknown";
}
ci.proxiedThumbnail=thumbnail;
if(stats!=null)
ci.totalUsers=stats.userCount;
return ci;
}
public static class Rule implements Parcelable{
public String id;
public String text;
@Override
public int describeContents(){
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags){
dest.writeString(this.id);
dest.writeString(this.text);
}
public void readFromParcel(Parcel source){
this.id=source.readString();
this.text=source.readString();
}
public Rule(){
}
protected Rule(Parcel in){
this.id=in.readString();
this.text=in.readString();
}
public static final Parcelable.Creator<Rule> CREATOR=new Parcelable.Creator<Rule>(){
@Override
public Rule createFromParcel(Parcel source){
return new Rule(source);
}
@Override
public Rule[] newArray(int size){
return new Rule[size];
}
};
}
public static class Stats implements Parcelable{
public int userCount;
public int statusCount;
public int domainCount;
@Override
public int describeContents(){
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags){
dest.writeInt(this.userCount);
dest.writeInt(this.statusCount);
dest.writeInt(this.domainCount);
}
public void readFromParcel(Parcel source){
this.userCount=source.readInt();
this.statusCount=source.readInt();
this.domainCount=source.readInt();
}
public Stats(){
}
protected Stats(Parcel in){
this.userCount=in.readInt();
this.statusCount=in.readInt();
this.domainCount=in.readInt();
}
public static final Parcelable.Creator<Stats> CREATOR=new Parcelable.Creator<Stats>(){
@Override
public Stats createFromParcel(Parcel source){
return new Stats(source);
}
@Override
public Stats[] newArray(int size){
return new Stats[size];
}
};
}
}

View File

@@ -0,0 +1,45 @@
package org.joinmastodon.android.model;
import org.joinmastodon.android.api.ObjectValidationException;
import org.joinmastodon.android.api.RequiredField;
import java.util.List;
/**
* Represents display or publishing preferences of user's own account. Returned as an additional entity when verifying and updated credentials, as an attribute of Account.
*/
public class Source extends BaseModel{
/**
* Profile bio.
*/
@RequiredField
public String note;
/**
* Metadata about the account.
*/
@RequiredField
public List<AccountField> fields;
/**
* The default post privacy to be used for new statuses.
*/
public StatusPrivacy privacy;
/**
* Whether new statuses should be marked sensitive by default.
*/
public boolean sensitive;
/**
* The default posting language for new statuses.
*/
public String language;
/**
* The number of pending follow requests.
*/
public int followRequestCount;
@Override
public void postprocess() throws ObjectValidationException{
super.postprocess();
for(AccountField f:fields)
f.postprocess();
}
}

View File

@@ -0,0 +1,14 @@
package org.joinmastodon.android.model;
import com.google.gson.annotations.SerializedName;
public enum StatusPrivacy{
@SerializedName("public")
PUBLIC,
@SerializedName("unlisted")
UNLISTED,
@SerializedName("private")
PRIVATE,
@SerializedName("direct")
DIRECT;
}

View File

@@ -0,0 +1,27 @@
package org.joinmastodon.android.model;
import org.joinmastodon.android.api.AllFieldsAreRequired;
/**
* Represents an OAuth token used for authenticating with the API and performing actions.
*/
@AllFieldsAreRequired
public class Token extends BaseModel{
/**
* An OAuth token to be used for authorization.
*/
public String accessToken;
/**
* The OAuth token type. Mastodon uses Bearer tokens.
*/
public String tokenType;
/**
* The OAuth scopes granted by this token, space-separated.
*/
public String scope;
/**
* When the token was generated.
* (unixtime)
*/
public long createdAt;
}

View File

@@ -0,0 +1,18 @@
package org.joinmastodon.android.model.catalog;
import org.joinmastodon.android.api.AllFieldsAreRequired;
import org.joinmastodon.android.model.BaseModel;
@AllFieldsAreRequired
public class CatalogCategory extends BaseModel{
public String category;
public int serversCount;
@Override
public String toString(){
return "CatalogCategory{"+
"category='"+category+'\''+
", serversCount="+serversCount+
'}';
}
}

View File

@@ -0,0 +1,53 @@
package org.joinmastodon.android.model.catalog;
import org.joinmastodon.android.api.AllFieldsAreRequired;
import org.joinmastodon.android.api.ObjectValidationException;
import org.joinmastodon.android.model.BaseModel;
import java.net.IDN;
import java.util.List;
@AllFieldsAreRequired
public class CatalogInstance extends BaseModel{
public String domain;
public String version;
public String description;
public List<String> languages;
public String region;
public List<String> categories;
public String proxiedThumbnail;
public int totalUsers;
public int lastWeekUsers;
public boolean approvalRequired;
public String language;
public String category;
public transient String normalizedDomain;
@Override
public void postprocess() throws ObjectValidationException{
super.postprocess();
if(domain.startsWith("xn--") || domain.contains(".xn--"))
normalizedDomain=IDN.toUnicode(domain);
else
normalizedDomain=domain;
}
@Override
public String toString(){
return "CatalogInstance{"+
"domain='"+domain+'\''+
", version='"+version+'\''+
", description='"+description+'\''+
", languages="+languages+
", region='"+region+'\''+
", categories="+categories+
", proxiedThumbnail='"+proxiedThumbnail+'\''+
", totalUsers="+totalUsers+
", lastWeekUsers="+lastWeekUsers+
", approvalRequired="+approvalRequired+
", language='"+language+'\''+
", category='"+category+'\''+
'}';
}
}