summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas <tschneider.ac@gmail.com>2022-09-09 17:24:57 +0200
committerThomas <tschneider.ac@gmail.com>2022-09-09 17:24:57 +0200
commite68db78dc4ce9ddfc5b639155feb0afe5e2aa343 (patch)
treee026e856e13c3051ac9939b95244fde297d2add5
parentc3a00df0670a505583f2efb87a6c5b1127ae51a7 (diff)
Profile media in a grid
-rw-r--r--app/src/main/java/app/fedilab/android/helper/SpannableHelper.java12
-rw-r--r--app/src/main/java/app/fedilab/android/ui/drawer/ImageAdapter.java121
-rw-r--r--app/src/main/java/app/fedilab/android/ui/fragment/media/FragmentMediaProfile.java182
-rw-r--r--app/src/main/java/app/fedilab/android/ui/pageadapter/FedilabProfilePageAdapter.java9
-rw-r--r--app/src/main/res/layout/drawer_media.xml15
5 files changed, 328 insertions, 11 deletions
diff --git a/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java b/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java
index 1e1aa3e32..5869d7eea 100644
--- a/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java
+++ b/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java
@@ -198,9 +198,6 @@ public class SpannableHelper {
final String url = content.toString().substring(matchStart, matchEnd);
- /* if (!url.startsWith("http")) {
- continue;
- }*/
String newURL = Helper.transformURL(context, url);
//If URL has been transformed
if (newURL.compareTo(url) != 0) {
@@ -316,7 +313,7 @@ public class SpannableHelper {
}
}
httpsURLConnection.getInputStream().close();
- if (redirect != null && redirect.compareTo(finalURl1) != 0) {
+ if (redirect != null && finalURl1 != null && redirect.compareTo(finalURl1) != 0) {
URL redirectURL = new URL(redirect);
String host = redirectURL.getHost();
String protocol = redirectURL.getProtocol();
@@ -384,8 +381,11 @@ public class SpannableHelper {
}
textView.setTag(CLICKABLE_SPAN);
Pattern link = Pattern.compile("https?://([\\da-z.-]+\\.[a-z.]{2,10})/(@[\\w._-]*[0-9]*)(/[0-9]+)?$");
- Matcher matcherLink = link.matcher(finalURl2);
- if (matcherLink.find() && !finalURl2.contains("medium.com")) {
+ Matcher matcherLink = null;
+ if (finalURl2 != null) {
+ matcherLink = link.matcher(finalURl2);
+ }
+ if (finalURl2 != null && matcherLink.find() && !finalURl2.contains("medium.com")) {
if (matcherLink.group(3) != null && Objects.requireNonNull(matcherLink.group(3)).length() > 0) { //It's a toot
CrossActionHelper.fetchRemoteStatus(context, currentAccount, finalURl2, new CrossActionHelper.Callback() {
@Override
diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/ImageAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/ImageAdapter.java
new file mode 100644
index 000000000..f4f8f2709
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/ui/drawer/ImageAdapter.java
@@ -0,0 +1,121 @@
+package app.fedilab.android.ui.drawer;
+/* Copyright 2022 Thomas Schneider
+ *
+ * This file is a part of Fedilab
+ *
+ * This program 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.
+ *
+ * Fedilab 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 Fedilab; if not,
+ * see <http://www.gnu.org/licenses>. */
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.core.app.ActivityOptionsCompat;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.bumptech.glide.Glide;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import app.fedilab.android.activities.ContextActivity;
+import app.fedilab.android.activities.MediaActivity;
+import app.fedilab.android.client.entities.api.Attachment;
+import app.fedilab.android.client.entities.api.Status;
+import app.fedilab.android.databinding.DrawerMediaBinding;
+import app.fedilab.android.helper.Helper;
+
+
+public class ImageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+ private final List<Status> statuses;
+ private Context context;
+
+ public ImageAdapter(List<Status> statuses) {
+ this.statuses = statuses;
+ }
+
+ public int getCount() {
+ return statuses.size();
+ }
+
+ public Status getItem(int position) {
+ return statuses.get(position);
+ }
+
+ @NonNull
+ @Override
+ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ context = parent.getContext();
+ DrawerMediaBinding itemBinding = DrawerMediaBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
+ return new ViewHolder(itemBinding);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
+ Status status = statuses.get(position);
+
+ final ViewHolder holder = (ViewHolder) viewHolder;
+
+ if (Helper.isValidContextForGlide(context) && status.art_attachment != null) {
+ if (status.art_attachment.preview_url != null) {
+ Glide.with(context).load(status.art_attachment.preview_url).into(holder.binding.media);
+ } else if (status.art_attachment.url != null) {
+ Glide.with(context).load(status.art_attachment.url).into(holder.binding.media);
+ }
+ }
+ holder.binding.media.setOnClickListener(v -> {
+ Intent mediaIntent = new Intent(context, MediaActivity.class);
+ Bundle b = new Bundle();
+ b.putInt(Helper.ARG_MEDIA_POSITION, position + 1);
+ ArrayList<Attachment> attachmentsTmp = new ArrayList<>();
+ for (Status status1 : statuses) {
+ attachmentsTmp.add(status1.art_attachment);
+ }
+ b.putSerializable(Helper.ARG_MEDIA_ARRAY, new ArrayList<>(attachmentsTmp));
+ mediaIntent.putExtras(b);
+ ActivityOptionsCompat options = ActivityOptionsCompat
+ .makeSceneTransitionAnimation((Activity) context, holder.binding.media, status.media_attachments.get(0).url);
+ // start the new activity
+ context.startActivity(mediaIntent, options.toBundle());
+ });
+
+ holder.binding.media.setOnLongClickListener(v -> {
+ Intent intentContext = new Intent(context, ContextActivity.class);
+ intentContext.putExtra(Helper.ARG_STATUS, status);
+ intentContext.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intentContext);
+ return false;
+ });
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public int getItemCount() {
+ return statuses.size();
+ }
+
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ DrawerMediaBinding binding;
+
+ public ViewHolder(DrawerMediaBinding itemView) {
+ super(itemView.getRoot());
+ binding = itemView;
+ }
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/media/FragmentMediaProfile.java b/app/src/main/java/app/fedilab/android/ui/fragment/media/FragmentMediaProfile.java
new file mode 100644
index 000000000..d1f00d63e
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/ui/fragment/media/FragmentMediaProfile.java
@@ -0,0 +1,182 @@
+package app.fedilab.android.ui.fragment.media;
+/* Copyright 2022 Thomas Schneider
+ *
+ * This file is a part of Fedilab
+ *
+ * This program 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.
+ *
+ * Fedilab 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 Fedilab; if not,
+ * see <http://www.gnu.org/licenses>. */
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import app.fedilab.android.BaseMainActivity;
+import app.fedilab.android.client.entities.api.Account;
+import app.fedilab.android.client.entities.api.Attachment;
+import app.fedilab.android.client.entities.api.Status;
+import app.fedilab.android.client.entities.api.Statuses;
+import app.fedilab.android.databinding.FragmentPaginationBinding;
+import app.fedilab.android.helper.Helper;
+import app.fedilab.android.helper.MastodonHelper;
+import app.fedilab.android.ui.drawer.ImageAdapter;
+import app.fedilab.android.viewmodel.mastodon.AccountsVM;
+
+
+public class FragmentMediaProfile extends Fragment {
+
+ private FragmentPaginationBinding binding;
+ private AccountsVM accountsVM;
+ private Account accountTimeline;
+ private boolean flagLoading;
+ private List<Status> mediaStatuses;
+ private String max_id;
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+ binding = FragmentPaginationBinding.inflate(inflater, container, false);
+ Bundle bundle = this.getArguments();
+ if (bundle != null) {
+ accountTimeline = (Account) getArguments().getSerializable(Helper.ARG_ACCOUNT);
+ }
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ flagLoading = false;
+ accountsVM = new ViewModelProvider(FragmentMediaProfile.this).get(AccountsVM.class);
+ mediaStatuses = new ArrayList<>();
+ accountsVM.getAccountStatuses(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, accountTimeline.id, null, null, null, null, null, true, false, MastodonHelper.statusesPerCall(requireActivity()))
+ .observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
+ }
+
+ /**
+ * Intialize the common view for statuses on different timelines
+ *
+ * @param statuses {@link Statuses}
+ */
+ private void initializeStatusesCommonView(final Statuses statuses) {
+ flagLoading = false;
+ if (binding == null || !isAdded() || getActivity() == null) {
+ return;
+ }
+ binding.loader.setVisibility(View.GONE);
+ binding.noAction.setVisibility(View.GONE);
+ binding.swipeContainer.setRefreshing(false);
+ if (statuses == null || statuses.statuses == null || statuses.statuses.size() == 0) {
+ binding.noAction.setVisibility(View.VISIBLE);
+ return;
+ }
+
+ for (Status status : statuses.statuses) {
+ for (Attachment attachment : status.media_attachments) {
+ try {
+ Status statusTmp = (Status) status.clone();
+ statusTmp.art_attachment = attachment;
+ mediaStatuses.add(statusTmp);
+ } catch (CloneNotSupportedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ ImageAdapter imageAdapter = new ImageAdapter(mediaStatuses);
+
+ flagLoading = statuses.pagination.max_id == null;
+ binding.recyclerView.setVisibility(View.VISIBLE);
+
+ if (max_id == null || (statuses.pagination.max_id != null && statuses.pagination.max_id.compareTo(max_id) < 0)) {
+ max_id = statuses.pagination.max_id;
+ }
+ GridLayoutManager gvLayout = new GridLayoutManager(requireActivity(), 3);
+ binding.recyclerView.setLayoutManager(gvLayout);
+ binding.recyclerView.setAdapter(imageAdapter);
+
+
+ binding.recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
+ if (requireActivity() instanceof BaseMainActivity) {
+ if (dy < 0 && !((BaseMainActivity) requireActivity()).getFloatingVisibility())
+ ((BaseMainActivity) requireActivity()).manageFloatingButton(true);
+ if (dy > 0 && ((BaseMainActivity) requireActivity()).getFloatingVisibility())
+ ((BaseMainActivity) requireActivity()).manageFloatingButton(false);
+ }
+ int firstVisibleItem = gvLayout.findFirstVisibleItemPosition();
+ if (dy > 0) {
+ int visibleItemCount = gvLayout.getChildCount();
+ int totalItemCount = gvLayout.getItemCount();
+ if (firstVisibleItem + visibleItemCount == totalItemCount) {
+ if (!flagLoading) {
+ flagLoading = true;
+ binding.loadingNextElements.setVisibility(View.VISIBLE);
+ accountsVM.getAccountStatuses(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, accountTimeline.id, max_id, null, null, null, null, true, false, MastodonHelper.statusesPerCall(requireActivity()))
+ .observe(getViewLifecycleOwner(), newStatuses -> dealWithPagination(newStatuses));
+ }
+ } else {
+ binding.loadingNextElements.setVisibility(View.GONE);
+ }
+ }
+ }
+ });
+
+ }
+
+
+ /**
+ * Update view and pagination when scrolling down
+ *
+ * @param fetched_statuses Statuses
+ */
+ private synchronized void dealWithPagination(Statuses fetched_statuses) {
+ if (binding == null || !isAdded() || getActivity() == null) {
+ return;
+ }
+ binding.swipeContainer.setRefreshing(false);
+ binding.loadingNextElements.setVisibility(View.GONE);
+ flagLoading = false;
+ if (this.mediaStatuses != null && fetched_statuses != null && fetched_statuses.statuses != null && fetched_statuses.statuses.size() > 0) {
+ flagLoading = fetched_statuses.pagination.max_id == null;
+ binding.noAction.setVisibility(View.GONE);
+ //We have to split media in different statuses
+ List<Status> mediaStatusesNew = new ArrayList<>();
+ for (Status status : fetched_statuses.statuses) {
+ if (status.media_attachments.size() > 1) {
+ for (Attachment attachment : status.media_attachments) {
+ status.media_attachments = new ArrayList<>();
+ status.media_attachments.add(0, attachment);
+ mediaStatusesNew.add(status);
+ }
+ }
+ }
+ this.mediaStatuses.addAll(mediaStatusesNew);
+ if (fetched_statuses.pagination.max_id == null) {
+ flagLoading = true;
+ } else if (max_id == null || fetched_statuses.pagination.max_id.compareTo(max_id) < 0) {
+ max_id = fetched_statuses.pagination.max_id;
+ }
+ } else {
+ flagLoading = true;
+ }
+ }
+}
diff --git a/app/src/main/java/app/fedilab/android/ui/pageadapter/FedilabProfilePageAdapter.java b/app/src/main/java/app/fedilab/android/ui/pageadapter/FedilabProfilePageAdapter.java
index 5e32fbd6b..39369fb23 100644
--- a/app/src/main/java/app/fedilab/android/ui/pageadapter/FedilabProfilePageAdapter.java
+++ b/app/src/main/java/app/fedilab/android/ui/pageadapter/FedilabProfilePageAdapter.java
@@ -25,6 +25,7 @@ import androidx.fragment.app.FragmentStatePagerAdapter;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.app.Timeline;
import app.fedilab.android.helper.Helper;
+import app.fedilab.android.ui.fragment.media.FragmentMediaProfile;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
public class FedilabProfilePageAdapter extends FragmentStatePagerAdapter {
@@ -73,12 +74,10 @@ public class FedilabProfilePageAdapter extends FragmentStatePagerAdapter {
fragmentProfileTimeline.setArguments(bundle);
return fragmentProfileTimeline;
case 2:
- fragmentProfileTimeline = new FragmentMastodonTimeline();
- bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.ACCOUNT_TIMELINE);
+ FragmentMediaProfile fragmentMediaProfile = new FragmentMediaProfile();
bundle.putSerializable(Helper.ARG_ACCOUNT, account);
- bundle.putBoolean(Helper.ARG_SHOW_MEDIA_ONY, true);
- fragmentProfileTimeline.setArguments(bundle);
- return fragmentProfileTimeline;
+ fragmentMediaProfile.setArguments(bundle);
+ return fragmentMediaProfile;
default:
return new FragmentMastodonTimeline();
}
diff --git a/app/src/main/res/layout/drawer_media.xml b/app/src/main/res/layout/drawer_media.xml
new file mode 100644
index 000000000..a542a806e
--- /dev/null
+++ b/app/src/main/res/layout/drawer_media.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/main_container"
+ android:layout_width="wrap_content"
+ android:layout_height="110dp"
+ android:divider="?android:dividerHorizontal"
+ android:orientation="vertical"
+ android:padding="1dp">
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/media"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="centerCrop" />
+</RelativeLayout> \ No newline at end of file