From 4c00ba823bbb22bd0b549766c9ac2a376d0e7b46 Mon Sep 17 00:00:00 2001 From: stom79 Date: Sat, 29 Dec 2018 12:09:02 +0100 Subject: [PATCH] Following Misskey - step 1 --- .../mastodon/activities/BaseMainActivity.java | 42 +++- .../asynctasks/RetrieveFeedsAsyncTask.java | 10 +- .../fr/gouv/etalab/mastodon/client/API.java | 228 ++++++++++++++++++ .../mastodon/drawers/StatusListAdapter.java | 6 +- .../fragments/DisplayStatusFragment.java | 2 +- app/src/main/res/drawable-hdpi/misskey.png | Bin 0 -> 1136 bytes app/src/main/res/drawable-ldpi/misskey.png | Bin 0 -> 500 bytes app/src/main/res/drawable-mdpi/misskey.png | Bin 0 -> 650 bytes app/src/main/res/drawable-xhdpi/misskey.png | Bin 0 -> 1268 bytes app/src/main/res/drawable-xxhdpi/misskey.png | Bin 0 -> 2196 bytes app/src/main/res/drawable-xxxhdpi/misskey.png | Bin 0 -> 2649 bytes app/src/main/res/layout/search_instance.xml | 5 + app/src/main/res/menu/remote_instances.xml | 10 + app/src/main/res/values/strings.xml | 2 + 14 files changed, 297 insertions(+), 8 deletions(-) create mode 100644 app/src/main/res/drawable-hdpi/misskey.png create mode 100644 app/src/main/res/drawable-ldpi/misskey.png create mode 100644 app/src/main/res/drawable-mdpi/misskey.png create mode 100644 app/src/main/res/drawable-xhdpi/misskey.png create mode 100644 app/src/main/res/drawable-xxhdpi/misskey.png create mode 100644 app/src/main/res/drawable-xxxhdpi/misskey.png diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/activities/BaseMainActivity.java b/app/src/main/java/fr/gouv/etalab/mastodon/activities/BaseMainActivity.java index cfc6346f2..229221bd8 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/activities/BaseMainActivity.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/activities/BaseMainActivity.java @@ -409,8 +409,9 @@ public abstract class BaseMainActivity extends BaseActivity SubMenu submMastodon = popup.getMenu().findItem(R.id.action_show_mastodon).getSubMenu(); SubMenu submPeertube = popup.getMenu().findItem(R.id.action_show_peertube).getSubMenu(); SubMenu submPixelfed = popup.getMenu().findItem(R.id.action_show_pixelfed).getSubMenu(); + SubMenu submMisskey = popup.getMenu().findItem(R.id.action_show_misskey).getSubMenu(); SubMenu submChannel = popup.getMenu().findItem(R.id.action_show_channel).getSubMenu(); - int i = 0, j = 0 , k = 0; + int i = 0, j = 0 , k = 0, l = 0 , m = 0; for (RemoteInstance remoteInstance : remoteInstances) { if (remoteInstance.getType() == null || remoteInstance.getType().equals("MASTODON")) { MenuItem itemPlaceHolder = submMastodon.findItem(R.id.mastodon_instances); @@ -506,11 +507,42 @@ public abstract class BaseMainActivity extends BaseActivity }); j++; } + if (remoteInstance.getType() == null || remoteInstance.getType().equals("MISSKEY")) { + MenuItem itemPlaceHolder = submPixelfed.findItem(R.id.misskey_instance); + if( itemPlaceHolder != null) + itemPlaceHolder.setVisible(false); + MenuItem item = submMisskey.add(0, l, Menu.NONE, remoteInstance.getHost()); + item.setIcon(R.drawable.misskey); + item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + DisplayStatusFragment statusFragment; + Bundle bundle = new Bundle(); + statusFragment = new DisplayStatusFragment(); + bundle.putSerializable("type", RetrieveFeedsAsyncTask.Type.REMOTE_INSTANCE); + bundle.putString("remote_instance", remoteInstance.getHost()); + statusFragment.setArguments(bundle); + String fragmentTag = "REMOTE_INSTANCE"; + instance_id = remoteInstance.getDbID(); + FragmentManager fragmentManager = getSupportFragmentManager(); + fragmentManager.beginTransaction() + .replace(R.id.main_app_container, statusFragment, fragmentTag).commit(); + main_app_container.setVisibility(View.VISIBLE); + viewPager.setVisibility(View.GONE); + tabLayout.setVisibility(View.GONE); + toolbarTitle.setVisibility(View.VISIBLE); + delete_instance.setVisibility(View.VISIBLE); + toolbarTitle.setText(remoteInstance.getHost()); + return false; + } + }); + l++; + } if (remoteInstance.getType() == null || remoteInstance.getType().equals("PEERTUBE")) { MenuItem itemPlaceHolder = submPeertube.findItem(R.id.peertube_instances); if( itemPlaceHolder != null) itemPlaceHolder.setVisible(false); - MenuItem item = submPeertube.add(0, j, Menu.NONE, remoteInstance.getHost()); + MenuItem item = submPeertube.add(0, m, Menu.NONE, remoteInstance.getHost()); item.setIcon(R.drawable.peertube_icon); item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override @@ -535,7 +567,7 @@ public abstract class BaseMainActivity extends BaseActivity return false; } }); - j++; + m++; } } } @@ -568,6 +600,8 @@ public abstract class BaseMainActivity extends BaseActivity new HttpsConnection(BaseMainActivity.this).get("https://" + instanceName + "/api/v1/videos/", 10, null, null); else if( radioGroup.getCheckedRadioButtonId() == R.id.pixelfed_instance) { new HttpsConnection(BaseMainActivity.this).get("https://" + instanceName + "/api/v1/timelines/public", 10, null, null); + }else if( radioGroup.getCheckedRadioButtonId() == R.id.misskey_instance) { + new HttpsConnection(BaseMainActivity.this).post("https://" + instanceName + "/api/notes/local-timeline", 10, null, null); } runOnUiThread(new Runnable() { public void run() { @@ -579,6 +613,8 @@ public abstract class BaseMainActivity extends BaseActivity new InstancesDAO(BaseMainActivity.this, db).insertInstance(instanceName, "PEERTUBE"); else if( radioGroup.getCheckedRadioButtonId() == R.id.pixelfed_instance) new InstancesDAO(BaseMainActivity.this, db).insertInstance(instanceName, "PIXELFED"); + else if( radioGroup.getCheckedRadioButtonId() == R.id.misskey_instance) + new InstancesDAO(BaseMainActivity.this, db).insertInstance(instanceName, "MISSKEY"); DisplayStatusFragment statusFragment; Bundle bundle = new Bundle(); statusFragment = new DisplayStatusFragment(); diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveFeedsAsyncTask.java b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveFeedsAsyncTask.java index 40f57e2e7..0689281fb 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveFeedsAsyncTask.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveFeedsAsyncTask.java @@ -176,7 +176,15 @@ public class RetrieveFeedsAsyncTask extends AsyncTask { status.setType(action); } } - }else { + }else if(remoteInstanceObj != null && remoteInstanceObj.size() > 0 && remoteInstanceObj.get(0).getType().equals("MISSKEY")){ + apiResponse = api.getMisskey(this.instanceName, max_id); + List statusesTemp = apiResponse.getStatuses(); + if( statusesTemp != null){ + for(fr.gouv.etalab.mastodon.client.Entities.Status status: statusesTemp){ + status.setType(action); + } + } + } else { apiResponse = api.getPeertube(this.instanceName, max_id); } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java b/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java index 730d09da2..cbf5f817a 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java @@ -17,6 +17,7 @@ package fr.gouv.etalab.mastodon.client; import android.content.Context; import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; +import android.util.Log; import org.json.JSONArray; import org.json.JSONException; @@ -1039,6 +1040,39 @@ public class API { apiResponse.setHowToVideos(howToVideos); return apiResponse; } + + + /** + * Retrieves Peertube videos from an instance *synchronously* + * @return APIResponse + */ + public APIResponse getMisskey(String instance, String max_id) { + + + HashMap params = new HashMap<>(); + if( max_id != null) + params.put("untilId",max_id); + try { + statuses = new ArrayList<>(); + HttpsConnection httpsConnection = new HttpsConnection(context); + String response = httpsConnection.post(String.format("https://"+instance+"/api/notes/local-timeline", max_id), 60, params, null); + apiResponse.setSince_id(httpsConnection.getSince_id()); + apiResponse.setMax_id(httpsConnection.getMax_id()); + statuses = parseNotes(context, instance, new JSONArray(response)); + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (KeyManagementException e) { + e.printStackTrace(); + } catch (JSONException e) { + e.printStackTrace(); + } + apiResponse.setStatuses(statuses); + return apiResponse; + } /** * Retrieves public timeline for the account *synchronously* * @param local boolean only local timeline @@ -3134,6 +3168,150 @@ public class API { return status; } + + /** + * Parse json response for several notes (Misskey) + * @param jsonArray JSONArray + * @return List + */ + public static List parseNotes(Context context, String instance, JSONArray jsonArray){ + + List statuses = new ArrayList<>(); + try { + int i = 0; + while (i < jsonArray.length() ){ + + JSONObject resobj = jsonArray.getJSONObject(i); + Status status = parseNotes(context, instance, resobj); + i++; + statuses.add(status); + } + + } catch (JSONException e) { + e.printStackTrace(); + } + return statuses; + } + + /** + * Parse json response for unique note (misskey) + * @param resobj JSONObject + * @return Status + */ + @SuppressWarnings("InfiniteRecursion") + public static Status parseNotes(Context context, String instance, JSONObject resobj){ + Status status = new Status(); + try { + status.setId(resobj.get("id").toString()); + status.setUri("https://" + instance + "/notes/" + resobj.get("id").toString()); + status.setCreated_at(Helper.mstStringToDate(context, resobj.get("createdAt").toString())); + status.setIn_reply_to_id(resobj.get("replyId").toString()); + status.setSensitive(false); + if(resobj.get("cw") != null && !resobj.get("cw").toString().equals("null")) + status.setSpoiler_text(resobj.get("cw").toString()); + try { + status.setVisibility(resobj.get("visibility").toString()); + }catch (Exception e){status.setVisibility("public"); e.printStackTrace();} + status.setUrl("https://" + instance + "/notes/" + resobj.get("id").toString()); + //Retrieves attachments + JSONArray arrayAttachement = resobj.getJSONArray("media"); + ArrayList attachments = new ArrayList<>(); + if( arrayAttachement != null){ + for(int j = 0 ; j < arrayAttachement.length() ; j++){ + JSONObject attObj = arrayAttachement.getJSONObject(j); + Attachment attachment = new Attachment(); + attachment.setId(attObj.get("id").toString()); + attachment.setPreview_url(attObj.get("thumbnailUrl").toString()); + attachment.setRemote_url(attObj.get("url").toString()); + if( attObj.get("type").toString().contains("/")){ + attachment.setType(attObj.get("type").toString().split("/")[0]); + }else + attachment.setType(attObj.get("type").toString()); + attachment.setText_url(attObj.get("url").toString()); + attachment.setUrl(attObj.get("url").toString()); + if(attObj.get("isSensitive").toString().equals("true")){ + status.setSensitive(true); + } + try { + attachment.setDescription(attObj.get("comment").toString()); + }catch (JSONException ignore){ignore.printStackTrace();} + attachments.add(attachment); + } + } + try { + status.setCard(parseCardResponse(resobj.getJSONObject("card"))); + }catch (Exception e){status.setCard(null);} + + status.setMedia_attachments(attachments); + //Retrieves mentions + List mentions = new ArrayList<>(); + + status.setAccount(parseMisskeyAccountResponse(context, instance, resobj.getJSONObject("user"))); + status.setContent(resobj.get("text").toString()); + try{ + status.setReplies_count(Integer.valueOf(resobj.get("repliesCount").toString())); + }catch (Exception e){ + status.setReplies_count(-1); + } + try { + status.setFavourited(Boolean.valueOf(resobj.get("isFavorited").toString())); + }catch (Exception e){ + status.setFavourited(false); + } + try{ + if(resobj.getJSONObject("renoteId") != null && !resobj.getJSONObject("renoteId").toString().equals("null")) + status.setReblog(parseStatuses(context, resobj.getJSONObject("renote"))); + }catch (Exception ignored){} + + status.setMentions(mentions); + //Retrieves tags + List tags = new ArrayList<>(); + JSONArray arrayTag = resobj.getJSONArray("tags"); + if( arrayTag != null){ + for(int j = 0 ; j < arrayTag.length() ; j++){ + JSONObject tagObj = arrayTag.getJSONObject(j); + Tag tag = new Tag(); + tag.setName(tagObj.get("name").toString()); + tag.setUrl(tagObj.get("url").toString()); + tags.add(tag); + } + } + status.setTags(tags); + + //Retrieves emjis + List emojiList = new ArrayList<>(); + try { + JSONArray emojisTag = resobj.getJSONArray("emojis"); + if( emojisTag != null){ + for(int j = 0 ; j < emojisTag.length() ; j++){ + JSONObject emojisObj = emojisTag.getJSONObject(j); + Emojis emojis = parseEmojis(emojisObj); + emojiList.add(emojis); + } + } + status.setEmojis(emojiList); + }catch (Exception e){ + status.setEmojis(new ArrayList<>()); + } + + //Retrieve Application + Application application = new Application(); + try { + if(resobj.getJSONObject("application") != null){ + application.setName(resobj.getJSONObject("application").getString("name")); + application.setWebsite(resobj.getJSONObject("application").getString("website")); + } + }catch (Exception e){ + application = new Application(); + } + status.setApplication(application); + + + } catch (JSONException ignored) {} catch (ParseException e) { + e.printStackTrace(); + } + return status; + } /** * Parse json response an unique instance * @param resobj JSONObject @@ -3421,6 +3599,56 @@ public class API { return account; } + + /** + * Parse json response an unique account + * @param resobj JSONObject + * @return Account + */ + @SuppressWarnings("InfiniteRecursion") + private static Account parseMisskeyAccountResponse(Context context, String instance, JSONObject resobj){ + + Log.v(Helper.TAG,"resobj= " + resobj); + Account account = new Account(); + try { + account.setId(resobj.get("id").toString()); + account.setUsername(resobj.get("username").toString()); + String host = resobj.get("host").toString(); + String acct; + if( host == null || host.equals("null")) + acct = resobj.get("username").toString(); + else + acct = resobj.get("username").toString() + "@" + host; + account.setAcct(acct); + account.setDisplay_name(resobj.get("name").toString()); + account.setCreated_at(new Date()); + + account.setUrl("https://" + instance + "/@"+account.getUsername()); + account.setAvatar(resobj.get("avatarUrl").toString()); + account.setAvatar_static(resobj.get("avatarUrl").toString()); + try { + account.setBot(Boolean.parseBoolean(resobj.get("isBot").toString())); + }catch (Exception e){ + account.setBot(false); + } + //Retrieves emjis + List emojiList = new ArrayList<>(); + try { + JSONArray emojisTag = resobj.getJSONArray("emojis"); + if( emojisTag != null){ + for(int j = 0 ; j < emojisTag.length() ; j++){ + JSONObject emojisObj = emojisTag.getJSONObject(j); + Emojis emojis = parseEmojis(emojisObj); + emojiList.add(emojis); + } + } + account.setEmojis(emojiList); + }catch (Exception e){ + account.setEmojis(new ArrayList<>()); + } + } catch (JSONException ignored) {} + return account; + } /** * Parse json response for list of accounts * @param jsonArray JSONArray diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/drawers/StatusListAdapter.java b/app/src/main/java/fr/gouv/etalab/mastodon/drawers/StatusListAdapter.java index bdf4a1dc0..d2bbbf1ea 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/drawers/StatusListAdapter.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/drawers/StatusListAdapter.java @@ -437,7 +437,7 @@ public class StatusListAdapter extends RecyclerView.Adapter implements OnPostAct boolean isCompactMode = sharedpreferences.getBoolean(Helper.SET_COMPACT_MODE, false); if( type == RetrieveFeedsAsyncTask.Type.CONTEXT && position == conversationPosition) return FOCUSED_STATUS; - else if( !Helper.filterToots(context, statuses.get(position), timedMute, type)) + else if( type != RetrieveFeedsAsyncTask.Type.REMOTE_INSTANCE && !Helper.filterToots(context, statuses.get(position), timedMute, type)) return HIDDEN_STATUS; else return isCompactMode?COMPACT_STATUS:DISPLAYED_STATUS; @@ -1091,8 +1091,8 @@ public class StatusListAdapter extends RecyclerView.Adapter implements OnPostAct } - - holder.status_mention_spoiler.setText(Helper.makeMentionsClick(context,status.getMentions()), TextView.BufferType.SPANNABLE); + if( status.getMentions() != null) + holder.status_mention_spoiler.setText(Helper.makeMentionsClick(context,status.getMentions()), TextView.BufferType.SPANNABLE); holder.status_mention_spoiler.setMovementMethod(LinkMovementMethod.getInstance()); if( getItemViewType(viewHolder.getAdapterPosition()) != COMPACT_STATUS ) { diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayStatusFragment.java b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayStatusFragment.java index 352c81c93..7281c6c71 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayStatusFragment.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayStatusFragment.java @@ -179,7 +179,7 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn statusListAdapter = new StatusListAdapter(context, tagTimeline, targetedId, isOnWifi, this.statuses); lv_status.setAdapter(statusListAdapter); } - }else if( search_peertube == null && (instanceType == null || instanceType.equals("MASTODON") || instanceType.equals("PIXELFED"))) { + }else if( search_peertube == null && (instanceType == null || instanceType.equals("MASTODON") || instanceType.equals("PIXELFED") || instanceType.equals("MISSKEY"))) { BaseMainActivity.displayPeertube = null; if( instanceType != null && instanceType.equals("PIXELFED")) type = RetrieveFeedsAsyncTask.Type.PIXELFED; diff --git a/app/src/main/res/drawable-hdpi/misskey.png b/app/src/main/res/drawable-hdpi/misskey.png new file mode 100644 index 0000000000000000000000000000000000000000..2961e94faf8e2e0dc8bb6779573c7d2d095a2d45 GIT binary patch literal 1136 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/search_instance.xml b/app/src/main/res/layout/search_instance.xml index 57272863e..ce2d5b894 100644 --- a/app/src/main/res/layout/search_instance.xml +++ b/app/src/main/res/layout/search_instance.xml @@ -31,5 +31,10 @@ android:layout_height="wrap_content" android:text="@string/pixelfed_instance" /> + \ No newline at end of file diff --git a/app/src/main/res/menu/remote_instances.xml b/app/src/main/res/menu/remote_instances.xml index 07ad2ed39..92e1ac2a7 100644 --- a/app/src/main/res/menu/remote_instances.xml +++ b/app/src/main/res/menu/remote_instances.xml @@ -37,6 +37,16 @@ + + + + + All these words (space-separated) None of these words (space-separated) Change column name + No Misskey instances + Misskey instance \ No newline at end of file