summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas <tschneider.ac@gmail.com>2023-03-11 11:12:21 +0100
committerThomas <tschneider.ac@gmail.com>2023-03-11 11:12:21 +0100
commit1b88633887a96a39b1f1cf778edd0e5d86fc0a0f (patch)
treed7b6cf501d08a7100946bee368c08eff2671ff91
parentbc274451034a214619950d9d4a71811e4bc58411 (diff)
Add fetching messagefetch_indicator
-rw-r--r--app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Status.java2
-rw-r--r--app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusAdapter.java74
-rw-r--r--app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTimeline.java63
-rw-r--r--app/src/main/res/layouts/mastodon/layout/drawer_message_fetching.xml24
-rw-r--r--app/src/main/res/values/strings.xml1
5 files changed, 124 insertions, 40 deletions
diff --git a/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Status.java b/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Status.java
index 7688d3e32..e81d537f8 100644
--- a/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Status.java
+++ b/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Status.java
@@ -114,6 +114,8 @@ public class Status implements Serializable, Cloneable {
public transient boolean isFetchMore = false;
+ public transient boolean isFetching = false;
+
public transient PositionFetchMore positionFetchMore = PositionFetchMore.BOTTOM;
public Attachment art_attachment;
diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusAdapter.java b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusAdapter.java
index a1e34c3d3..741e47f43 100644
--- a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusAdapter.java
+++ b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusAdapter.java
@@ -120,6 +120,7 @@ import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.databinding.DrawerFetchMoreBinding;
+import app.fedilab.android.databinding.DrawerMessageFetchingBinding;
import app.fedilab.android.databinding.DrawerStatusArtBinding;
import app.fedilab.android.databinding.DrawerStatusBinding;
import app.fedilab.android.databinding.DrawerStatusFilteredBinding;
@@ -1621,12 +1622,16 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
videoSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
.createMediaSource(mediaItem);
}
- ExoPlayer player = new ExoPlayer.Builder(context).build();
- player.setRepeatMode(Player.REPEAT_MODE_ONE);
- layoutMediaBinding.mediaVideo.setPlayer(player);
- player.setMediaSource(videoSource);
- player.prepare();
- player.setPlayWhenReady(true);
+ try {
+ ExoPlayer player = new ExoPlayer.Builder(context).build();
+ player.setRepeatMode(Player.REPEAT_MODE_ONE);
+ layoutMediaBinding.mediaVideo.setPlayer(player);
+ player.setMediaSource(videoSource);
+ player.prepare();
+ player.setPlayWhenReady(true);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
int finalMediaPosition = mediaPosition;
layoutMediaBinding.mediaVideo.setOnClickListener(v -> {
@@ -1686,12 +1691,16 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
videoSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
.createMediaSource(mediaItem);
}
- ExoPlayer player = new ExoPlayer.Builder(context).build();
- player.setRepeatMode(Player.REPEAT_MODE_ONE);
- layoutMediaBinding.mediaVideo.setPlayer(player);
- player.setMediaSource(videoSource);
- player.prepare();
- player.setPlayWhenReady(true);
+ try {
+ ExoPlayer player = new ExoPlayer.Builder(context).build();
+ player.setRepeatMode(Player.REPEAT_MODE_ONE);
+ layoutMediaBinding.mediaVideo.setPlayer(player);
+ player.setMediaSource(videoSource);
+ player.prepare();
+ player.setPlayWhenReady(true);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
int finalMediaPosition = mediaPosition;
layoutMediaBinding.mediaVideo.setOnClickListener(v -> {
final int timeout = sharedpreferences.getInt(context.getString(R.string.SET_NSFW_TIMEOUT), 5);
@@ -2447,6 +2456,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
}
drawerFetchMoreBinding.fetchMoreMin.setOnClickListener(v -> {
status.isFetchMore = false;
+ status.isFetching = true;
int position = holder.getBindingAdapterPosition();
adapter.notifyItemChanged(position);
if (position < statusList.size() - 1) {
@@ -2456,25 +2466,27 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
} else {
fromId = status.id;
}
- fetchMoreCallBack.onClickMinId(fromId);
+ fetchMoreCallBack.onClickMinId(fromId, status);
}
});
drawerFetchMoreBinding.fetchMoreMax.setOnClickListener(v -> {
//We hide the button
status.isFetchMore = false;
+ status.isFetching = true;
String fromId;
if (status.positionFetchMore == Status.PositionFetchMore.TOP || holder.getBindingAdapterPosition() == 0) {
fromId = statusList.get(holder.getBindingAdapterPosition()).id;
} else {
fromId = statusList.get(holder.getBindingAdapterPosition() - 1).id;
}
- fetchMoreCallBack.onClickMaxId(fromId);
+ fetchMoreCallBack.onClickMaxId(fromId, status);
adapter.notifyItemChanged(holder.getBindingAdapterPosition());
});
} else {
holder.binding.fetchMoreContainerBottom.setVisibility(View.GONE);
holder.binding.fetchMoreContainerTop.setVisibility(View.GONE);
status.isFetchMore = false;
+ status.isFetching = true;
int position = holder.getBindingAdapterPosition();
String statusIdMin = null, statusIdMax;
if (position < statusList.size() - 1) {
@@ -2490,6 +2502,23 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
statusIdMax = statusList.get(holder.getBindingAdapterPosition() - 1).id;
}
fetchMoreCallBack.autoFetch(statusIdMin, statusIdMax, status);
+ adapter.notifyItemChanged(holder.getBindingAdapterPosition());
+ }
+ } else if (status.isFetching) {
+ DrawerMessageFetchingBinding drawerMessageFetchingBinding = DrawerMessageFetchingBinding.inflate(LayoutInflater.from(context));
+ LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ drawerMessageFetchingBinding.fetchingContainer.setLayoutParams(lp);
+ drawerMessageFetchingBinding.fetchingProgress.getIndeterminateDrawable().setColorFilter(ThemeHelper.getAttColor(context, R.attr.colorPrimary), PorterDuff.Mode.SRC_IN);
+ if (status.positionFetchMore == Status.PositionFetchMore.BOTTOM) {
+ holder.binding.fetchMoreContainerBottom.setVisibility(View.GONE);
+ holder.binding.fetchMoreContainerTop.setVisibility(View.VISIBLE);
+ holder.binding.fetchMoreContainerTop.removeAllViews();
+ holder.binding.fetchMoreContainerTop.addView(drawerMessageFetchingBinding.getRoot());
+ } else {
+ holder.binding.fetchMoreContainerBottom.setVisibility(View.VISIBLE);
+ holder.binding.fetchMoreContainerTop.setVisibility(View.GONE);
+ holder.binding.fetchMoreContainerBottom.removeAllViews();
+ holder.binding.fetchMoreContainerBottom.addView(drawerMessageFetchingBinding.getRoot());
}
} else {
holder.binding.fetchMoreContainerBottom.setVisibility(View.GONE);
@@ -2996,6 +3025,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
holder.bindingFilteredHide.layoutFetchMore.fetchMoreContainer.setVisibility(View.VISIBLE);
holder.bindingFilteredHide.layoutFetchMore.fetchMoreMin.setOnClickListener(v -> {
status.isFetchMore = false;
+ status.isFetching = true;
notifyItemChanged(holder.getBindingAdapterPosition());
if (holder.getBindingAdapterPosition() < statusList.size() - 1) {
String fromId;
@@ -3004,12 +3034,13 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
} else {
fromId = status.id;
}
- fetchMoreCallBack.onClickMinId(fromId);
+ fetchMoreCallBack.onClickMinId(fromId, status);
}
});
holder.bindingFilteredHide.layoutFetchMore.fetchMoreMax.setOnClickListener(v -> {
//We hide the button
status.isFetchMore = false;
+ status.isFetching = true;
notifyItemChanged(holder.getBindingAdapterPosition());
String fromId;
if (status.positionFetchMore == Status.PositionFetchMore.TOP || holder.getBindingAdapterPosition() == 0) {
@@ -3017,11 +3048,12 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
} else {
fromId = statusList.get(holder.getBindingAdapterPosition() - 1).id;
}
- fetchMoreCallBack.onClickMaxId(fromId);
+ fetchMoreCallBack.onClickMaxId(fromId, status);
});
} else {
status.isFetchMore = false;
+ status.isFetching = true;
String minId = null, maxId;
if (holder.getBindingAdapterPosition() < statusList.size() - 1) {
if (status.positionFetchMore == Status.PositionFetchMore.TOP) {
@@ -3053,6 +3085,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
holder.bindingFiltered.layoutFetchMore.fetchMoreContainer.setVisibility(View.VISIBLE);
holder.bindingFiltered.layoutFetchMore.fetchMoreMin.setOnClickListener(v -> {
status.isFetchMore = false;
+ status.isFetching = true;
notifyItemChanged(holder.getBindingAdapterPosition());
if (holder.getBindingAdapterPosition() < statusList.size() - 1) {
String fromId;
@@ -3061,19 +3094,20 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
} else {
fromId = status.id;
}
- fetchMoreCallBack.onClickMinId(fromId);
+ fetchMoreCallBack.onClickMinId(fromId, status);
}
});
holder.bindingFiltered.layoutFetchMore.fetchMoreMax.setOnClickListener(v -> {
//We hide the button
status.isFetchMore = false;
+ status.isFetching = true;
String fromId;
if (status.positionFetchMore == Status.PositionFetchMore.TOP || holder.getBindingAdapterPosition() == 0) {
fromId = statusList.get(holder.getBindingAdapterPosition()).id;
} else {
fromId = statusList.get(holder.getBindingAdapterPosition() - 1).id;
}
- fetchMoreCallBack.onClickMaxId(fromId);
+ fetchMoreCallBack.onClickMaxId(fromId, status);
notifyItemChanged(holder.getBindingAdapterPosition());
});
} else {
@@ -3194,9 +3228,9 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
public interface FetchMoreCallBack {
- void onClickMinId(String min_id);
+ void onClickMinId(String min_id, Status fetchStatus);
- void onClickMaxId(String max_id);
+ void onClickMaxId(String max_id, Status fetchStatus);
void autoFetch(String min_id, String max_id, Status status);
}
diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTimeline.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTimeline.java
index 31add35e2..c753bcaf9 100644
--- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTimeline.java
+++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTimeline.java
@@ -434,10 +434,17 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
*
* @param fetched_statuses Statuses
*/
- private synchronized void dealWithPagination(Statuses fetched_statuses, DIRECTION direction, boolean fetchingMissing, boolean canScroll) {
+ private synchronized void dealWithPagination(Statuses fetched_statuses, DIRECTION direction, boolean fetchingMissing, boolean canScroll, Status fetchStatus) {
if (binding == null || !isAdded() || getActivity() == null) {
return;
}
+ if (fetchStatus != null) {
+ int position = getPosition(fetchStatus);
+ if (position >= 0 && position < timelineStatuses.size()) {
+ timelineStatuses.get(position).isFetching = false;
+ statusAdapter.notifyItemChanged(position);
+ }
+ }
binding.swipeContainer.setRefreshing(false);
binding.loadingNextElements.setVisibility(View.GONE);
flagLoading = false;
@@ -509,7 +516,15 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
if (direction == DIRECTION.SCROLL_TOP) {
new Handler().postDelayed(() -> binding.recyclerView.scrollToPosition(0), 200);
}
+ }
+ /**
+ * Update view and pagination when scrolling down
+ *
+ * @param fetched_statuses Statuses
+ */
+ private synchronized void dealWithPagination(Statuses fetched_statuses, DIRECTION direction, boolean fetchingMissing, boolean canScroll) {
+ dealWithPagination(fetched_statuses, direction, fetchingMissing, canScroll, null);
}
/**
@@ -902,13 +917,21 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
}
}
-
/**
* Router for timelines
*
* @param direction - DIRECTION null if first call, then is set to TOP or BOTTOM depending of scroll
*/
private void route(DIRECTION direction, boolean fetchingMissing) {
+ route(direction, fetchingMissing, null);
+ }
+
+ /**
+ * Router for timelines
+ *
+ * @param direction - DIRECTION null if first call, then is set to TOP or BOTTOM depending of scroll
+ */
+ private void route(DIRECTION direction, boolean fetchingMissing, Status fetchStatus) {
if (binding == null || !isAdded() || getActivity() == null) {
return;
}
@@ -933,14 +956,14 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
});
} else if (direction == DIRECTION.BOTTOM) {
timelinesVM.getNitter(pinnedTimeline.remoteInstance.host, max_id)
- .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true));
+ .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true, fetchStatus));
} else if (direction == DIRECTION.TOP) {
flagLoading = false;
} else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
timelinesVM.getNitter(pinnedTimeline.remoteInstance.host, null)
.observe(getViewLifecycleOwner(), statusesRefresh -> {
if (statusAdapter != null) {
- dealWithPagination(statusesRefresh, direction, true, true);
+ dealWithPagination(statusesRefresh, direction, true, true, fetchStatus);
} else {
initializeStatusesCommonView(statusesRefresh);
}
@@ -956,14 +979,14 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
} else if (direction == DIRECTION.BOTTOM) {
timelinesVM.getMisskey(remoteInstance, max_id, MastodonHelper.statusesPerCall(requireActivity()))
- .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true));
+ .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true, fetchStatus));
} else if (direction == DIRECTION.TOP) {
flagLoading = false;
} else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
timelinesVM.getMisskey(remoteInstance, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesRefresh -> {
if (statusAdapter != null) {
- dealWithPagination(statusesRefresh, direction, true, true);
+ dealWithPagination(statusesRefresh, direction, true, true, fetchStatus);
} else {
initializeStatusesCommonView(statusesRefresh);
}
@@ -976,14 +999,14 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
} else if (direction == DIRECTION.BOTTOM) {
timelinesVM.getPeertube(remoteInstance, String.valueOf(timelineStatuses.size()), MastodonHelper.statusesPerCall(requireActivity()))
- .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true));
+ .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true, fetchStatus));
} else if (direction == DIRECTION.TOP) {
flagLoading = false;
} else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
timelinesVM.getPeertube(remoteInstance, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesRefresh -> {
if (statusAdapter != null) {
- dealWithPagination(statusesRefresh, direction, true, true);
+ dealWithPagination(statusesRefresh, direction, true, true, fetchStatus);
} else {
initializeStatusesCommonView(statusesRefresh);
}
@@ -1065,7 +1088,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
}
} else if (direction == DIRECTION.BOTTOM) {
accountsVM.getAccountStatuses(tempInstance, tempToken, accountId, max_id, null, null, exclude_replies, exclude_reblogs, media_only, false, MastodonHelper.statusesPerCall(requireActivity()))
- .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true));
+ .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true, fetchStatus));
} else {
flagLoading = false;
}
@@ -1090,7 +1113,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
Statuses statuses = new Statuses();
statuses.statuses = results.statuses;
statuses.pagination = new Pagination();
- dealWithPagination(statuses, direction, false, true);
+ dealWithPagination(statuses, direction, false, true, fetchStatus);
}
});
} else {
@@ -1115,7 +1138,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
} else if (direction == DIRECTION.BOTTOM) {
accountsVM.getFavourites(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, String.valueOf(MastodonHelper.statusesPerCall(requireActivity())), null, max_id)
- .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true));
+ .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true, fetchStatus));
} else {
flagLoading = false;
}
@@ -1125,7 +1148,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
} else if (direction == DIRECTION.BOTTOM) {
accountsVM.getBookmarks(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, String.valueOf(MastodonHelper.statusesPerCall(requireActivity())), max_id, null, null)
- .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true));
+ .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true, fetchStatus));
} else {
flagLoading = false;
}
@@ -1135,7 +1158,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
} else if (direction == DIRECTION.BOTTOM) {
timelinesVM.getStatusTrends(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, max_id, MastodonHelper.statusesPerCall(requireActivity()))
- .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true));
+ .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true, fetchStatus));
} else {
flagLoading = false;
}
@@ -1145,7 +1168,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
} else if (direction == DIRECTION.BOTTOM) {
timelinesVM.getStatusTrends(null, publicTrendsDomain, max_id, MastodonHelper.statusesPerCall(requireActivity()))
- .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true));
+ .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true, fetchStatus));
} else {
flagLoading = false;
}
@@ -1163,26 +1186,26 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
}
@Override
- public void onClickMinId(String min_id) {
+ public void onClickMinId(String min_id, Status fetchStatus) {
//Fetch more has been pressed
min_id_fetch_more = min_id;
route(DIRECTION.TOP, true);
}
@Override
- public void onClickMaxId(String max_id) {
+ public void onClickMaxId(String max_id, Status fetchStatus) {
max_id_fetch_more = max_id;
- route(DIRECTION.BOTTOM, true);
+ route(DIRECTION.BOTTOM, true, fetchStatus);
}
@Override
- public void autoFetch(String min_id, String max_id, Status statusToUpdate) {
+ public void autoFetch(String min_id, String max_id, Status fetchStatus) {
if (scrollingUp) {
min_id_fetch_more = min_id;
- route(DIRECTION.TOP, true);
+ route(DIRECTION.TOP, true, fetchStatus);
} else {
max_id_fetch_more = max_id;
- route(DIRECTION.BOTTOM, true);
+ route(DIRECTION.BOTTOM, true, fetchStatus);
}
}
diff --git a/app/src/main/res/layouts/mastodon/layout/drawer_message_fetching.xml b/app/src/main/res/layouts/mastodon/layout/drawer_message_fetching.xml
new file mode 100644
index 000000000..17ec3cc9b
--- /dev/null
+++ b/app/src/main/res/layouts/mastodon/layout/drawer_message_fetching.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/fetching_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:paddingHorizontal="6dp">
+
+ <ProgressBar
+ android:id="@+id/fetching_progress"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:indeterminate="true" />
+
+ <androidx.appcompat.widget.AppCompatTextView
+ style="@style/TextAppearance.AppCompat.Title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/fetching_messages"
+ android:textAlignment="center"
+ android:textColor="?colorPrimary" />
+</androidx.appcompat.widget.LinearLayoutCompat> \ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b3dfcecf6..1f24432fd 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1958,4 +1958,5 @@
<string name="load_media_remotely">Load media remotely</string>
<string name="toast_error_media">Media cannot be loaded!</string>
<string name="fetch_remote_media">Automatically fetch remote media when they are not available</string>
+ <string name="fetching_messages">Fetching messages</string>
</resources> \ No newline at end of file