Commit 020d218d authored by Thomas's avatar Thomas
Browse files

add suggestions

parent 0796ff7b
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -117,6 +117,7 @@ import app.fedilab.android.activities.ReorderTimelinesActivity;
import app.fedilab.android.activities.ScheduledActivity;
import app.fedilab.android.activities.SearchResultTabActivity;
import app.fedilab.android.activities.SettingsActivity;
import app.fedilab.android.activities.SuggestionActivity;
import app.fedilab.android.activities.TrendsActivity;
import app.fedilab.android.broadcastreceiver.NetworkStateReceiver;
import app.fedilab.android.client.entities.api.Emoji;
@@ -391,6 +392,9 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
            } else if (id == R.id.nav_trends) {
                Intent intent = new Intent(this, TrendsActivity.class);
                startActivity(intent);
            } else if (id == R.id.nav_suggestions) {
                Intent intent = new Intent(this, SuggestionActivity.class);
                startActivity(intent);
            } else if (id == R.id.nav_cache) {
                Intent intent = new Intent(BaseMainActivity.this, CacheActivity.class);
                startActivity(intent);
+3 −6
Original line number Diff line number Diff line
@@ -23,11 +23,10 @@ import androidx.core.content.ContextCompat;
import org.jetbrains.annotations.NotNull;

import app.fedilab.android.R;
import app.fedilab.android.client.entities.app.Timeline;
import app.fedilab.android.databinding.ActivitySuggestionsBinding;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonAccount;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonSuggestion;


public class SuggestionActivity extends BaseActivity {
@@ -37,7 +36,7 @@ public class SuggestionActivity extends BaseActivity {
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ThemeHelper.applyThemeBar(this);
        app.fedilab.android.databinding.ActivitySuggestionsBinding binding = ActivitySuggestionsBinding.inflate(getLayoutInflater());
        ActivitySuggestionsBinding binding = ActivitySuggestionsBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());


@@ -46,9 +45,7 @@ public class SuggestionActivity extends BaseActivity {
            getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
        }

        Bundle bundle = new Bundle();
        bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.ACCOUNT_SUGGESTION);
        Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_tags, new FragmentMastodonAccount(), bundle, null, null);
        Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_suggestions, new FragmentMastodonSuggestion(), null, null, null);
    }


+140 −0
Original line number Diff line number Diff line
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.content.res.ColorStateList;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityOptionsCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner;
import androidx.recyclerview.widget.RecyclerView;

import java.lang.ref.WeakReference;
import java.util.List;

import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.activities.ProfileActivity;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.Suggestion;
import app.fedilab.android.databinding.DrawerSuggestionBinding;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.viewmodel.mastodon.AccountsVM;


public class SuggestionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private final List<Suggestion> suggestionList;
    private Context context;

    public SuggestionAdapter(List<Suggestion> suggestionList) {
        this.suggestionList = suggestionList;
    }


    public int getCount() {
        return suggestionList.size();
    }

    public Suggestion getItem(int position) {
        return suggestionList.get(position);
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        this.context = parent.getContext();
        DrawerSuggestionBinding itemBinding = DrawerSuggestionBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
        return new SuggestionViewHolder(itemBinding);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
        Account account = suggestionList.get(position).account;
        SuggestionViewHolder holder = (SuggestionViewHolder) viewHolder;
        MastodonHelper.loadPPMastodon(holder.binding.avatar, account);


        AccountsVM accountsVM = new ViewModelProvider((ViewModelStoreOwner) context).get(AccountsVM.class);

        holder.binding.avatar.setOnClickListener(v -> {
            Intent intent = new Intent(context, ProfileActivity.class);
            Bundle b = new Bundle();
            b.putSerializable(Helper.ARG_ACCOUNT, account);
            intent.putExtras(b);
            ActivityOptionsCompat options = ActivityOptionsCompat
                    .makeSceneTransitionAnimation((Activity) context, holder.binding.avatar, context.getString(R.string.activity_porfile_pp));
            // start the new activity
            context.startActivity(intent, options.toBundle());
        });
        holder.binding.followAction.setIconResource(R.drawable.ic_baseline_person_add_24);
        holder.binding.followAction.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(context, R.color.cyanea_accent_dark_reference)));
        holder.binding.displayName.setText(
                account.getSpanDisplayName(context,
                        new WeakReference<>(holder.binding.displayName)),
                TextView.BufferType.SPANNABLE);
        holder.binding.username.setText(String.format("@%s", account.acct));
        holder.binding.bio.setText(
                account.getSpanNote(context,
                        new WeakReference<>(holder.binding.bio)),
                TextView.BufferType.SPANNABLE);

        holder.binding.followAction.setEnabled(false);
        holder.binding.followAction.setOnClickListener(v -> {
            suggestionList.remove(position);
            notifyItemRemoved(position);
            accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, false);
        });
        holder.binding.notInterested.setOnClickListener(view -> {
            suggestionList.remove(position);
            notifyItemRemoved(position);
            accountsVM.removeSuggestion(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id);
        });
        //TODO, remove when supported
        holder.binding.notInterested.setVisibility(View.GONE);
    }

    public long getItemId(int position) {
        return position;
    }

    @Override
    public int getItemCount() {
        return suggestionList.size();
    }


    public static class SuggestionViewHolder extends RecyclerView.ViewHolder {
        DrawerSuggestionBinding binding;

        SuggestionViewHolder(DrawerSuggestionBinding itemView) {
            super(itemView.getRoot());
            binding = itemView;
        }
    }

}
 No newline at end of file
