fix account list loading and clean up a lot of code (#823)
* fix account list loading and clean up a lot of code * remove ACCESS_COARSE_LOCATION for API levels 23+ * small improvements
This commit is contained in:
parent
ca881af7c5
commit
28c1c90a98
10 changed files with 135 additions and 378 deletions
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
package com.keylesspalace.tusky.adapter;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
|
||||
|
|
@ -26,55 +27,40 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
public abstract class AccountAdapter extends RecyclerView.Adapter {
|
||||
static final int VIEW_TYPE_ACCOUNT = 0;
|
||||
static final int VIEW_TYPE_FOOTER = 1;
|
||||
|
||||
|
||||
List<Account> accountList;
|
||||
AccountActionListener accountActionListener;
|
||||
FooterViewHolder.State footerState;
|
||||
|
||||
private String topId;
|
||||
private String bottomId;
|
||||
private boolean bottomLoading;
|
||||
|
||||
AccountAdapter(AccountActionListener accountActionListener) {
|
||||
super();
|
||||
accountList = new ArrayList<>();
|
||||
this.accountList = new ArrayList<>();
|
||||
this.accountActionListener = accountActionListener;
|
||||
footerState = FooterViewHolder.State.END;
|
||||
bottomLoading = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return accountList.size() + 1;
|
||||
return accountList.size() + (bottomLoading ? 1 : 0);
|
||||
}
|
||||
|
||||
public void update(@Nullable List<Account> newAccounts, @Nullable String fromId,
|
||||
@Nullable String uptoId) {
|
||||
if (newAccounts == null || newAccounts.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bottomId = fromId;
|
||||
topId = uptoId;
|
||||
|
||||
if (accountList.isEmpty()) {
|
||||
accountList = ListUtils.removeDuplicates(newAccounts);
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (position == accountList.size() && bottomLoading) {
|
||||
return VIEW_TYPE_FOOTER;
|
||||
} else {
|
||||
int index = accountList.indexOf(newAccounts.get(newAccounts.size() - 1));
|
||||
for (int i = 0; i < index; i++) {
|
||||
accountList.remove(0);
|
||||
}
|
||||
int newIndex = newAccounts.indexOf(accountList.get(0));
|
||||
if (newIndex == -1) {
|
||||
accountList.addAll(0, newAccounts);
|
||||
} else {
|
||||
accountList.addAll(0, newAccounts.subList(0, newIndex));
|
||||
}
|
||||
return VIEW_TYPE_ACCOUNT;
|
||||
}
|
||||
}
|
||||
|
||||
public void update(@NonNull List<Account> newAccounts) {
|
||||
accountList = ListUtils.removeDuplicates(newAccounts);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void addItems(List<Account> newAccounts, @Nullable String fromId) {
|
||||
if (fromId != null) {
|
||||
bottomId = fromId;
|
||||
}
|
||||
public void addItems(List<Account> newAccounts) {
|
||||
int end = accountList.size();
|
||||
Account last = accountList.get(end - 1);
|
||||
if (last != null && !findAccount(newAccounts, last.getId())) {
|
||||
|
|
@ -83,6 +69,19 @@ public abstract class AccountAdapter extends RecyclerView.Adapter {
|
|||
}
|
||||
}
|
||||
|
||||
public void setBottomLoading(boolean loading) {
|
||||
boolean wasLoading = bottomLoading;
|
||||
if(wasLoading == loading) {
|
||||
return;
|
||||
}
|
||||
bottomLoading = loading;
|
||||
if(loading) {
|
||||
notifyItemInserted(accountList.size());
|
||||
} else {
|
||||
notifyItemRemoved(accountList.size());
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean findAccount(List<Account> accounts, String id) {
|
||||
for (Account account : accounts) {
|
||||
if (account.getId().equals(id)) {
|
||||
|
|
@ -110,25 +109,5 @@ public abstract class AccountAdapter extends RecyclerView.Adapter {
|
|||
notifyItemInserted(position);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Account getItem(int position) {
|
||||
if (position >= 0 && position < accountList.size()) {
|
||||
return accountList.get(position);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setFooterState(FooterViewHolder.State newFooterState) {
|
||||
footerState = newFooterState;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getBottomId() {
|
||||
return bottomId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getTopId() {
|
||||
return topId;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,8 +31,6 @@ import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
|||
import com.squareup.picasso.Picasso;
|
||||
|
||||
public class BlocksAdapter extends AccountAdapter {
|
||||
private static final int VIEW_TYPE_BLOCKED_USER = 0;
|
||||
private static final int VIEW_TYPE_FOOTER = 1;
|
||||
|
||||
public BlocksAdapter(AccountActionListener accountActionListener) {
|
||||
super(accountActionListener);
|
||||
|
|
@ -43,7 +41,7 @@ public class BlocksAdapter extends AccountAdapter {
|
|||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
switch (viewType) {
|
||||
default:
|
||||
case VIEW_TYPE_BLOCKED_USER: {
|
||||
case VIEW_TYPE_ACCOUNT: {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_blocked_user, parent, false);
|
||||
return new BlockedUserViewHolder(view);
|
||||
|
|
@ -51,29 +49,17 @@ public class BlocksAdapter extends AccountAdapter {
|
|||
case VIEW_TYPE_FOOTER: {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_footer, parent, false);
|
||||
return new FooterViewHolder(view);
|
||||
return new LoadingFooterViewHolder(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
|
||||
if (position < accountList.size()) {
|
||||
if (getItemViewType(position) == VIEW_TYPE_ACCOUNT) {
|
||||
BlockedUserViewHolder holder = (BlockedUserViewHolder) viewHolder;
|
||||
holder.setupWithAccount(accountList.get(position));
|
||||
holder.setupActionListener(accountActionListener);
|
||||
} else {
|
||||
FooterViewHolder holder = (FooterViewHolder) viewHolder;
|
||||
holder.setState(footerState);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (position == accountList.size()) {
|
||||
return VIEW_TYPE_FOOTER;
|
||||
} else {
|
||||
return VIEW_TYPE_BLOCKED_USER;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,8 +26,6 @@ import com.keylesspalace.tusky.interfaces.AccountActionListener;
|
|||
|
||||
/** Both for follows and following lists. */
|
||||
public class FollowAdapter extends AccountAdapter {
|
||||
private static final int VIEW_TYPE_ACCOUNT = 0;
|
||||
private static final int VIEW_TYPE_FOOTER = 1;
|
||||
|
||||
public FollowAdapter(AccountActionListener accountActionListener) {
|
||||
super(accountActionListener);
|
||||
|
|
@ -46,29 +44,18 @@ public class FollowAdapter extends AccountAdapter {
|
|||
case VIEW_TYPE_FOOTER: {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_footer, parent, false);
|
||||
return new FooterViewHolder(view);
|
||||
return new LoadingFooterViewHolder(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
|
||||
if (position < accountList.size()) {
|
||||
if (getItemViewType(position) == VIEW_TYPE_ACCOUNT) {
|
||||
AccountViewHolder holder = (AccountViewHolder) viewHolder;
|
||||
holder.setupWithAccount(accountList.get(position));
|
||||
holder.setupActionListener(accountActionListener);
|
||||
} else {
|
||||
FooterViewHolder holder = (FooterViewHolder) viewHolder;
|
||||
holder.setState(footerState);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (position == accountList.size()) {
|
||||
return VIEW_TYPE_FOOTER;
|
||||
} else {
|
||||
return VIEW_TYPE_ACCOUNT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,8 +31,6 @@ import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
|||
import com.squareup.picasso.Picasso;
|
||||
|
||||
public class FollowRequestsAdapter extends AccountAdapter {
|
||||
private static final int VIEW_TYPE_FOLLOW_REQUEST = 0;
|
||||
private static final int VIEW_TYPE_FOOTER = 1;
|
||||
|
||||
public FollowRequestsAdapter(AccountActionListener accountActionListener) {
|
||||
super(accountActionListener);
|
||||
|
|
@ -43,7 +41,7 @@ public class FollowRequestsAdapter extends AccountAdapter {
|
|||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
switch (viewType) {
|
||||
default:
|
||||
case VIEW_TYPE_FOLLOW_REQUEST: {
|
||||
case VIEW_TYPE_ACCOUNT: {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_follow_request, parent, false);
|
||||
return new FollowRequestViewHolder(view);
|
||||
|
|
@ -51,29 +49,17 @@ public class FollowRequestsAdapter extends AccountAdapter {
|
|||
case VIEW_TYPE_FOOTER: {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_footer, parent, false);
|
||||
return new FooterViewHolder(view);
|
||||
return new LoadingFooterViewHolder(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
|
||||
if (position < accountList.size()) {
|
||||
if (getItemViewType(position) == VIEW_TYPE_ACCOUNT) {
|
||||
FollowRequestViewHolder holder = (FollowRequestViewHolder) viewHolder;
|
||||
holder.setupWithAccount(accountList.get(position));
|
||||
holder.setupActionListener(accountActionListener);
|
||||
} else {
|
||||
FooterViewHolder holder = (FooterViewHolder) viewHolder;
|
||||
holder.setState(footerState);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (position == accountList.size()) {
|
||||
return VIEW_TYPE_FOOTER;
|
||||
} else {
|
||||
return VIEW_TYPE_FOLLOW_REQUEST;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
/* Copyright 2017 Andrew Dawson
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Tusky 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 Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.adapter;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v7.content.res.AppCompatResources;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.support.v7.widget.RecyclerView.LayoutParams;
|
||||
|
||||
import com.keylesspalace.tusky.R;
|
||||
|
||||
public class FooterViewHolder extends RecyclerView.ViewHolder {
|
||||
public enum State {
|
||||
EMPTY,
|
||||
END,
|
||||
LOADING
|
||||
}
|
||||
|
||||
private View container;
|
||||
private ProgressBar progressBar;
|
||||
private TextView endMessage;
|
||||
|
||||
FooterViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
container = itemView.findViewById(R.id.footer_container);
|
||||
progressBar = itemView.findViewById(R.id.footer_progress_bar);
|
||||
endMessage = itemView.findViewById(R.id.footer_end_message);
|
||||
Drawable top = AppCompatResources.getDrawable(itemView.getContext(),
|
||||
R.drawable.elephant_friend_empty);
|
||||
endMessage.setCompoundDrawablesWithIntrinsicBounds(null, top, null, null);
|
||||
}
|
||||
|
||||
public void setState(State state) {
|
||||
switch (state) {
|
||||
case LOADING: {
|
||||
RecyclerView.LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.MATCH_PARENT);
|
||||
container.setLayoutParams(layoutParams);
|
||||
container.setVisibility(View.VISIBLE);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
endMessage.setVisibility(View.GONE);
|
||||
break;
|
||||
}
|
||||
case END: {
|
||||
RecyclerView.LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.WRAP_CONTENT);
|
||||
container.setLayoutParams(layoutParams);
|
||||
container.setVisibility(View.GONE);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
endMessage.setVisibility(View.GONE);
|
||||
break;
|
||||
}
|
||||
case EMPTY: {
|
||||
RecyclerView.LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.MATCH_PARENT);
|
||||
container.setLayoutParams(layoutParams);
|
||||
container.setVisibility(View.VISIBLE);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
endMessage.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/* Copyright 2018 Conny Duck
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Tusky 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 Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.adapter
|
||||
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.view.View
|
||||
|
||||
class LoadingFooterViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
|
||||
|
|
@ -16,8 +16,6 @@ import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
|||
import com.squareup.picasso.Picasso;
|
||||
|
||||
public class MutesAdapter extends AccountAdapter {
|
||||
private static final int VIEW_TYPE_MUTED_USER = 0;
|
||||
private static final int VIEW_TYPE_FOOTER = 1;
|
||||
|
||||
public MutesAdapter(AccountActionListener accountActionListener) {
|
||||
super(accountActionListener);
|
||||
|
|
@ -28,7 +26,7 @@ public class MutesAdapter extends AccountAdapter {
|
|||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
switch (viewType) {
|
||||
default:
|
||||
case VIEW_TYPE_MUTED_USER: {
|
||||
case VIEW_TYPE_ACCOUNT: {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_muted_user, parent, false);
|
||||
return new MutesAdapter.MutedUserViewHolder(view);
|
||||
|
|
@ -36,31 +34,20 @@ public class MutesAdapter extends AccountAdapter {
|
|||
case VIEW_TYPE_FOOTER: {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_footer, parent, false);
|
||||
return new FooterViewHolder(view);
|
||||
return new LoadingFooterViewHolder(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
|
||||
if (position < accountList.size()) {
|
||||
if (getItemViewType(position) == VIEW_TYPE_ACCOUNT) {
|
||||
MutedUserViewHolder holder = (MutedUserViewHolder) viewHolder;
|
||||
holder.setupWithAccount(accountList.get(position));
|
||||
holder.setupActionListener(accountActionListener);
|
||||
} else {
|
||||
FooterViewHolder holder = (FooterViewHolder) viewHolder;
|
||||
holder.setState(footerState);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (position == accountList.size()) {
|
||||
return VIEW_TYPE_FOOTER;
|
||||
} else {
|
||||
return VIEW_TYPE_MUTED_USER;
|
||||
}
|
||||
}
|
||||
|
||||
static class MutedUserViewHolder extends RecyclerView.ViewHolder {
|
||||
private ImageView avatar;
|
||||
|
|
|
|||
|
|
@ -56,10 +56,9 @@ import java.util.List;
|
|||
|
||||
public class NotificationsAdapter extends RecyclerView.Adapter {
|
||||
private static final int VIEW_TYPE_MENTION = 0;
|
||||
private static final int VIEW_TYPE_FOOTER = 1;
|
||||
private static final int VIEW_TYPE_STATUS_NOTIFICATION = 2;
|
||||
private static final int VIEW_TYPE_FOLLOW = 3;
|
||||
private static final int VIEW_TYPE_PLACEHOLDER = 4;
|
||||
private static final int VIEW_TYPE_STATUS_NOTIFICATION = 1;
|
||||
private static final int VIEW_TYPE_FOLLOW = 2;
|
||||
private static final int VIEW_TYPE_PLACEHOLDER = 3;
|
||||
|
||||
private List<NotificationViewData> notifications;
|
||||
private StatusActionListener statusListener;
|
||||
|
|
@ -87,11 +86,6 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
.inflate(R.layout.item_status, parent, false);
|
||||
return new StatusViewHolder(view);
|
||||
}
|
||||
case VIEW_TYPE_FOOTER: {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_footer, parent, false);
|
||||
return new FooterViewHolder(view);
|
||||
}
|
||||
case VIEW_TYPE_STATUS_NOTIFICATION: {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_status_notification, parent, false);
|
||||
|
|
@ -172,31 +166,28 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (position == notifications.size()) {
|
||||
return VIEW_TYPE_FOOTER;
|
||||
} else {
|
||||
NotificationViewData notification = notifications.get(position);
|
||||
if (notification instanceof NotificationViewData.Concrete) {
|
||||
NotificationViewData.Concrete concrete = ((NotificationViewData.Concrete) notification);
|
||||
switch (concrete.getType()) {
|
||||
default:
|
||||
case MENTION: {
|
||||
return VIEW_TYPE_MENTION;
|
||||
}
|
||||
case FAVOURITE:
|
||||
case REBLOG: {
|
||||
return VIEW_TYPE_STATUS_NOTIFICATION;
|
||||
}
|
||||
case FOLLOW: {
|
||||
return VIEW_TYPE_FOLLOW;
|
||||
}
|
||||
NotificationViewData notification = notifications.get(position);
|
||||
if (notification instanceof NotificationViewData.Concrete) {
|
||||
NotificationViewData.Concrete concrete = ((NotificationViewData.Concrete) notification);
|
||||
switch (concrete.getType()) {
|
||||
default:
|
||||
case MENTION: {
|
||||
return VIEW_TYPE_MENTION;
|
||||
}
|
||||
case FAVOURITE:
|
||||
case REBLOG: {
|
||||
return VIEW_TYPE_STATUS_NOTIFICATION;
|
||||
}
|
||||
case FOLLOW: {
|
||||
return VIEW_TYPE_FOLLOW;
|
||||
}
|
||||
} else if (notification instanceof NotificationViewData.Placeholder) {
|
||||
return VIEW_TYPE_PLACEHOLDER;
|
||||
} else {
|
||||
throw new AssertionError("Unknown notification type");
|
||||
}
|
||||
} else if (notification instanceof NotificationViewData.Placeholder) {
|
||||
return VIEW_TYPE_PLACEHOLDER;
|
||||
} else {
|
||||
throw new AssertionError("Unknown notification type");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void update(@Nullable List<NotificationViewData> newNotifications) {
|
||||
|
|
@ -364,8 +355,8 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
private void setCreatedAt(@Nullable Date createdAt) {
|
||||
// This is the visible timestampInfo.
|
||||
String readout;
|
||||
/* This one is for screen-readers. Frequently, they would mispronounce timestamps like "17m"
|
||||
* as 17 meters instead of minutes. */
|
||||
/* This one is for screen-readers. Frequently, they would mispronounce timestamps like "17m"
|
||||
* as 17 meters instead of minutes. */
|
||||
CharSequence readoutAloud;
|
||||
if (createdAt != null) {
|
||||
long then = createdAt.getTime();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue