diff --git a/app/src/main/java/app/fedilab/android/client/entities/StatusCache.java b/app/src/main/java/app/fedilab/android/client/entities/StatusCache.java index f4e1265c..e6d951c3 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/StatusCache.java +++ b/app/src/main/java/app/fedilab/android/client/entities/StatusCache.java @@ -211,11 +211,11 @@ public class StatusCache { String selection = Sqlite.COL_INSTANCE + "='" + instance + "' AND " + Sqlite.COL_USER_ID + "= '" + user_id + "'"; String limit = String.valueOf(MastodonHelper.statusesPerCall(context)); if (max_id == null && min_id != null) { - selection += "AND " + Sqlite.COL_STATUS_ID + " >= '" + min_id + "'"; + selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + min_id + "'"; } else if (max_id != null && min_id == null) { selection += "AND " + Sqlite.COL_STATUS_ID + " < '" + max_id + "'"; } else if (max_id != null) { - selection += "AND " + Sqlite.COL_STATUS_ID + " >= '" + min_id + "' AND " + Sqlite.COL_STATUS_ID + " < '" + max_id + "'"; + selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + min_id + "' AND " + Sqlite.COL_STATUS_ID + " < '" + max_id + "'"; limit = null; } try { diff --git a/app/src/main/java/app/fedilab/android/helper/DividerDecoration.java b/app/src/main/java/app/fedilab/android/helper/DividerDecoration.java index a0bd2086..9ed2c742 100644 --- a/app/src/main/java/app/fedilab/android/helper/DividerDecoration.java +++ b/app/src/main/java/app/fedilab/android/helper/DividerDecoration.java @@ -44,17 +44,7 @@ public class DividerDecoration extends RecyclerView.ItemDecoration { R.color.decoration_2, R.color.decoration_3, R.color.decoration_4, - R.color.decoration_5, - R.color.decoration_6, - R.color.decoration_7, - R.color.decoration_8, - R.color.decoration_9, - R.color.decoration_10, - R.color.decoration_11, - R.color.decoration_12, - R.color.decoration_13, - R.color.decoration_14, - R.color.decoration_15 + R.color.decoration_5 ); public DividerDecoration(Context context, List statuses) { diff --git a/app/src/main/java/app/fedilab/android/helper/Helper.java b/app/src/main/java/app/fedilab/android/helper/Helper.java index dce0f1d0..8c758d2b 100644 --- a/app/src/main/java/app/fedilab/android/helper/Helper.java +++ b/app/src/main/java/app/fedilab/android/helper/Helper.java @@ -168,6 +168,7 @@ public class Helper { public static final String ARG_EXCLUDED_NOTIFICATION_TYPE = "ARG_EXCLUDED_NOTIFICATION_TYPE"; public static final String ARG_STATUS = "ARG_STATUS"; public static final String ARG_STATUS_DELETED = "ARG_STATUS_DELETED"; + public static final String ARG_STATUS_POSTED = "ARG_STATUS_POSTED"; public static final String ARG_STATUS_ACTION = "ARG_STATUS_ACTION"; public static final String ARG_STATUS_ACCOUNT_ID_DELETED = "ARG_STATUS_ACCOUNT_ID_DELETED"; diff --git a/app/src/main/java/app/fedilab/android/services/PostMessageService.java b/app/src/main/java/app/fedilab/android/services/PostMessageService.java index 0af6234d..15d6f92c 100644 --- a/app/src/main/java/app/fedilab/android/services/PostMessageService.java +++ b/app/src/main/java/app/fedilab/android/services/PostMessageService.java @@ -51,6 +51,7 @@ import app.fedilab.android.client.mastodon.entities.ScheduledStatus; import app.fedilab.android.client.mastodon.entities.Status; import app.fedilab.android.exception.DBException; import app.fedilab.android.helper.Helper; +import app.fedilab.android.ui.drawer.StatusAdapter; import okhttp3.MultipartBody; import okhttp3.OkHttpClient; import retrofit2.Call; @@ -233,6 +234,9 @@ public class PostMessageService extends IntentService { if (statusResponse.isSuccessful()) { Status statusReply = statusResponse.body(); + if (statusReply != null) { + StatusAdapter.sendAction(this, Helper.ARG_STATUS_POSTED, statusReply, null); + } if (firstSendMessage == null && statusReply != null) { firstSendMessage = statusReply; } diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java index 38571c9b..5edadc53 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java @@ -223,6 +223,12 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.actionButtonBookmark.setInActiveImageTintColor(theme_icons_color); holder.binding.actionButtonBoost.setInActiveImageTintColor(theme_icons_color); + if (status.pinned) { + holder.binding.statusPinned.setVisibility(View.VISIBLE); + } else { + holder.binding.statusPinned.setVisibility(View.GONE); + } + if (theme_text_header_2_line != -1) { Pattern hashAcct; SpannableString wordToSpan; @@ -242,6 +248,7 @@ public class StatusAdapter extends RecyclerView.Adapter } } Helper.changeDrawableColor(context, holder.binding.statusBoostIcon, theme_text_header_2_line); + Helper.changeDrawableColor(context, holder.binding.statusPinned, theme_text_header_2_line); } if (theme_statuses_color != -1) { holder.binding.cardviewContainer.setBackgroundColor(theme_statuses_color); @@ -1426,7 +1433,7 @@ public class StatusAdapter extends RecyclerView.Adapter * @param status - Status that is sent (can be null) * @param id - Id of an account (can be null) */ - private static void sendAction(@NonNull Context context, @NonNull String type, @Nullable Status status, @Nullable String id) { + public static void sendAction(@NonNull Context context, @NonNull String type, @Nullable Status status, @Nullable String id) { Bundle b = new Bundle(); if (status != null) { b.putSerializable(type, status); diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java index 454647f0..c8480b7c 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java @@ -16,7 +16,12 @@ package app.fedilab.android.ui.fragment.timeline; import static app.fedilab.android.activities.ContextActivity.expand; +import android.content.BroadcastReceiver; +import android.content.Intent; +import android.content.IntentFilter; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -24,6 +29,7 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.recyclerview.widget.LinearLayoutManager; import java.util.ArrayList; @@ -31,11 +37,13 @@ import java.util.List; import app.fedilab.android.BaseMainActivity; import app.fedilab.android.R; +import app.fedilab.android.activities.ContextActivity; import app.fedilab.android.client.mastodon.entities.Context; import app.fedilab.android.client.mastodon.entities.Status; import app.fedilab.android.databinding.FragmentPaginationBinding; import app.fedilab.android.helper.DividerDecoration; import app.fedilab.android.helper.Helper; +import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.ui.drawer.StatusAdapter; import app.fedilab.android.viewmodel.mastodon.StatusesVM; @@ -52,6 +60,93 @@ public class FragmentMastodonContext extends Fragment { private Status firstStatus; private boolean pullToRefresh; + + //Handle actions that can be done in other fragments + private final BroadcastReceiver receive_action = new BroadcastReceiver() { + @Override + public void onReceive(android.content.Context context, Intent intent) { + Bundle b = intent.getExtras(); + if (b != null) { + Status receivedStatus = (Status) b.getSerializable(Helper.ARG_STATUS_ACTION); + String delete_statuses_for_user = b.getString(Helper.ARG_STATUS_ACCOUNT_ID_DELETED); + Status status_to_delete = (Status) b.getSerializable(Helper.ARG_STATUS_DELETED); + Status statusPosted = (Status) b.getSerializable(Helper.ARG_STATUS_POSTED); + if (receivedStatus != null && statusAdapter != null) { + int position = getPosition(receivedStatus); + if (position >= 0) { + statuses.get(position).reblog = receivedStatus.reblog; + statuses.get(position).favourited = receivedStatus.favourited; + statuses.get(position).bookmarked = receivedStatus.bookmarked; + statusAdapter.notifyItemChanged(position); + } + } else if (delete_statuses_for_user != null && statusAdapter != null) { + List statusesToRemove = new ArrayList<>(); + for (Status status : statuses) { + if (status.account.id.equals(delete_statuses_for_user)) { + statusesToRemove.add(status); + } + } + for (Status statusToRemove : statusesToRemove) { + int position = getPosition(statusToRemove); + if (position >= 0) { + statuses.remove(position); + statusAdapter.notifyItemRemoved(position); + } + } + } else if (status_to_delete != null && statusAdapter != null) { + int position = getPosition(status_to_delete); + if (position >= 0) { + statuses.remove(position); + statusAdapter.notifyItemRemoved(position); + } + } else if (statusPosted != null && statusAdapter != null) { + if (requireActivity() instanceof ContextActivity) { + new Thread(() -> { + Status convertStatus = SpannableHelper.convertStatus(context, statusPosted); + Handler mainHandler = new Handler(Looper.getMainLooper()); + Runnable myRunnable = () -> { + int i = 0; + for (Status status : statuses) { + if (status.id.equals(convertStatus.in_reply_to_id)) { + statuses.add(i, convertStatus); + statusAdapter.notifyItemInserted(i); + if (requireActivity() instanceof ContextActivity) { + //Redraw decorations + statusAdapter.notifyItemRangeChanged(0, statuses.size()); + } + break; + } + i++; + } + }; + mainHandler.post(myRunnable); + }).start(); + } + } + } + } + }; + + + /** + * Return the position of the status in the ArrayList + * + * @param status - Status to fetch + * @return position or -1 if not found + */ + private int getPosition(Status status) { + int position = 0; + boolean found = false; + for (Status _status : statuses) { + if (_status.id.compareTo(status.id) == 0) { + found = true; + break; + } + position++; + } + return found ? position : -1; + } + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -92,6 +187,7 @@ public class FragmentMastodonContext extends Fragment { statusesVM.getContext(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, focusedStatus.id) .observe(getViewLifecycleOwner(), this::initializeContextView); } + LocalBroadcastManager.getInstance(requireActivity()).registerReceiver(receive_action, new IntentFilter(Helper.RECEIVE_STATUS_ACTION)); return binding.getRoot(); } @@ -155,10 +251,11 @@ public class FragmentMastodonContext extends Fragment { @Override public void onDestroyView() { - super.onDestroyView(); binding.recyclerView.setAdapter(null); statusAdapter = null; binding = null; + LocalBroadcastManager.getInstance(requireActivity()).unregisterReceiver(receive_action); + super.onDestroyView(); } } \ No newline at end of file diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java index 7f1b4ab5..a4ffab38 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java @@ -52,6 +52,7 @@ import app.fedilab.android.client.mastodon.entities.Statuses; import app.fedilab.android.databinding.FragmentPaginationBinding; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; +import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.ui.drawer.StatusAdapter; import app.fedilab.android.viewmodel.mastodon.AccountsVM; @@ -91,6 +92,7 @@ public class FragmentMastodonTimeline extends Fragment { Status receivedStatus = (Status) b.getSerializable(Helper.ARG_STATUS_ACTION); String delete_statuses_for_user = b.getString(Helper.ARG_STATUS_ACCOUNT_ID_DELETED); Status status_to_delete = (Status) b.getSerializable(Helper.ARG_STATUS_DELETED); + Status statusPosted = (Status) b.getSerializable(Helper.ARG_STATUS_DELETED); if (receivedStatus != null && statusAdapter != null) { int position = getPosition(receivedStatus); if (position >= 0) { @@ -119,6 +121,16 @@ public class FragmentMastodonTimeline extends Fragment { statuses.remove(position); statusAdapter.notifyItemRemoved(position); } + } else if (statusPosted != null && statusAdapter != null && timelineType == Timeline.TimeLineEnum.HOME) { + new Thread(() -> { + Status convertStatus = SpannableHelper.convertStatus(context, statusPosted); + Handler mainHandler = new Handler(Looper.getMainLooper()); + Runnable myRunnable = () -> { + statuses.add(0, convertStatus); + statusAdapter.notifyItemInserted(0); + }; + mainHandler.post(myRunnable); + }).start(); } } } @@ -345,12 +357,22 @@ public class FragmentMastodonTimeline extends Fragment { @Override public void onPause() { - super.onPause(); + /* if (mLayoutManager != null) { + int position = mLayoutManager.findFirstVisibleItemPosition(); + new Thread(() -> { + try { + new QuickLoad(requireActivity()).storeTimeline(position, timelineType, statuses, ident); + } catch (Exception ignored) { + } + }).start(); + }*/ storeMarker(); + super.onPause(); } @Override public void onDestroyView() { + //Update last read id for home timeline if (mLayoutManager != null) { int position = mLayoutManager.findFirstVisibleItemPosition(); new Thread(() -> { @@ -360,7 +382,6 @@ public class FragmentMastodonTimeline extends Fragment { } }).start(); } - //Update last read id for home timeline storeMarker(); if (binding != null) { binding.recyclerView.setAdapter(null); diff --git a/app/src/main/res/layout/drawer_status.xml b/app/src/main/res/layout/drawer_status.xml index 94e132fc..db756446 100644 --- a/app/src/main/res/layout/drawer_status.xml +++ b/app/src/main/res/layout/drawer_status.xml @@ -93,9 +93,17 @@ android:layout_height="20dp" android:scaleType="centerInside" android:src="@drawable/ic_person" /> - + + #4b0082 #ee82ee - #E57373 - #BA68C8 + #7986CB + #4DB6AC #FFD54F #64B5F6 - #81C784 + #9E9E9E #4DB6AC #FF8A65 #4DD0E1