+0 −8
Original line number Diff line number Diff line
@@ -143,14 +143,6 @@ public class FragmentMastodonAccount extends Fragment {
                accountsVM.getBlocks(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, String.valueOf(MastodonHelper.accountsPerCall(requireActivity())), max_id, null)
                        .observe(getViewLifecycleOwner(), this::initializeAccountCommonView);
            }
        } else if (timelineType == Timeline.TimeLineEnum.ACCOUNT_SUGGESTION) {
            if (firstLoad) {
                accountsVM.getSuggestions(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null)
                        .observe(getViewLifecycleOwner(), this::initializeAccountCommonView);
            } else {
                accountsVM.getSuggestions(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, max_id)
                        .observe(getViewLifecycleOwner(), this::initializeAccountCommonView);
            }
        }
    }

+161 −0
Original line number Diff line number Diff line
package app.fedilab.android.ui.fragment.timeline;
/* 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.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import java.util.List;

import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Suggestion;
import app.fedilab.android.client.entities.api.Suggestions;
import app.fedilab.android.databinding.FragmentPaginationBinding;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.ui.drawer.SuggestionAdapter;
import app.fedilab.android.viewmodel.mastodon.AccountsVM;

public class FragmentMastodonSuggestion extends Fragment {


    private FragmentPaginationBinding binding;
    private AccountsVM accountsVM;
    private boolean flagLoading;
    private List<Suggestion> suggestions;
    private String max_id;
    private SuggestionAdapter suggestionAdapter;

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        flagLoading = false;
        binding = FragmentPaginationBinding.inflate(inflater, container, false);
        binding.getRoot().setBackgroundColor(ThemeHelper.getBackgroundColor(requireActivity()));
        return binding.getRoot();
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        int c1 = getResources().getColor(R.color.cyanea_accent_reference);
        binding.swipeContainer.setProgressBackgroundColorSchemeColor(getResources().getColor(R.color.cyanea_primary_reference));
        binding.swipeContainer.setColorSchemeColors(
                c1, c1, c1
        );
        binding.loader.setVisibility(View.VISIBLE);
        binding.recyclerView.setVisibility(View.GONE);
        accountsVM = new ViewModelProvider(FragmentMastodonSuggestion.this).get(AccountsVM.class);
        max_id = null;
        accountsVM.getSuggestions(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null)
                .observe(getViewLifecycleOwner(), this::initializeAccountCommonView);
    }


    public void scrollToTop() {
        binding.recyclerView.setAdapter(suggestionAdapter);
    }

    /**
     * Intialize the view for Suggestions
     *
     * @param suggestions {@link Suggestions}
     */
    private void initializeAccountCommonView(final Suggestions suggestions) {
        flagLoading = false;
        if (binding == null || !isAdded() || getActivity() == null) {
            return;
        }
        binding.loader.setVisibility(View.GONE);
        binding.noAction.setVisibility(View.GONE);
        binding.swipeContainer.setRefreshing(false);
        binding.swipeContainer.setOnRefreshListener(() -> {
            binding.swipeContainer.setRefreshing(true);
            flagLoading = false;
            max_id = null;
            accountsVM.getSuggestions(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null)
                    .observe(getViewLifecycleOwner(), this::initializeAccountCommonView);
        });
        if (suggestions == null || suggestions.suggestions == null || suggestions.suggestions.size() == 0) {
            binding.noAction.setVisibility(View.VISIBLE);
            binding.noActionText.setText(R.string.no_accounts);
            return;
        }
        binding.recyclerView.setVisibility(View.VISIBLE);

        this.suggestions = suggestions.suggestions;
        suggestionAdapter = new SuggestionAdapter(this.suggestions);
        flagLoading = suggestions.pagination.max_id == null;
        LinearLayoutManager mLayoutManager = new LinearLayoutManager(requireActivity());
        binding.recyclerView.setLayoutManager(mLayoutManager);
        binding.recyclerView.setAdapter(suggestionAdapter);
        max_id = suggestions.pagination.max_id;
        binding.recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                int firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
                if (dy > 0) {
                    int visibleItemCount = mLayoutManager.getChildCount();
                    int totalItemCount = mLayoutManager.getItemCount();
                    if (firstVisibleItem + visibleItemCount == totalItemCount) {
                        if (!flagLoading) {
                            flagLoading = true;
                            binding.loadingNextElements.setVisibility(View.VISIBLE);
                            accountsVM.getSuggestions(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, max_id)
                                    .observe(getViewLifecycleOwner(), suggestionsPaginated -> {
                                        dealWithPagination(suggestionsPaginated);
                                    });
                        }
                    } else {
                        binding.loadingNextElements.setVisibility(View.GONE);
                    }
                }

            }
        });
    }


    /**
     * Update view and pagination when scrolling down
     *
     * @param suggestions_fetched Suggestions
     */
    private void dealWithPagination(Suggestions suggestions_fetched) {
        flagLoading = false;
        if (binding == null || !isAdded() || getActivity() == null) {
            return;
        }
        binding.loadingNextElements.setVisibility(View.GONE);
        if (this.suggestions != null && suggestions_fetched != null && suggestions_fetched.suggestions != null) {
            flagLoading = suggestions_fetched.pagination.max_id == null;
            int startId = this.suggestions.size();
            this.suggestions.addAll(suggestions_fetched.suggestions);
            max_id = suggestions_fetched.pagination.max_id;
            suggestionAdapter.notifyItemRangeInserted(startId, suggestions_fetched.suggestions.size());
        } else {
            flagLoading = true;
        }
    }
}
 No newline at end of file
Loading