diff --git a/app/src/main/java/com/keylesspalace/tusky/Account.java b/app/src/main/java/com/keylesspalace/tusky/Account.java deleted file mode 100644 index ae88d2f3b..000000000 --- a/app/src/main/java/com/keylesspalace/tusky/Account.java +++ /dev/null @@ -1,92 +0,0 @@ -/* 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.util.ArrayList; -import java.util.List; - -class Account { - String id; - String username; - String displayName; - Spanned note; - String url; - String avatar; - String header; - String followersCount; - String followingCount; - String statusesCount; - - public static Account parse(JSONObject object) throws JSONException { - Account account = new Account(); - account.id = object.getString("id"); - account.username = object.getString("acct"); - account.displayName = object.getString("display_name"); - if (account.displayName.isEmpty()) { - account.displayName = object.getString("username"); - } - account.note = HtmlUtils.fromHtml(object.getString("note")); - account.url = object.getString("url"); - String avatarUrl = object.getString("avatar"); - if (!avatarUrl.equals("/avatars/original/missing.png")) { - account.avatar = avatarUrl; - } else { - account.avatar = null; - } - String headerUrl = object.getString("header"); - if (!headerUrl.equals("/headers/original/missing.png")) { - account.header = headerUrl; - } else { - account.header = null; - } - account.followersCount = object.getString("followers_count"); - account.followingCount = object.getString("following_count"); - account.statusesCount = object.getString("statuses_count"); - return account; - } - - public static List parse(JSONArray array) throws JSONException { - List accounts = new ArrayList<>(); - for (int i = 0; i < array.length(); i++) { - JSONObject object = array.getJSONObject(i); - Account account = parse(object); - accounts.add(account); - } - return accounts; - } - - @Override - public int hashCode() { - return id.hashCode(); - } - - @Override - public boolean equals(Object other) { - if (this.id == null) { - return this == other; - } else if (!(other instanceof Account)) { - return false; - } - Account account = (Account) other; - return account.id.equals(this.id); - } -} diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.java b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.java index db7d28644..f517d4021 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.java @@ -45,6 +45,7 @@ import com.android.volley.Response; import com.android.volley.VolleyError; import com.android.volley.toolbox.JsonArrayRequest; import com.android.volley.toolbox.JsonObjectRequest; +import com.keylesspalace.tusky.entity.Account; import com.pkmmte.view.CircularImageView; import com.squareup.picasso.Picasso; @@ -55,6 +56,9 @@ import org.json.JSONObject; import java.util.HashMap; import java.util.Map; +import retrofit2.Call; +import retrofit2.Callback; + public class AccountActivity extends BaseActivity { private static final String TAG = "AccountActivity"; // Volley request tag and logging tag @@ -71,6 +75,7 @@ public class AccountActivity extends BaseActivity { protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_account); + createMastodonAPI(); Intent intent = getIntent(); accountId = intent.getStringExtra("id"); @@ -169,37 +174,17 @@ public class AccountActivity extends BaseActivity { } private void obtainAccount() { - String endpoint = String.format(getString(R.string.endpoint_accounts), accountId); - String url = "https://" + domain + endpoint; - JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null, - new Response.Listener() { - @Override - public void onResponse(JSONObject response) { - Account account; - try { - account = Account.parse(response); - } catch (JSONException e) { - onObtainAccountFailure(); - return; - } - onObtainAccountSuccess(account); - } - }, - new Response.ErrorListener() { - @Override - public void onErrorResponse(VolleyError error) { - onObtainAccountFailure(); - } - }) { + mastodonAPI.account(accountId).enqueue(new Callback() { @Override - public Map getHeaders() throws AuthFailureError { - Map headers = new HashMap<>(); - headers.put("Authorization", "Bearer " + accessToken); - return headers; + public void onResponse(Call call, retrofit2.Response response) { + onObtainAccountSuccess(response.body()); } - }; - request.setTag(TAG); - VolleySingleton.getInstance(this).addToRequestQueue(request); + + @Override + public void onFailure(Call call, Throwable t) { + onObtainAccountFailure(); + } + }); } private void onObtainAccountSuccess(Account account) { diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountAdapter.java b/app/src/main/java/com/keylesspalace/tusky/AccountAdapter.java index 6fd60a6fd..ab7653a6c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/AccountAdapter.java @@ -17,6 +17,8 @@ package com.keylesspalace.tusky; import android.support.v7.widget.RecyclerView; +import com.keylesspalace.tusky.entity.Account; + import java.util.ArrayList; import java.util.List; diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountFragment.java b/app/src/main/java/com/keylesspalace/tusky/AccountFragment.java index c3e7be81c..358875160 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/AccountFragment.java @@ -34,16 +34,16 @@ import com.android.volley.AuthFailureError; import com.android.volley.Request; import com.android.volley.Response; import com.android.volley.VolleyError; -import com.android.volley.toolbox.JsonArrayRequest; import com.android.volley.toolbox.StringRequest; - -import org.json.JSONArray; -import org.json.JSONException; +import com.keylesspalace.tusky.entity.Account; import java.util.HashMap; import java.util.List; import java.util.Map; +import retrofit2.Call; +import retrofit2.Callback; + public class AccountFragment extends Fragment implements AccountActionListener, FooterActionListener { private static final String TAG = "Account"; // logging tag and Volley request tag @@ -170,55 +170,39 @@ public class AccountFragment extends Fragment implements AccountActionListener, } private void fetchAccounts(final String fromId) { + MastodonAPI api = ((BaseActivity) getActivity()).mastodonAPI; + + Callback> cb = new Callback>() { + @Override + public void onResponse(Call> call, retrofit2.Response> response) { + onFetchAccountsSuccess(response.body(), fromId); + } + + @Override + public void onFailure(Call> call, Throwable t) { + onFetchAccountsFailure((Exception) t); + } + }; + String endpoint; switch (type) { default: case FOLLOWS: { endpoint = String.format(getString(R.string.endpoint_following), accountId); + api.accountFollowing(accountId, fromId, null, null).enqueue(cb); break; } case FOLLOWERS: { endpoint = String.format(getString(R.string.endpoint_followers), accountId); + api.accountFollowers(accountId, fromId, null, null).enqueue(cb); break; } case BLOCKS: { endpoint = getString(R.string.endpoint_blocks); + api.blocks(fromId, null, null).enqueue(cb); break; } } - String url = "https://" + domain + endpoint; - if (fromId != null) { - url += "?max_id=" + fromId; - } - JsonArrayRequest request = new JsonArrayRequest(url, - new Response.Listener() { - @Override - public void onResponse(JSONArray response) { - List accounts; - try { - accounts = Account.parse(response); - } catch (JSONException e) { - onFetchAccountsFailure(e); - return; - } - onFetchAccountsSuccess(accounts, fromId); - } - }, - new Response.ErrorListener() { - @Override - public void onErrorResponse(VolleyError error) { - onFetchAccountsFailure(error); - } - }) { - @Override - public Map getHeaders() throws AuthFailureError { - Map headers = new HashMap<>(); - headers.put("Authorization", "Bearer " + accessToken); - return headers; - } - }; - request.setTag(TAG); - VolleySingleton.getInstance(getContext()).addToRequestQueue(request); } private void fetchAccounts() { diff --git a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java index 4725c25c4..7568dccd6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java @@ -25,9 +25,13 @@ import android.os.Bundle; import android.preference.PreferenceManager; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; +import android.text.Spanned; import android.util.TypedValue; import android.view.Menu; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + import java.io.IOException; import okhttp3.Interceptor; @@ -99,10 +103,14 @@ public class BaseActivity extends AppCompatActivity { }) .build(); + Gson gson = new GsonBuilder() + .registerTypeAdapter(Spanned.class, new SpannedTypeAdapter()) + .create(); + Retrofit retrofit = new Retrofit.Builder() .baseUrl(getBaseUrl()) .client(okHttpClient) - .addConverterFactory(GsonConverterFactory.create()) + .addConverterFactory(GsonConverterFactory.create(gson)) .build(); mastodonAPI = retrofit.create(MastodonAPI.class); diff --git a/app/src/main/java/com/keylesspalace/tusky/BlocksAdapter.java b/app/src/main/java/com/keylesspalace/tusky/BlocksAdapter.java index 0b4405477..37bfc4034 100644 --- a/app/src/main/java/com/keylesspalace/tusky/BlocksAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/BlocksAdapter.java @@ -23,6 +23,7 @@ import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; +import com.keylesspalace.tusky.entity.Account; import com.squareup.picasso.Picasso; import java.util.HashSet; diff --git a/app/src/main/java/com/keylesspalace/tusky/FollowAdapter.java b/app/src/main/java/com/keylesspalace/tusky/FollowAdapter.java index de70bcee6..140e32d02 100644 --- a/app/src/main/java/com/keylesspalace/tusky/FollowAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/FollowAdapter.java @@ -23,6 +23,7 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; +import com.keylesspalace.tusky.entity.Account; import com.squareup.picasso.Picasso; /** Both for follows and following lists. */ diff --git a/app/src/main/java/com/keylesspalace/tusky/MastodonAPI.java b/app/src/main/java/com/keylesspalace/tusky/MastodonAPI.java index 06c985897..846ad3e33 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MastodonAPI.java +++ b/app/src/main/java/com/keylesspalace/tusky/MastodonAPI.java @@ -1,5 +1,6 @@ package com.keylesspalace.tusky; +import com.keylesspalace.tusky.entity.Account; import com.keylesspalace.tusky.entity.Media; import com.keylesspalace.tusky.entity.Relationship; import com.keylesspalace.tusky.entity.StatusContext; @@ -21,18 +22,33 @@ import retrofit2.http.Query; public interface MastodonAPI { @GET("api/v1/timelines/home") - Call> homeTimeline(@Query("max_id") int maxId, @Query("since_id") int sinceId, @Query("limit") int limit); + Call> homeTimeline( + @Query("max_id") String maxId, + @Query("since_id") String sinceId, + @Query("limit") Integer limit); @GET("api/v1/timelines/public") - Call> publicTimeline(@Query("local") boolean local, @Query("max_id") int maxId, @Query("since_id") int sinceId, @Query("limit") int limit); + Call> publicTimeline( + @Query("local") Boolean local, + @Query("max_id") String maxId, + @Query("since_id") String sinceId, + @Query("limit") Integer limit); @GET("api/v1/timelines/tag/{hashtag}") - Call> hashtagTimeline(@Path("hashtag") String hashtag, @Query("local") boolean local, @Query("max_id") int maxId, @Query("since_id") int sinceId, @Query("limit") int limit); + Call> hashtagTimeline( + @Path("hashtag") String hashtag, + @Query("local") Boolean local, + @Query("max_id") String maxId, + @Query("since_id") String sinceId, + @Query("limit") Integer limit); @GET("api/v1/notifications") - Call> notifications(@Query("max_id") int maxId, @Query("since_id") int sinceId, @Query("limit") int limit); + Call> notifications( + @Query("max_id") String maxId, + @Query("since_id") String sinceId, + @Query("limit") Integer limit); @POST("api/v1/notifications/clear") Call clearNotifications(); @GET("api/v1/notifications/{id}") - Call notification(@Path("id") int notificationId); + Call notification(@Path("id") String notificationId); @Multipart @POST("api/v1/media") @@ -40,67 +56,108 @@ public interface MastodonAPI { @FormUrlEncoded @POST("api/v1/statuses") - Call createStatus(@Field("status") String text, @Field("in_reply_to_id") int inReplyToId, @Field("spoiler_text") String warningText, @Field("visibility") String visibility, @Field("sensitive") boolean sensitive, @Field("media_ids[]") List mediaIds); + Call createStatus( + @Field("status") String text, + @Field("in_reply_to_id") String inReplyToId, + @Field("spoiler_text") String warningText, + @Field("visibility") String visibility, + @Field("sensitive") Boolean sensitive, + @Field("media_ids[]") List mediaIds); @GET("api/v1/statuses/{id}") - Call status(@Path("id") int statusId); + Call status(@Path("id") String statusId); @GET("api/v1/statuses/{id}/context") - Call statusContext(@Path("id") int statusId); + Call statusContext(@Path("id") String statusId); @GET("api/v1/statuses/{id}/reblogged_by") - Call> statusRebloggedBy(@Path("id") int statusId, @Query("max_id") int maxId, @Query("since_id") int sinceId, @Query("limit") int limit); + Call> statusRebloggedBy( + @Path("id") String statusId, + @Query("max_id") String maxId, + @Query("since_id") String sinceId, + @Query("limit") Integer limit); @GET("api/v1/statuses/{id}/favourited_by") - Call> statusFavouritedBy(@Path("id") int statusId, @Query("max_id") int maxId, @Query("since_id") int sinceId, @Query("limit") int limit); + Call> statusFavouritedBy( + @Path("id") String statusId, + @Query("max_id") String maxId, + @Query("since_id") String sinceId, + @Query("limit") Integer limit); @DELETE("api/v1/statuses/{id}") - Call deleteStatus(@Path("id") int statusId); + Call deleteStatus(@Path("id") String statusId); @POST("api/v1/statuses/{id}/reblog") - Call reblogStatus(@Path("id") int statusId); + Call reblogStatus(@Path("id") String statusId); @POST("api/v1/statuses/{id}/unreblog") - Call unreblogStatus(@Path("id") int statusId); + Call unreblogStatus(@Path("id") String statusId); @POST("api/v1/statuses/{id}/favourite") - Call favouriteStatus(@Path("id") int statusId); + Call favouriteStatus(@Path("id") String statusId); @POST("api/v1/statuses/{id}/unfavourite") - Call unfavouriteStatus(@Path("id") int statusId); + Call unfavouriteStatus(@Path("id") String statusId); @GET("api/v1/accounts/verify_credentials") Call accountVerifyCredentials(); @GET("api/v1/accounts/search") - Call> searchAccounts(@Query("q") String q, @Query("resolve") boolean resolve, @Query("limit") int limit); + Call> searchAccounts( + @Query("q") String q, + @Query("resolve") Boolean resolve, + @Query("limit") Integer limit); @GET("api/v1/accounts/{id}") - Call account(@Path("id") int accountId); + Call account(@Path("id") String accountId); @GET("api/v1/accounts/{id}/statuses") - Call> accountStatuses(@Path("id") int accountId, @Query("max_id") int maxId, @Query("since_id") int sinceId, @Query("limit") int limit); + Call> accountStatuses( + @Path("id") String accountId, + @Query("max_id") String maxId, + @Query("since_id") String sinceId, + @Query("limit") Integer limit); @GET("api/v1/accounts/{id}/followers") - Call> accountFollowers(@Path("id") int accountId, @Query("max_id") int maxId, @Query("since_id") int sinceId, @Query("limit") int limit); + Call> accountFollowers( + @Path("id") String accountId, + @Query("max_id") String maxId, + @Query("since_id") String sinceId, + @Query("limit") Integer limit); @GET("api/v1/accounts/{id}/following") - Call> accountFollowing(@Path("id") int accountId, @Query("max_id") int maxId, @Query("since_id") int sinceId, @Query("limit") int limit); + Call> accountFollowing( + @Path("id") String accountId, + @Query("max_id") String maxId, + @Query("since_id") String sinceId, + @Query("limit") Integer limit); @POST("api/v1/accounts/{id}/follow") - Call followAccount(@Path("id") int accountId); + Call followAccount(@Path("id") String accountId); @POST("api/v1/accounts/{id}/unfollow") - Call unfollowAccount(@Path("id") int accountId); + Call unfollowAccount(@Path("id") String accountId); @POST("api/v1/accounts/{id}/block") - Call blockAccount(@Path("id") int accountId); + Call blockAccount(@Path("id") String accountId); @POST("api/v1/accounts/{id}/unblock") - Call unblockAccount(@Path("id") int accountId); + Call unblockAccount(@Path("id") String accountId); @POST("api/v1/accounts/{id}/mute") - Call muteAccount(@Path("id") int accountId); + Call muteAccount(@Path("id") String accountId); @POST("api/v1/accounts/{id}/unmute") - Call unmuteAccount(@Path("id") int accountId); + Call unmuteAccount(@Path("id") String accountId); @GET("api/v1/accounts/relationships") Call> relationships(@Query("id[]") List accountIds); @GET("api/v1/blocks") - Call> blocks(@Query("max_id") int maxId, @Query("since_id") int sinceId, @Query("limit") int limit); + Call> blocks( + @Query("max_id") String maxId, + @Query("since_id") String sinceId, + @Query("limit") Integer limit); @GET("api/v1/mutes") - Call> mutes(@Query("max_id") int maxId, @Query("since_id") int sinceId, @Query("limit") int limit); + Call> mutes( + @Query("max_id") String maxId, + @Query("since_id") String sinceId, + @Query("limit") Integer limit); @GET("api/v1/favourites") - Call> favourites(@Query("max_id") int maxId, @Query("since_id") int sinceId, @Query("limit") int limit); + Call> favourites( + @Query("max_id") String maxId, + @Query("since_id") String sinceId, + @Query("limit") Integer limit); @GET("api/v1/follow_requests") - Call> followRequests(@Query("max_id") int maxId, @Query("since_id") int sinceId, @Query("limit") int limit); + Call> followRequests( + @Query("max_id") String maxId, + @Query("since_id") String sinceId, + @Query("limit") Integer limit); @POST("api/v1/follow_requests/{id}/authorize") - Call authorizeFollowRequest(@Path("id") int accountId); + Call authorizeFollowRequest(@Path("id") String accountId); @POST("api/v1/follow_requests/{id}/reject") - Call rejectFollowRequest(@Path("id") int accountId); + Call rejectFollowRequest(@Path("id") String accountId); } diff --git a/app/src/main/java/com/keylesspalace/tusky/SpannedTypeAdapter.java b/app/src/main/java/com/keylesspalace/tusky/SpannedTypeAdapter.java new file mode 100644 index 000000000..1ef4db1c3 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/SpannedTypeAdapter.java @@ -0,0 +1,17 @@ +package com.keylesspalace.tusky; + +import android.text.Spanned; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; + +import java.lang.reflect.Type; + +class SpannedTypeAdapter implements JsonDeserializer { + @Override + public Spanned deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + return HtmlUtils.fromHtml(json.getAsString()); + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Account.java b/app/src/main/java/com/keylesspalace/tusky/entity/Account.java new file mode 100644 index 000000000..6f03b0305 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Account.java @@ -0,0 +1,63 @@ +/* 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.entity; + +import android.text.Spanned; + +import com.google.gson.annotations.SerializedName; + +public class Account { + public String id; + + @SerializedName("acct") + public String username; + + @SerializedName("display_name") + public String displayName; + + public Spanned note; + + public String url; + + public String avatar; + + public String header; + + @SerializedName("followers_count") + public String followersCount; + + @SerializedName("following_count") + public String followingCount; + + @SerializedName("statuses_count") + public String statusesCount; + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (this.id == null) { + return this == other; + } else if (!(other instanceof Account)) { + return false; + } + Account account = (Account) other; + return account.id.equals(this.id); + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Media.java b/app/src/main/java/com/keylesspalace/tusky/entity/Media.java index ec6ef31cb..1db867fb0 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Media.java +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Media.java @@ -3,7 +3,7 @@ package com.keylesspalace.tusky.entity; import com.google.gson.annotations.SerializedName; public class Media { - public int id; + public String id; public String type; diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Relationship.java b/app/src/main/java/com/keylesspalace/tusky/entity/Relationship.java index 6964b585a..d08744053 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Relationship.java +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Relationship.java @@ -3,7 +3,7 @@ package com.keylesspalace.tusky.entity; import com.google.gson.annotations.SerializedName; public class Relationship { - public int id; + public String id; public boolean following;