From 6dda9c3753ea5772e40388401a6fde72246d5646 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Thu, 23 Aug 2018 13:53:12 +0200 Subject: [PATCH] Avoid useless posts reloading on LatestPostsFragment pauses --- .../client/ui/adapters/FriendsAdapter.java | 253 ++++++++++++------ .../ui/fragments/FriendsListFragment.java | 200 ++++++-------- .../listeners/OnFriendListActionListener.java | 33 +++ .../main/res/layout/fragment_friendslist.xml | 4 +- ...iend_item.xml => friend_accepted_item.xml} | 38 +-- .../main/res/layout/friend_pending_item.xml | 92 +++++++ app/src/main/res/values-fr/strings.xml | 2 + app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/strings.xml | 2 + 9 files changed, 409 insertions(+), 216 deletions(-) create mode 100644 app/src/main/java/org/communiquons/android/comunic/client/ui/listeners/OnFriendListActionListener.java rename app/src/main/res/layout/{fragment_friends_list_friend_item.xml => friend_accepted_item.xml} (54%) create mode 100644 app/src/main/res/layout/friend_pending_item.xml diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/FriendsAdapter.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/FriendsAdapter.java index b06af79..98a5cbc 100644 --- a/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/FriendsAdapter.java +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/FriendsAdapter.java @@ -1,21 +1,22 @@ package org.communiquons.android.comunic.client.ui.adapters; -import android.app.Activity; +import android.content.Context; +import android.support.annotation.CallSuper; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; import android.widget.Button; -import android.widget.ImageView; import android.widget.TextView; import org.communiquons.android.comunic.client.R; -import org.communiquons.android.comunic.client.data.helpers.ImageLoadHelper; +import org.communiquons.android.comunic.client.data.models.Friend; import org.communiquons.android.comunic.client.data.models.FriendUser; -import org.communiquons.android.comunic.client.data.utils.Utilities; -import org.communiquons.android.comunic.client.ui.fragments.FriendsListFragment; +import org.communiquons.android.comunic.client.data.models.UserInfo; +import org.communiquons.android.comunic.client.ui.listeners.OnFriendListActionListener; +import org.communiquons.android.comunic.client.ui.utils.UiUtils; +import org.communiquons.android.comunic.client.ui.views.WebUserAccountImage; import java.util.ArrayList; @@ -26,100 +27,188 @@ import java.util.ArrayList; * Created by pierre on 11/15/17. */ -public class FriendsAdapter extends ArrayAdapter { +public class FriendsAdapter extends BaseRecyclerViewAdapter { /** - * The fragment creating the adapter + * View type */ - private FriendsListFragment mFLfragment; + private static final int VIEW_TYPE_ACCEPTED_FRIEND = 1; + private static final int VIEW_TYPE_PENDING_FRIEND = 2; + + /** + * The list of friends, with their information + */ + private ArrayList mList; + + /** + * Actions listener + */ + private OnFriendListActionListener mListener; /** * Class constructor * - * @param friendsListFragment Friends list fragment object * @param context The context of execution of the application * @param friendsList The list of friends to display (with user information) + * @param listener Actions on friendlist listener */ - public FriendsAdapter(FriendsListFragment friendsListFragment, - Activity context, ArrayList friendsList){ - super(context, 0, friendsList); - mFLfragment = friendsListFragment; + public FriendsAdapter(Context context, ArrayList friendsList, + OnFriendListActionListener listener){ + super(context); + + mList = friendsList; + mListener = listener; + } + + @Override + public int getItemCount() { + return mList.size(); + } + + @Override + public int getItemViewType(int position) { + return mList.get(position).getFriend().isAccepted() ? VIEW_TYPE_ACCEPTED_FRIEND : + VIEW_TYPE_PENDING_FRIEND; } @NonNull @Override - public View getView(final int position, @Nullable View convertView, @NonNull ViewGroup parent) { - View listItemView = convertView; + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int type) { + View view; - //Check if the view has to be created - if(listItemView == null){ - listItemView = LayoutInflater.from(getContext()) - .inflate(R.layout.fragment_friends_list_friend_item, parent, false); + if(type == VIEW_TYPE_ACCEPTED_FRIEND){ + view = LayoutInflater.from(getContext()).inflate(R.layout.friend_accepted_item, + viewGroup, false); + return new AcceptedFriendHolder(view); } - //Get friend information - FriendUser friendUser = getItem(position); - - //Update user account image - ImageView user_image = listItemView.findViewById(R.id.fragment_friendslist_item_accountimage); - user_image.setImageDrawable(getContext().getDrawable(R.drawable.default_account_image)); - ImageLoadHelper.load(getContext(), friendUser.getUserInfo().getAcountImageURL(), user_image); - - //Update user name - TextView user_name = listItemView.findViewById(R.id.fragment_friendslist_item_fullname); - user_name.setText(Utilities.prepareStringTextView(friendUser.getUserInfo().getFullName())); - - //Update user status - boolean signed_in = friendUser.getFriend().signed_in(); - TextView statusView = listItemView.findViewById(R.id.fragment_friendslist_item_status); - - //Set the text - statusView.setText(signed_in ? - getContext().getText(R.string.user_status_online) : - getContext().getText(R.string.user_status_offline) - ); - - //Set the color - int status_color; - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { - status_color = getContext().getResources().getColor(signed_in ? R.color.holo_green_dark : R.color.darker_gray, null); - } - else { - status_color = getContext().getResources().getColor(signed_in ? R.color.holo_green_dark : R.color.darker_gray); - } - statusView.setTextColor(status_color); - - //Action button - Button action = listItemView.findViewById(R.id.fragment_friendslist_item_action); - - //Define the action of the accept request button - if(!friendUser.getFriend().isAccepted()){ - - //Update the button - action.setVisibility(View.VISIBLE); - action.setText(R.string.action_friends_respond_request); - - //Make the button lives - action.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - - //Hide the view - v.setVisibility(View.GONE); - - mFLfragment.showPopupRequestResponse(position); - } - }); - - } - else { - - //Remove button actions and hide it - action.setVisibility(View.GONE); - action.setOnClickListener(null); + if(type == VIEW_TYPE_PENDING_FRIEND){ + view = LayoutInflater.from(getContext()).inflate(R.layout.friend_pending_item, + viewGroup, false); + return new PendingFriendHolder(view); } - return listItemView; + throw new RuntimeException("Undefined view type: " + type); } + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int pos) { + ((BaseFriendHolder)viewHolder).bind(pos); + } + + + /** + * Base Friend ViewHolder + */ + private class BaseFriendHolder extends RecyclerView.ViewHolder { + + private WebUserAccountImage mUserAccountImage; + private TextView mUserName; + private TextView mUserStatus; + + BaseFriendHolder(@NonNull View itemView) { + super(itemView); + + mUserAccountImage = itemView.findViewById(R.id.account_image); + mUserName = itemView.findViewById(R.id.account_name); + mUserStatus = itemView.findViewById(R.id.user_status); + } + + Friend getFriend(int pos){ + return mList.get(pos).getFriend(); + } + + UserInfo getUserInfo(int pos){ + return mList.get(pos).getUserInfo(); + } + + int getCurrentUserID(){ + return getUserInfo(getLayoutPosition()).getId(); + } + + @CallSuper + void bind(int pos){ + + //Update user information + mUserAccountImage.setUser(getUserInfo(pos)); + mUserName.setText(getUserInfo(pos).getDisplayFullName()); + + //Update user status + boolean signed_in = getFriend(pos).signed_in(); + mUserStatus.setText(UiUtils.getString(getContext(), signed_in ? + R.string.user_status_online : R.string.user_status_offline)); + mUserStatus.setTextColor(UiUtils.getColor(getContext(), + signed_in ? R.color.holo_green_dark : R.color.darker_gray)); + + + //Open user page on click + itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mListener.onOpenUserPage(getCurrentUserID()); + } + }); + } + } + + + /** + * Accepted friend holder + */ + private class AcceptedFriendHolder extends BaseFriendHolder implements View.OnLongClickListener { + + AcceptedFriendHolder(@NonNull View itemView) { + super(itemView); + + itemView.setOnLongClickListener(this); + } + + @Override + public boolean onLongClick(View v) { + + if(!v.equals(itemView)) + return false; + + mListener.onOpenContextMenuForFriend(itemView, getLayoutPosition()); + return true; + } + } + + + /** + * Pending friend view holder + */ + private class PendingFriendHolder extends BaseFriendHolder implements View.OnClickListener { + + private Button mAcceptButton; + private Button mRejectButton; + + PendingFriendHolder(@NonNull View itemView) { + super(itemView); + + mAcceptButton = itemView.findViewById(R.id.accept_button); + mRejectButton = itemView.findViewById(R.id.reject_button); + + mAcceptButton.setOnClickListener(this); + mRejectButton.setOnClickListener(this); + } + + @Override + void bind(int pos) { + super.bind(pos); + + mAcceptButton.setVisibility(View.VISIBLE); + mRejectButton.setVisibility(View.VISIBLE); + } + + @Override + public void onClick(View v) { + + mAcceptButton.setVisibility(View.INVISIBLE); + mRejectButton.setVisibility(View.INVISIBLE); + + boolean accept = v.equals(mAcceptButton); + mListener.onRespondFrienshipRequest(getLayoutPosition(), accept); + } + } } diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/fragments/FriendsListFragment.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/fragments/FriendsListFragment.java index c0a2483..738ac9c 100644 --- a/app/src/main/java/org/communiquons/android/comunic/client/ui/fragments/FriendsListFragment.java +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/fragments/FriendsListFragment.java @@ -1,37 +1,39 @@ package org.communiquons.android.comunic.client.ui.fragments; import android.app.AlertDialog; -import android.support.v4.app.Fragment; import android.content.Context; import android.content.DialogInterface; import android.os.AsyncTask; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v7.widget.DividerItemDecoration; +import android.support.v7.widget.LinearLayoutManager; import android.util.ArrayMap; -import android.view.ContextMenu; import android.view.LayoutInflater; -import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ListView; +import android.widget.PopupMenu; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; -import org.communiquons.android.comunic.client.ui.activities.MainActivity; import org.communiquons.android.comunic.client.R; import org.communiquons.android.comunic.client.data.helpers.DatabaseHelper; +import org.communiquons.android.comunic.client.data.helpers.FriendsListHelper; import org.communiquons.android.comunic.client.data.helpers.GetUsersHelper; -import org.communiquons.android.comunic.client.data.models.UserInfo; import org.communiquons.android.comunic.client.data.models.Friend; import org.communiquons.android.comunic.client.data.models.FriendUser; -import org.communiquons.android.comunic.client.ui.adapters.FriendsAdapter; -import org.communiquons.android.comunic.client.data.helpers.FriendsListHelper; +import org.communiquons.android.comunic.client.data.models.UserInfo; import org.communiquons.android.comunic.client.data.utils.FriendsUtils; +import org.communiquons.android.comunic.client.ui.activities.MainActivity; +import org.communiquons.android.comunic.client.ui.adapters.FriendsAdapter; +import org.communiquons.android.comunic.client.ui.listeners.OnFriendListActionListener; import org.communiquons.android.comunic.client.ui.listeners.onOpenUsersPageListener; import org.communiquons.android.comunic.client.ui.listeners.openConversationListener; +import org.communiquons.android.comunic.client.ui.views.ScrollRecyclerView; import java.util.ArrayList; @@ -42,8 +44,8 @@ import java.util.ArrayList; * Created by pierre on 11/11/17. */ -public class FriendsListFragment extends Fragment - implements AdapterView.OnItemClickListener{ +public class FriendsListFragment extends Fragment implements OnFriendListActionListener, + PopupMenu.OnMenuItemClickListener { /** * Debug tag @@ -103,7 +105,12 @@ public class FriendsListFragment extends Fragment /** * Friends list view */ - private ListView mFriendsListView; + private ScrollRecyclerView mFriendsList; + + /** + * Current friend on context menu + */ + private int mPosInContextMenu; @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -125,7 +132,7 @@ public class FriendsListFragment extends Fragment try { convOpener = (openConversationListener) getActivity(); usersPageOpener = (onOpenUsersPageListener) getActivity(); - } catch (ClassCastException e){ + } catch (ClassCastException e) { e.printStackTrace(); throw new RuntimeException(getActivity().getClass().getName() + " must implement" + @@ -136,13 +143,13 @@ public class FriendsListFragment extends Fragment @Nullable @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_friendslist, container, false); } @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); //Get loading progress bar @@ -152,8 +159,8 @@ public class FriendsListFragment extends Fragment mNoFriendNotice = view.findViewById(R.id.no_friend_notice); mNoFriendNotice.setVisibility(View.GONE); - //Get friends listview - mFriendsListView = view.findViewById(R.id.fragment_friendslist_listview); + //Get friends RecyclerView + mFriendsList = view.findViewById(R.id.friendslist); //Retain the fragment //setRetainInstance(true); @@ -175,12 +182,12 @@ public class FriendsListFragment extends Fragment /** * Refresh the friend list */ - void refresh_friend_list(){ + void refresh_friend_list() { //Display loading bar display_progress_bar(true); - new AsyncTask>(){ + new AsyncTask>() { @Override protected ArrayList doInBackground(Void... params) { @@ -189,15 +196,15 @@ public class FriendsListFragment extends Fragment ArrayList friendsList = flistHelper.get(); //Check for errors - if(friendsList == null) + if (friendsList == null) return null; - //Get user infos + //Get user info ArrayMap userInfos = usersHelper.getMultiple( FriendsUtils.getFriendsIDs(friendsList)); //Check for errors - if(userInfos == null) + if (userInfos == null) return null; //Merge friend and user and return result @@ -209,7 +216,7 @@ public class FriendsListFragment extends Fragment protected void onPostExecute(ArrayList friendUsers) { //Check the activity still exists - if(getActivity() == null) + if (getActivity() == null) return; apply_friends_list(friendUsers); @@ -222,13 +229,13 @@ public class FriendsListFragment extends Fragment * * @param friendsList The friends list to apply */ - private void apply_friends_list(@Nullable ArrayList friendsList){ + private void apply_friends_list(@Nullable ArrayList friendsList) { //Remove progress bar display_progress_bar(false); //Check for errors - if(friendsList == null){ + if (friendsList == null) { Toast.makeText(mContext, R.string.fragment_friendslist_err_refresh, Toast.LENGTH_LONG).show(); return; @@ -241,47 +248,15 @@ public class FriendsListFragment extends Fragment updateNoFriendNoticeVisibility(); //Set the adapter - fAdapter = new FriendsAdapter(this, getActivity(), friendsList); - mFriendsListView.setAdapter(fAdapter); + fAdapter = new FriendsAdapter(getActivity(), friendsList, this); + mFriendsList.setLayoutManager(new LinearLayoutManager(getActivity())); + mFriendsList.addItemDecoration(new DividerItemDecoration(mFriendsList.getContext(), + DividerItemDecoration.VERTICAL)); + mFriendsList.setAdapter(fAdapter); //Register the view for the context menu - registerForContextMenu(mFriendsListView); + registerForContextMenu(mFriendsList); - mFriendsListView.setOnItemClickListener(this); - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - - MenuInflater inflater = getActivity().getMenuInflater(); - inflater.inflate(R.menu.menu_fragment_friendslist_item, menu); - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - - AdapterView.AdapterContextMenuInfo info = - (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); - - //Get the friend position in the list - int friendPos = info.position; - - switch (item.getItemId()){ - - //To open a private conversation with the friend - case R.id.menu_fragment_friendslist_private_conversation: - convOpener.openPrivateConversation(friendsList.get(friendPos).getFriend().getId()); - return true; - - //To delete the friend - case R.id.menu_fragment_friendslist_delete_friend: - delete_friend(friendPos); - return true; - } - - //If it is not for us, it is for someone else - return super.onContextItemSelected(item); } /** @@ -289,7 +264,7 @@ public class FriendsListFragment extends Fragment * * @param pos the position of the friend to delete */ - private void delete_friend(final int pos){ + private void delete_friend(final int pos) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle(R.string.popup_deletefriend_title) @@ -307,7 +282,7 @@ public class FriendsListFragment extends Fragment fAdapter.notifyDataSetChanged(); //Remove the friend list on a parallel thread - new AsyncTask(){ + new AsyncTask() { @Override protected Void doInBackground(Integer[] params) { @@ -325,48 +300,19 @@ public class FriendsListFragment extends Fragment } - /** - * Show a popup to offer the user to respond to a friendship request - * - * @param pos The position of the friend in the list - */ - public void showPopupRequestResponse(final int pos){ - - new AlertDialog.Builder(getActivity()) - - .setTitle(R.string.popup_respond_friendship_request_title) - .setMessage(R.string.popup_respond_friendship_request_message) - - .setNegativeButton(R.string.action_friends_deny_request, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - respondRequest(pos, false); - } - }) - - .setPositiveButton(R.string.action_friends_accept_request, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - respondRequest(pos, true); - } - }) - - .show(); + @Override + public void onOpenUserPage(int userID) { + usersPageOpener.openUserPage(userID); } - /** - * Respond to a friendship request - * - * @param pos The position of the friend respond the request - * @param accept Specify wether the user accepted the request or not - */ - private void respondRequest(int pos, final boolean accept){ + @Override + public void onRespondFrienshipRequest(int pos, final boolean response) { //Get the Friend object Friend targetFriend = friendsList.get(pos).getFriend(); - if(accept) + if (response) //Mark the friend as accepted targetFriend.setAccepted(true); else @@ -377,13 +323,47 @@ public class FriendsListFragment extends Fragment fAdapter.notifyDataSetChanged(); //Accept the request on a separate thread - new AsyncTask(){ + new AsyncTask() { @Override protected Void doInBackground(Friend... params) { - flistHelper.respondRequest(params[0], accept); + flistHelper.respondRequest(params[0], response); return null; } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, targetFriend); + + } + + @Override + public void onOpenContextMenuForFriend(View view, int pos) { + + //Save selected position + mPosInContextMenu = pos; + + //Initialize and show menu + PopupMenu menu = new PopupMenu(getActivity(), view); + menu.inflate(R.menu.menu_fragment_friendslist_item); + menu.setOnMenuItemClickListener(this); + menu.show(); + } + + @Override + public boolean onMenuItemClick(MenuItem item) { + + switch (item.getItemId()) { + + //To open a private conversation with the friend + case R.id.menu_fragment_friendslist_private_conversation: + convOpener.openPrivateConversation(friendsList.get(mPosInContextMenu).getFriend().getId()); + return true; + + //To delete the friend + case R.id.menu_fragment_friendslist_delete_friend: + delete_friend(mPosInContextMenu); + return true; + } + + + return false; } /** @@ -391,25 +371,15 @@ public class FriendsListFragment extends Fragment * * @param display Specify whether the loading bar has to be shown or not */ - private void display_progress_bar(boolean display){ + private void display_progress_bar(boolean display) { mProgressBar.setVisibility(display ? View.VISIBLE : View.GONE); } /** * Update the visibility of the no friend notice */ - private void updateNoFriendNoticeVisibility(){ - if(friendsList != null) + private void updateNoFriendNoticeVisibility() { + if (friendsList != null) mNoFriendNotice.setVisibility(friendsList.size() == 0 ? View.VISIBLE : View.GONE); } - - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - - //Get clicked user ID - int userID = friendsList.get(position).get_user_id(); - - //Open user page - usersPageOpener.openUserPage(userID); - } } diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/listeners/OnFriendListActionListener.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/listeners/OnFriendListActionListener.java new file mode 100644 index 0000000..19fb485 --- /dev/null +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/listeners/OnFriendListActionListener.java @@ -0,0 +1,33 @@ +package org.communiquons.android.comunic.client.ui.listeners; + +import android.view.View; + +/** + * Friends action listener + * + * @author Pierre HUBERT + */ +public interface OnFriendListActionListener { + + /** + * Open a user page + * + * @param userID The ID of the user page to open + */ + void onOpenUserPage(int userID); + + /** + * Respond to a friendship request + * + * @param pos Position of the friend on the list + * @param response TRUE to accept / FALSE else + */ + void onRespondFrienshipRequest(int pos, boolean response); + + /** + * Open the context menu for a friend + * + * @param pos The position of the friend in the list + */ + void onOpenContextMenuForFriend(View view, int pos); +} diff --git a/app/src/main/res/layout/fragment_friendslist.xml b/app/src/main/res/layout/fragment_friendslist.xml index 80dad2d..0ba1887 100644 --- a/app/src/main/res/layout/fragment_friendslist.xml +++ b/app/src/main/res/layout/fragment_friendslist.xml @@ -19,8 +19,8 @@ android:textAlignment="center" android:layout_marginTop="50dp"/> - - + android:paddingStart="8dp" + android:paddingTop="8dp" + android:clickable="true" + android:focusable="true" + android:background="?selectableItemBackground"> - + android:paddingStart="8dp" + app:layout_constraintBottom_toBottomOf="@+id/account_image" + app:layout_constraintStart_toEndOf="@+id/account_image" + app:layout_constraintTop_toTopOf="@+id/account_image"> - -