added direct message paging system, moved ViewHolder classes to a new package, bug fixes, layout fix

This commit is contained in:
nuclearfog 2021-02-07 21:02:14 +01:00
parent 4462fec2a9
commit 35f479c78c
No known key found for this signature in database
GPG Key ID: D5490E4A81F97B14
33 changed files with 1013 additions and 719 deletions

View File

@ -26,7 +26,7 @@ import org.nuclearfog.twidda.backend.ListAction;
import org.nuclearfog.twidda.backend.ListManager; import org.nuclearfog.twidda.backend.ListManager;
import org.nuclearfog.twidda.backend.ListManager.ListManagerCallback; import org.nuclearfog.twidda.backend.ListManager.ListManagerCallback;
import org.nuclearfog.twidda.backend.engine.EngineException; import org.nuclearfog.twidda.backend.engine.EngineException;
import org.nuclearfog.twidda.backend.items.UserList; import org.nuclearfog.twidda.backend.items.TwitterList;
import org.nuclearfog.twidda.backend.utils.AppStyles; import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.backend.utils.DialogBuilder; import org.nuclearfog.twidda.backend.utils.DialogBuilder;
import org.nuclearfog.twidda.backend.utils.DialogBuilder.OnDialogClick; import org.nuclearfog.twidda.backend.utils.DialogBuilder.OnDialogClick;
@ -91,7 +91,7 @@ public class ListDetail extends AppCompatActivity implements OnTabSelectedListen
private Dialog unfollowDialog, deleteDialog; private Dialog unfollowDialog, deleteDialog;
@Nullable @Nullable
private UserList userList; private TwitterList userList;
private long listId = -1; private long listId = -1;
@Override @Override
@ -114,8 +114,8 @@ public class ListDetail extends AppCompatActivity implements OnTabSelectedListen
unfollowDialog = DialogBuilder.create(this, LIST_UNFOLLOW, this); unfollowDialog = DialogBuilder.create(this, LIST_UNFOLLOW, this);
Object data = getIntent().getSerializableExtra(KEY_LIST_DATA); Object data = getIntent().getSerializableExtra(KEY_LIST_DATA);
if (data instanceof UserList) { if (data instanceof TwitterList) {
userList = (UserList) data; userList = (TwitterList) data;
listId = userList.getId(); listId = userList.getId();
toolbar.setTitle(userList.getTitle()); toolbar.setTitle(userList.getTitle());
toolbar.setSubtitle(userList.getDescription()); toolbar.setSubtitle(userList.getDescription());
@ -228,8 +228,8 @@ public class ListDetail extends AppCompatActivity implements OnTabSelectedListen
if (result != null && reqCode == REQ_LIST_CHANGE) { if (result != null && reqCode == REQ_LIST_CHANGE) {
if (returnCode == RET_LIST_CHANGED) { if (returnCode == RET_LIST_CHANGED) {
Object data = result.getSerializableExtra(RET_LIST_DATA); Object data = result.getSerializableExtra(RET_LIST_DATA);
if (data instanceof UserList) { if (data instanceof TwitterList) {
userList = (UserList) data; userList = (TwitterList) data;
toolbar.setTitle(userList.getTitle()); toolbar.setTitle(userList.getTitle());
toolbar.setSubtitle(userList.getDescription()); toolbar.setSubtitle(userList.getDescription());
invalidateOptionsMenu(); invalidateOptionsMenu();
@ -312,9 +312,9 @@ public class ListDetail extends AppCompatActivity implements OnTabSelectedListen
/** /**
* called from {@link ListAction} to update userlist information * called from {@link ListAction} to update userlist information
* *
* @param userList UserList information * @param userList TwitterList information
*/ */
public void onSuccess(UserList userList, ListAction.Action action) { public void onSuccess(TwitterList userList, ListAction.Action action) {
this.userList = userList; this.userList = userList;
switch (action) { switch (action) {
case LOAD: case LOAD:

View File

@ -20,7 +20,7 @@ import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.ListUpdater; import org.nuclearfog.twidda.backend.ListUpdater;
import org.nuclearfog.twidda.backend.engine.EngineException; import org.nuclearfog.twidda.backend.engine.EngineException;
import org.nuclearfog.twidda.backend.holder.ListHolder; import org.nuclearfog.twidda.backend.holder.ListHolder;
import org.nuclearfog.twidda.backend.items.UserList; import org.nuclearfog.twidda.backend.items.TwitterList;
import org.nuclearfog.twidda.backend.utils.AppStyles; import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.backend.utils.DialogBuilder; import org.nuclearfog.twidda.backend.utils.DialogBuilder;
import org.nuclearfog.twidda.backend.utils.DialogBuilder.OnDialogClick; import org.nuclearfog.twidda.backend.utils.DialogBuilder.OnDialogClick;
@ -52,7 +52,7 @@ public class ListEditor extends AppCompatActivity implements OnClickListener, On
private Dialog leaveDialog, loadingCircle; private Dialog leaveDialog, loadingCircle;
private AlertDialog errorDialog; private AlertDialog errorDialog;
@Nullable @Nullable
private UserList userList; private TwitterList userList;
@Override @Override
protected void onCreate(Bundle b) { protected void onCreate(Bundle b) {
@ -73,8 +73,8 @@ public class ListEditor extends AppCompatActivity implements OnClickListener, On
AppStyles.setEditorTheme(settings, root, background); AppStyles.setEditorTheme(settings, root, background);
Object data = getIntent().getSerializableExtra(KEY_LIST_EDITOR_DATA); Object data = getIntent().getSerializableExtra(KEY_LIST_EDITOR_DATA);
if (data instanceof UserList) { if (data instanceof TwitterList) {
userList = (UserList) data; userList = (TwitterList) data;
titleInput.setText(userList.getTitle()); titleInput.setText(userList.getTitle());
subTitleInput.setText(userList.getDescription()); subTitleInput.setText(userList.getDescription());
visibility.setChecked(!userList.isPrivate()); visibility.setChecked(!userList.isPrivate());
@ -146,7 +146,7 @@ public class ListEditor extends AppCompatActivity implements OnClickListener, On
/** /**
* called when a list was updated successfully * called when a list was updated successfully
*/ */
public void onSuccess(UserList result) { public void onSuccess(TwitterList result) {
if (userList != null) { if (userList != null) {
Toast.makeText(this, R.string.info_list_updated, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.info_list_updated, Toast.LENGTH_SHORT).show();
Intent data = new Intent(); Intent data = new Intent();

View File

@ -5,9 +5,6 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import androidx.annotation.MainThread; import androidx.annotation.MainThread;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -15,8 +12,9 @@ import androidx.recyclerview.widget.RecyclerView.Adapter;
import androidx.recyclerview.widget.RecyclerView.ViewHolder; import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import org.nuclearfog.twidda.R; import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.adapter.holder.Footer;
import org.nuclearfog.twidda.adapter.holder.ImageItem;
import org.nuclearfog.twidda.backend.holder.ImageHolder; import org.nuclearfog.twidda.backend.holder.ImageHolder;
import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.database.GlobalSettings; import org.nuclearfog.twidda.database.GlobalSettings;
import java.util.LinkedList; import java.util.LinkedList;
@ -49,7 +47,10 @@ public class ImageAdapter extends Adapter<ViewHolder> {
private boolean loading = false; private boolean loading = false;
private boolean saveImg = true; private boolean saveImg = true;
/**
* @param settings App settings to set theme
* @param itemClickListener click listener
*/
public ImageAdapter(GlobalSettings settings, OnImageClickListener itemClickListener) { public ImageAdapter(GlobalSettings settings, OnImageClickListener itemClickListener) {
this.itemClickListener = itemClickListener; this.itemClickListener = itemClickListener;
this.settings = settings; this.settings = settings;
@ -145,7 +146,9 @@ public class ImageAdapter extends Adapter<ViewHolder> {
return item; return item;
} else { } else {
View view = inflater.inflate(R.layout.item_image_load, parent, false); View view = inflater.inflate(R.layout.item_image_load, parent, false);
return new LoadItem(view, settings.getHighlightColor()); Footer footer = new Footer(view, settings);
footer.loadBtn.setVisibility(View.INVISIBLE);
return footer;
} }
} }
@ -160,35 +163,8 @@ public class ImageAdapter extends Adapter<ViewHolder> {
} }
/** /**
* Holder for image * click listener for image items
*/ */
private final class ImageItem extends ViewHolder {
final ImageView preview;
final ImageButton saveButton;
ImageItem(View view, GlobalSettings settings) {
super(view);
preview = view.findViewById(R.id.item_image_preview);
saveButton = view.findViewById(R.id.item_image_save);
saveButton.setImageResource(R.drawable.save);
AppStyles.setButtonColor(saveButton, settings.getFontColor());
AppStyles.setDrawableColor(saveButton, settings.getIconColor());
}
}
/**
* Holder for progress circle
*/
private final class LoadItem extends ViewHolder {
LoadItem(View v, int color) {
super(v);
ProgressBar progress = v.findViewById(R.id.imageitem_progress);
AppStyles.setProgressColor(progress, color);
}
}
public interface OnImageClickListener { public interface OnImageClickListener {
/** /**

View File

@ -4,24 +4,20 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.MainThread; import androidx.annotation.MainThread;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView.Adapter; import androidx.recyclerview.widget.RecyclerView.Adapter;
import androidx.recyclerview.widget.RecyclerView.ViewHolder; import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.squareup.picasso.Picasso; import com.squareup.picasso.Picasso;
import org.nuclearfog.twidda.R; import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.holder.UserListList; import org.nuclearfog.twidda.adapter.holder.Footer;
import org.nuclearfog.twidda.adapter.holder.ListHolder;
import org.nuclearfog.twidda.backend.items.TwitterList;
import org.nuclearfog.twidda.backend.items.User; import org.nuclearfog.twidda.backend.items.User;
import org.nuclearfog.twidda.backend.items.UserList; import org.nuclearfog.twidda.backend.lists.UserLists;
import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.database.GlobalSettings; import org.nuclearfog.twidda.database.GlobalSettings;
import org.nuclearfog.twidda.fragment.UserListFragment; import org.nuclearfog.twidda.fragment.UserListFragment;
@ -29,7 +25,6 @@ import java.text.NumberFormat;
import jp.wasabeef.picasso.transformations.RoundedCornersTransformation; import jp.wasabeef.picasso.transformations.RoundedCornersTransformation;
import static android.graphics.PorterDuff.Mode.SRC_IN;
import static android.view.View.GONE; import static android.view.View.GONE;
import static android.view.View.INVISIBLE; import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
@ -63,7 +58,7 @@ public class ListAdapter extends Adapter<ViewHolder> {
private GlobalSettings settings; private GlobalSettings settings;
private NumberFormat formatter = NumberFormat.getIntegerInstance(); private NumberFormat formatter = NumberFormat.getIntegerInstance();
private UserListList data = new UserListList(); private UserLists data = new UserLists();
private int loadingIndex = NO_LOADING; private int loadingIndex = NO_LOADING;
@ -78,11 +73,13 @@ public class ListAdapter extends Adapter<ViewHolder> {
* @param newData new list to add * @param newData new list to add
*/ */
@MainThread @MainThread
public void setData(UserListList newData) { public void setData(UserLists newData) {
if (newData.isEmpty()) { if (newData.isEmpty()) {
if (!data.isEmpty() && data.peekLast() == null) { if (!data.isEmpty() && data.peekLast() == null) {
// remove footer // remove footer
data.pollLast(); int end = data.size() - 1;
data.remove(end);
notifyItemRemoved(end);
} }
} else if (data.isEmpty() || !newData.hasPrevious()) { } else if (data.isEmpty() || !newData.hasPrevious()) {
data.replace(newData); data.replace(newData);
@ -98,7 +95,7 @@ public class ListAdapter extends Adapter<ViewHolder> {
data.remove(end); data.remove(end);
notifyItemRemoved(end); notifyItemRemoved(end);
} }
data.addListAt(newData, end); data.addAt(newData, end);
notifyItemRangeInserted(end, newData.size()); notifyItemRangeInserted(end, newData.size());
} }
disableLoading(); disableLoading();
@ -111,7 +108,7 @@ public class ListAdapter extends Adapter<ViewHolder> {
* @param list updated list * @param list updated list
*/ */
@MainThread @MainThread
public void updateItem(UserList list) { public void updateItem(TwitterList list) {
int index = data.indexOf(list); int index = data.indexOf(list);
if (index >= 0) { if (index >= 0) {
data.set(index, list); data.set(index, list);
@ -126,12 +123,9 @@ public class ListAdapter extends Adapter<ViewHolder> {
*/ */
@MainThread @MainThread
public void removeItem(long itemId) { public void removeItem(long itemId) {
for (int index = 0; index < data.size(); index++) { int index = data.removeItem(itemId);
if (data.get(index).getId() == itemId) { if (index >= 0) {
data.remove(index);
notifyItemRemoved(index); notifyItemRemoved(index);
break;
}
} }
} }
@ -155,44 +149,46 @@ public class ListAdapter extends Adapter<ViewHolder> {
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == ITEM_LIST) { if (viewType == ITEM_LIST) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list, parent, false); View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list, parent, false);
final ListHolder vh = new ListHolder(v, settings); final ListHolder itemHolder = new ListHolder(v, settings);
vh.profile_img.setOnClickListener(new OnClickListener() { itemHolder.profile_img.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
int position = vh.getLayoutPosition(); int position = itemHolder.getLayoutPosition();
if (position != NO_POSITION) { if (position != NO_POSITION) {
User user = data.get(position).getListOwner(); TwitterList item = data.get(position);
listener.onProfileClick(user); if (item != null) {
listener.onProfileClick(item.getListOwner());
}
} }
} }
}); });
v.setOnClickListener(new OnClickListener() { v.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
int position = vh.getLayoutPosition(); int position = itemHolder.getLayoutPosition();
if (position != NO_POSITION) { if (position != NO_POSITION) {
UserList list = data.get(position); TwitterList list = data.get(position);
listener.onListClick(list); listener.onListClick(list);
} }
} }
}); });
return vh; return itemHolder;
} else { } else {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_placeholder, parent, false); View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_placeholder, parent, false);
final PlaceHolder ph = new PlaceHolder(v, settings); final Footer footer = new Footer(v, settings);
ph.loadBtn.setOnClickListener(new OnClickListener() { footer.loadBtn.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
int position = ph.getLayoutPosition(); int position = footer.getLayoutPosition();
if (position != NO_POSITION) { if (position != NO_POSITION) {
listener.onFooterClick(data.getNext()); listener.onFooterClick(data.getNext());
ph.loadCircle.setVisibility(VISIBLE); footer.loadCircle.setVisibility(VISIBLE);
ph.loadBtn.setVisibility(INVISIBLE); footer.loadBtn.setVisibility(INVISIBLE);
loadingIndex = position; loadingIndex = position;
} }
} }
}); });
return ph; return footer;
} }
} }
@ -201,7 +197,8 @@ public class ListAdapter extends Adapter<ViewHolder> {
public void onBindViewHolder(@NonNull ViewHolder holder, int index) { public void onBindViewHolder(@NonNull ViewHolder holder, int index) {
if (holder instanceof ListHolder) { if (holder instanceof ListHolder) {
ListHolder vh = (ListHolder) holder; ListHolder vh = (ListHolder) holder;
UserList item = data.get(index); TwitterList item = data.get(index);
if (item != null) {
User owner = item.getListOwner(); User owner = item.getListOwner();
vh.textViews[0].setText(item.getTitle()); vh.textViews[0].setText(item.getTitle());
vh.textViews[1].setText(item.getDescription()); vh.textViews[1].setText(item.getDescription());
@ -241,14 +238,15 @@ public class ListAdapter extends Adapter<ViewHolder> {
} else { } else {
vh.icons[5].setVisibility(GONE); vh.icons[5].setVisibility(GONE);
} }
} else if (holder instanceof PlaceHolder) { }
PlaceHolder placeHolder = (PlaceHolder) holder; } else if (holder instanceof Footer) {
Footer footer = (Footer) holder;
if (loadingIndex != NO_LOADING) { if (loadingIndex != NO_LOADING) {
placeHolder.loadCircle.setVisibility(VISIBLE); footer.loadCircle.setVisibility(VISIBLE);
placeHolder.loadBtn.setVisibility(INVISIBLE); footer.loadBtn.setVisibility(INVISIBLE);
} else { } else {
placeHolder.loadCircle.setVisibility(INVISIBLE); footer.loadCircle.setVisibility(INVISIBLE);
placeHolder.loadBtn.setVisibility(VISIBLE); footer.loadBtn.setVisibility(VISIBLE);
} }
} }
} }
@ -264,75 +262,6 @@ public class ListAdapter extends Adapter<ViewHolder> {
} }
} }
/**
* view holder class for an user list item
*/
private final class ListHolder extends ViewHolder {
final ImageView[] icons = new ImageView[7];
final TextView[] textViews = new TextView[8];
final ImageView profile_img;
ListHolder(View v, GlobalSettings settings) {
super(v);
CardView background = (CardView) v;
profile_img = v.findViewById(R.id.list_owner_profile);
icons[0] = v.findViewById(R.id.list_user_verified);
icons[1] = v.findViewById(R.id.list_user_locked);
icons[2] = v.findViewById(R.id.list_member_icon);
icons[3] = v.findViewById(R.id.list_subscriber_icon);
icons[4] = v.findViewById(R.id.list_date_icon);
icons[5] = v.findViewById(R.id.list_private);
icons[6] = v.findViewById(R.id.list_follow_icon);
textViews[0] = v.findViewById(R.id.list_title);
textViews[1] = v.findViewById(R.id.list_description);
textViews[2] = v.findViewById(R.id.list_ownername);
textViews[3] = v.findViewById(R.id.list_screenname);
textViews[4] = v.findViewById(R.id.list_createdat);
textViews[5] = v.findViewById(R.id.list_member);
textViews[6] = v.findViewById(R.id.list_subscriber);
textViews[7] = v.findViewById(R.id.list_action);
icons[0].setImageResource(R.drawable.verify);
icons[1].setImageResource(R.drawable.lock);
icons[2].setImageResource(R.drawable.user);
icons[3].setImageResource(R.drawable.subscriber);
icons[4].setImageResource(R.drawable.calendar);
icons[5].setImageResource(R.drawable.lock);
icons[6].setImageResource(R.drawable.followback);
for (TextView tv : textViews) {
tv.setTextColor(settings.getFontColor());
tv.setTypeface(settings.getFontFace());
}
for (ImageView icon : icons) {
icon.setColorFilter(settings.getIconColor(), SRC_IN);
}
background.setCardBackgroundColor(settings.getCardColor());
}
}
/**
* view holder class for a footer view
*/
private final class PlaceHolder extends ViewHolder {
final ProgressBar loadCircle;
final Button loadBtn;
PlaceHolder(@NonNull View v, GlobalSettings settings) {
super(v);
CardView background = (CardView) v;
loadCircle = v.findViewById(R.id.placeholder_loading);
loadBtn = v.findViewById(R.id.placeholder_button);
loadBtn.setTypeface(settings.getFontFace());
loadBtn.setTextColor(settings.getFontColor());
AppStyles.setProgressColor(loadCircle, settings.getHighlightColor());
background.setCardBackgroundColor(settings.getCardColor());
}
}
/** /**
* Listener for an item * Listener for an item
*/ */
@ -343,7 +272,7 @@ public class ListAdapter extends Adapter<ViewHolder> {
* *
* @param listItem Item data and information * @param listItem Item data and information
*/ */
void onListClick(UserList listItem); void onListClick(TwitterList listItem);
/** /**
* called when the profile image of the owner was clicked * called when the profile image of the owner was clicked

View File

@ -1,17 +1,12 @@
package org.nuclearfog.twidda.adapter; package org.nuclearfog.twidda.adapter;
import android.text.Spanned; import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.MainThread; import androidx.annotation.MainThread;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView.Adapter; import androidx.recyclerview.widget.RecyclerView.Adapter;
import androidx.recyclerview.widget.RecyclerView.ViewHolder; import androidx.recyclerview.widget.RecyclerView.ViewHolder;
@ -20,17 +15,17 @@ import com.squareup.picasso.Picasso;
import org.nuclearfog.tag.Tagger; import org.nuclearfog.tag.Tagger;
import org.nuclearfog.tag.Tagger.OnTagClickListener; import org.nuclearfog.tag.Tagger.OnTagClickListener;
import org.nuclearfog.twidda.R; import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.adapter.holder.Footer;
import org.nuclearfog.twidda.adapter.holder.MessageHolder;
import org.nuclearfog.twidda.backend.items.Message; import org.nuclearfog.twidda.backend.items.Message;
import org.nuclearfog.twidda.backend.items.User; import org.nuclearfog.twidda.backend.items.User;
import org.nuclearfog.twidda.backend.lists.MessageList;
import org.nuclearfog.twidda.database.GlobalSettings; import org.nuclearfog.twidda.database.GlobalSettings;
import java.util.ArrayList;
import java.util.List;
import jp.wasabeef.picasso.transformations.RoundedCornersTransformation; import jp.wasabeef.picasso.transformations.RoundedCornersTransformation;
import static android.graphics.PorterDuff.Mode.SRC_IN;
import static android.view.View.GONE; import static android.view.View.GONE;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
import static org.nuclearfog.twidda.backend.utils.StringTools.getTimeString; import static org.nuclearfog.twidda.backend.utils.StringTools.getTimeString;
@ -43,27 +38,56 @@ import static org.nuclearfog.twidda.backend.utils.StringTools.getTimeString;
*/ */
public class MessageAdapter extends Adapter<ViewHolder> { public class MessageAdapter extends Adapter<ViewHolder> {
private static final int NO_INDEX = -1;
private static final int TYPE_MESSAGE = 0;
private static final int TYPE_FOOTER = 1;
private OnItemSelected itemClickListener; private OnItemSelected itemClickListener;
private GlobalSettings settings; private GlobalSettings settings;
private List<Message> messages = new ArrayList<>(); private MessageList data = new MessageList(null, null);
private int loadingIndex = NO_INDEX;
/**
* @param settings App settings for theme
* @param itemClickListener click listener
*/
public MessageAdapter(GlobalSettings settings, OnItemSelected itemClickListener) { public MessageAdapter(GlobalSettings settings, OnItemSelected itemClickListener) {
this.itemClickListener = itemClickListener; this.itemClickListener = itemClickListener;
this.settings = settings; this.settings = settings;
} }
/** /**
* replace all messages from list * set messages
* *
* @param messageList new message list * @param newData new message list
*/ */
@MainThread @MainThread
public void replaceAll(@NonNull List<Message> messageList) { public void setData(MessageList newData) {
messages.clear(); if (newData.isEmpty()) {
messages.addAll(messageList); if (!data.isEmpty() && data.peekLast() == null) {
int end = data.size() - 1;
data.remove(end);
notifyItemRemoved(end);
}
} else if (data.isEmpty() || !newData.hasPrev()) {
data.replaceAll(newData);
if (newData.hasNext()) {
// add footer
data.add(null);
}
notifyDataSetChanged(); notifyDataSetChanged();
} else {
int end = data.size() - 1;
if (!newData.hasNext()) {
// remove footer
data.remove(end);
notifyItemRemoved(end);
}
data.addAt(newData, end);
notifyItemRangeInserted(end, newData.size());
}
disableLoading();
} }
/** /**
@ -73,14 +97,8 @@ public class MessageAdapter extends Adapter<ViewHolder> {
*/ */
@MainThread @MainThread
public void remove(long id) { public void remove(long id) {
int pos = -1; int pos = data.removeItem(id);
for (int index = 0; index < messages.size() && pos < 0; index++) { if (pos >= 0) {
if (messages.get(index).getId() == id) {
messages.remove(index);
pos = index;
}
}
if (pos != -1) {
notifyItemRemoved(pos); notifyItemRemoved(pos);
} }
} }
@ -88,19 +106,31 @@ public class MessageAdapter extends Adapter<ViewHolder> {
@Override @Override
public long getItemId(int index) { public long getItemId(int index) {
return messages.get(index).getId(); Message message = data.get(index);
if (message != null)
return message.getId();
return -1;
} }
@Override @Override
public int getItemCount() { public int getItemCount() {
return messages.size(); return data.size();
}
@Override
public int getItemViewType(int index) {
if (data.get(index) == null)
return TYPE_FOOTER;
return TYPE_MESSAGE;
} }
@NonNull @NonNull
@Override @Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == TYPE_MESSAGE) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_dm, parent, false); View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_dm, parent, false);
final MessageHolder vh = new MessageHolder(view, settings); final MessageHolder vh = new MessageHolder(view, settings);
vh.buttons[0].setOnClickListener(new View.OnClickListener() { vh.buttons[0].setOnClickListener(new View.OnClickListener() {
@ -108,7 +138,7 @@ public class MessageAdapter extends Adapter<ViewHolder> {
public void onClick(View v) { public void onClick(View v) {
int position = vh.getLayoutPosition(); int position = vh.getLayoutPosition();
if (position != NO_POSITION) { if (position != NO_POSITION) {
itemClickListener.onClick(messages.get(position), OnItemSelected.Action.ANSWER); itemClickListener.onClick(data.get(position), OnItemSelected.Action.ANSWER);
} }
} }
}); });
@ -117,7 +147,7 @@ public class MessageAdapter extends Adapter<ViewHolder> {
public void onClick(View v) { public void onClick(View v) {
int position = vh.getLayoutPosition(); int position = vh.getLayoutPosition();
if (position != NO_POSITION) { if (position != NO_POSITION) {
itemClickListener.onClick(messages.get(position), OnItemSelected.Action.DELETE); itemClickListener.onClick(data.get(position), OnItemSelected.Action.DELETE);
} }
} }
}); });
@ -126,20 +156,40 @@ public class MessageAdapter extends Adapter<ViewHolder> {
public void onClick(View v) { public void onClick(View v) {
int position = vh.getLayoutPosition(); int position = vh.getLayoutPosition();
if (position != NO_POSITION) { if (position != NO_POSITION) {
itemClickListener.onClick(messages.get(position), OnItemSelected.Action.PROFILE); itemClickListener.onClick(data.get(position), OnItemSelected.Action.PROFILE);
} }
} }
}); });
return vh; return vh;
} else {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_placeholder, parent, false);
final Footer footer = new Footer(v, settings);
footer.loadBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = footer.getLayoutPosition();
if (position != NO_POSITION) {
boolean success = itemClickListener.onFooterClick(data.getNextCursor());
if (success) {
footer.loadCircle.setVisibility(VISIBLE);
footer.loadBtn.setVisibility(INVISIBLE);
loadingIndex = position;
}
}
}
});
return footer;
}
} }
@Override @Override
public void onBindViewHolder(@NonNull ViewHolder vh, int index) { public void onBindViewHolder(@NonNull ViewHolder vh, int index) {
Spanned text; if (vh instanceof MessageHolder) {
Message message = messages.get(index); Message message = data.get(index);
if (message != null) {
User sender = message.getSender(); User sender = message.getSender();
text = Tagger.makeTextWithLinks(message.getText(), settings.getHighlightColor(), itemClickListener); Spanned text = Tagger.makeTextWithLinks(message.getText(), settings.getHighlightColor(), itemClickListener);
MessageHolder holder = (MessageHolder) vh; MessageHolder holder = (MessageHolder) vh;
holder.textViews[0].setText(sender.getUsername()); holder.textViews[0].setText(sender.getUsername());
@ -167,47 +217,26 @@ public class MessageAdapter extends Adapter<ViewHolder> {
holder.profile_img.setImageResource(0); holder.profile_img.setImageResource(0);
} }
} }
} else if (vh instanceof Footer) {
Footer footer = (Footer) vh;
if (loadingIndex != NO_INDEX) {
footer.loadCircle.setVisibility(VISIBLE);
footer.loadBtn.setVisibility(INVISIBLE);
} else {
footer.loadCircle.setVisibility(INVISIBLE);
footer.loadBtn.setVisibility(VISIBLE);
}
}
}
/** /**
* Holder class for a message view * disable loading animation in footer
*/ */
private final class MessageHolder extends ViewHolder { public void disableLoading() {
if (loadingIndex != NO_INDEX) {
final TextView[] textViews = new TextView[5]; int oldIndex = loadingIndex;
final Button[] buttons = new Button[2]; loadingIndex = NO_INDEX;
final ImageView profile_img, verifiedIcon, lockedIcon; notifyItemChanged(oldIndex);
MessageHolder(View v, GlobalSettings settings) {
super(v);
CardView background = (CardView) v;
ImageView receiver_icon = v.findViewById(R.id.dm_receiver_icon);
profile_img = v.findViewById(R.id.dm_profile_img);
verifiedIcon = v.findViewById(R.id.dm_user_verified);
lockedIcon = v.findViewById(R.id.dm_user_locked);
textViews[0] = v.findViewById(R.id.dm_username);
textViews[1] = v.findViewById(R.id.dm_screenname);
textViews[2] = v.findViewById(R.id.dm_receiver);
textViews[3] = v.findViewById(R.id.dm_time);
textViews[4] = v.findViewById(R.id.dm_message);
buttons[0] = v.findViewById(R.id.dm_answer);
buttons[1] = v.findViewById(R.id.dm_delete);
for (TextView tv : textViews) {
tv.setTextColor(settings.getFontColor());
tv.setTypeface(settings.getFontFace());
}
for (Button button : buttons) {
button.setTextColor(settings.getFontColor());
button.setTypeface(settings.getFontFace());
}
receiver_icon.setImageResource(R.drawable.right);
verifiedIcon.setImageResource(R.drawable.verify);
lockedIcon.setImageResource(R.drawable.lock);
verifiedIcon.setColorFilter(settings.getIconColor(), SRC_IN);
lockedIcon.setColorFilter(settings.getIconColor(), SRC_IN);
receiver_icon.setColorFilter(settings.getIconColor(), SRC_IN);
background.setCardBackgroundColor(settings.getCardColor());
textViews[4].setMovementMethod(LinkMovementMethod.getInstance());
} }
} }
@ -219,7 +248,7 @@ public class MessageAdapter extends Adapter<ViewHolder> {
enum Action { enum Action {
ANSWER, ANSWER,
DELETE, DELETE,
PROFILE PROFILE,
} }
/** /**
@ -229,5 +258,13 @@ public class MessageAdapter extends Adapter<ViewHolder> {
* @param action what button was clicked * @param action what button was clicked
*/ */
void onClick(Message message, Action action); void onClick(Message message, Action action);
/**
* called when the footer was clicked
*
* @param cursor message cursor
* @return true if task was started
*/
boolean onFooterClick(String cursor);
} }
} }

View File

@ -5,15 +5,14 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.MainThread; import androidx.annotation.MainThread;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView.Adapter; import androidx.recyclerview.widget.RecyclerView.Adapter;
import androidx.recyclerview.widget.RecyclerView.ViewHolder; import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import org.nuclearfog.twidda.R; import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.adapter.holder.TrendHolder;
import org.nuclearfog.twidda.backend.items.Trend; import org.nuclearfog.twidda.backend.items.Trend;
import org.nuclearfog.twidda.database.GlobalSettings; import org.nuclearfog.twidda.database.GlobalSettings;
@ -86,7 +85,7 @@ public class TrendAdapter extends Adapter<ViewHolder> {
@Override @Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_trend, parent, false); View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_trend, parent, false);
final ItemHolder vh = new ItemHolder(v, settings); final TrendHolder vh = new TrendHolder(v, settings);
v.setOnClickListener(new OnClickListener() { v.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@ -102,7 +101,7 @@ public class TrendAdapter extends Adapter<ViewHolder> {
@Override @Override
public void onBindViewHolder(@NonNull ViewHolder vh, int index) { public void onBindViewHolder(@NonNull ViewHolder vh, int index) {
ItemHolder holder = (ItemHolder) vh; TrendHolder holder = (TrendHolder) vh;
Trend trend = trends.get(index); Trend trend = trends.get(index);
holder.textViews[0].setText(trend.getRankStr()); holder.textViews[0].setText(trend.getRankStr());
holder.textViews[1].setText(trend.getName()); holder.textViews[1].setText(trend.getName());
@ -116,28 +115,6 @@ public class TrendAdapter extends Adapter<ViewHolder> {
} }
} }
/**
* view holder class for an item view
*/
private final class ItemHolder extends ViewHolder {
final TextView[] textViews = new TextView[3];
ItemHolder(View v, GlobalSettings settings) {
super(v);
CardView background = (CardView) v;
textViews[0] = v.findViewById(R.id.trendpos);
textViews[1] = v.findViewById(R.id.trendname);
textViews[2] = v.findViewById(R.id.trendvol);
background.setCardBackgroundColor(settings.getCardColor());
for (TextView tv : textViews) {
tv.setTextColor(settings.getFontColor());
tv.setTypeface(settings.getFontFace());
}
}
}
/** /**
* Listener for trend list * Listener for trend list
*/ */

View File

@ -6,14 +6,9 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.MainThread; import androidx.annotation.MainThread;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView.Adapter; import androidx.recyclerview.widget.RecyclerView.Adapter;
import androidx.recyclerview.widget.RecyclerView.ViewHolder; import androidx.recyclerview.widget.RecyclerView.ViewHolder;
@ -21,9 +16,10 @@ import com.squareup.picasso.Picasso;
import org.nuclearfog.tag.Tagger; import org.nuclearfog.tag.Tagger;
import org.nuclearfog.twidda.R; import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.adapter.holder.Footer;
import org.nuclearfog.twidda.adapter.holder.TweetHolder;
import org.nuclearfog.twidda.backend.items.Tweet; import org.nuclearfog.twidda.backend.items.Tweet;
import org.nuclearfog.twidda.backend.items.User; import org.nuclearfog.twidda.backend.items.User;
import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.database.GlobalSettings; import org.nuclearfog.twidda.database.GlobalSettings;
import java.text.NumberFormat; import java.text.NumberFormat;
@ -227,11 +223,11 @@ public class TweetAdapter extends Adapter<ViewHolder> {
return vh; return vh;
} else { } else {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_placeholder, parent, false); View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_placeholder, parent, false);
final PlaceHolder vh = new PlaceHolder(v, settings); final Footer footer = new Footer(v, settings);
vh.loadBtn.setOnClickListener(new OnClickListener() { footer.loadBtn.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
int position = vh.getLayoutPosition(); int position = footer.getLayoutPosition();
if (position != NO_POSITION) { if (position != NO_POSITION) {
long sinceId = 0; long sinceId = 0;
long maxId = 0; long maxId = 0;
@ -243,14 +239,16 @@ public class TweetAdapter extends Adapter<ViewHolder> {
sinceId = tweets.get(position + 1).getId(); sinceId = tweets.get(position + 1).getId();
maxId = tweets.get(position - 1).getId(); maxId = tweets.get(position - 1).getId();
} }
itemClickListener.onHolderClick(sinceId, maxId, position); boolean success = itemClickListener.onHolderClick(sinceId, maxId, position);
vh.loadCircle.setVisibility(VISIBLE); if (success) {
vh.loadBtn.setVisibility(INVISIBLE); footer.loadCircle.setVisibility(VISIBLE);
footer.loadBtn.setVisibility(INVISIBLE);
loadingIndex = position; loadingIndex = position;
} }
} }
}
}); });
return vh; return footer;
} }
} }
@ -259,125 +257,67 @@ public class TweetAdapter extends Adapter<ViewHolder> {
public void onBindViewHolder(@NonNull ViewHolder holder, int index) { public void onBindViewHolder(@NonNull ViewHolder holder, int index) {
Tweet tweet = tweets.get(index); Tweet tweet = tweets.get(index);
if (holder instanceof TweetHolder && tweet != null) { if (holder instanceof TweetHolder && tweet != null) {
TweetHolder vh = (TweetHolder) holder; TweetHolder tweetItem = (TweetHolder) holder;
User user = tweet.getUser(); User user = tweet.getUser();
if (tweet.getEmbeddedTweet() != null) { if (tweet.getEmbeddedTweet() != null) {
vh.textViews[5].setText(user.getScreenname()); tweetItem.textViews[5].setText(user.getScreenname());
vh.textViews[5].setVisibility(VISIBLE); tweetItem.textViews[5].setVisibility(VISIBLE);
vh.rtUser.setVisibility(VISIBLE); tweetItem.rtUser.setVisibility(VISIBLE);
tweet = tweet.getEmbeddedTweet(); tweet = tweet.getEmbeddedTweet();
user = tweet.getUser(); user = tweet.getUser();
} else { } else {
vh.textViews[5].setVisibility(INVISIBLE); tweetItem.textViews[5].setVisibility(INVISIBLE);
vh.rtUser.setVisibility(INVISIBLE); tweetItem.rtUser.setVisibility(INVISIBLE);
} }
Spanned text = Tagger.makeTextWithLinks(tweet.getTweet(), settings.getHighlightColor()); Spanned text = Tagger.makeTextWithLinks(tweet.getTweet(), settings.getHighlightColor());
vh.textViews[2].setText(text); tweetItem.textViews[2].setText(text);
vh.textViews[0].setText(user.getUsername()); tweetItem.textViews[0].setText(user.getUsername());
vh.textViews[1].setText(user.getScreenname()); tweetItem.textViews[1].setText(user.getScreenname());
vh.textViews[3].setText(formatter.format(tweet.getRetweetCount())); tweetItem.textViews[3].setText(formatter.format(tweet.getRetweetCount()));
vh.textViews[4].setText(formatter.format(tweet.getFavoriteCount())); tweetItem.textViews[4].setText(formatter.format(tweet.getFavoriteCount()));
vh.textViews[6].setText(getTimeString(tweet.getTime())); tweetItem.textViews[6].setText(getTimeString(tweet.getTime()));
if (tweet.retweeted()) { if (tweet.retweeted()) {
vh.rtIcon.setColorFilter(Color.GREEN, SRC_IN); tweetItem.rtIcon.setColorFilter(Color.GREEN, SRC_IN);
} else { } else {
vh.rtIcon.setColorFilter(settings.getIconColor(), SRC_IN); tweetItem.rtIcon.setColorFilter(settings.getIconColor(), SRC_IN);
} }
if (tweet.favored()) { if (tweet.favored()) {
vh.favIcon.setColorFilter(Color.YELLOW, SRC_IN); tweetItem.favIcon.setColorFilter(Color.YELLOW, SRC_IN);
} else { } else {
vh.favIcon.setColorFilter(settings.getIconColor(), SRC_IN); tweetItem.favIcon.setColorFilter(settings.getIconColor(), SRC_IN);
} }
if (user.isVerified()) { if (user.isVerified()) {
vh.verifiedIcon.setVisibility(VISIBLE); tweetItem.verifiedIcon.setVisibility(VISIBLE);
} else { } else {
vh.verifiedIcon.setVisibility(GONE); tweetItem.verifiedIcon.setVisibility(GONE);
} }
if (user.isLocked()) { if (user.isLocked()) {
vh.lockedIcon.setVisibility(VISIBLE); tweetItem.lockedIcon.setVisibility(VISIBLE);
} else { } else {
vh.lockedIcon.setVisibility(GONE); tweetItem.lockedIcon.setVisibility(GONE);
} }
if (settings.getImageLoad() && user.hasProfileImage()) { if (settings.getImageLoad() && user.hasProfileImage()) {
String pbLink = user.getImageLink(); String pbLink = user.getImageLink();
if (!user.hasDefaultProfileImage()) if (!user.hasDefaultProfileImage())
pbLink += settings.getImageSuffix(); pbLink += settings.getImageSuffix();
Picasso.get().load(pbLink).transform(new RoundedCornersTransformation(2, 0)) Picasso.get().load(pbLink).transform(new RoundedCornersTransformation(2, 0))
.error(R.drawable.no_image).into(vh.profile); .error(R.drawable.no_image).into(tweetItem.profile);
} else { } else {
vh.profile.setImageResource(0); tweetItem.profile.setImageResource(0);
} }
} else if (holder instanceof PlaceHolder) { } else if (holder instanceof Footer) {
PlaceHolder vh = (PlaceHolder) holder; Footer footer = (Footer) holder;
if (loadingIndex != NO_INDEX) { if (loadingIndex != NO_INDEX) {
vh.loadCircle.setVisibility(VISIBLE); footer.loadCircle.setVisibility(VISIBLE);
vh.loadBtn.setVisibility(INVISIBLE); footer.loadBtn.setVisibility(INVISIBLE);
} else { } else {
vh.loadCircle.setVisibility(INVISIBLE); footer.loadCircle.setVisibility(INVISIBLE);
vh.loadBtn.setVisibility(VISIBLE); footer.loadBtn.setVisibility(VISIBLE);
} }
} }
} }
/**
* Holder class for the tweet view
*/
private final class TweetHolder extends ViewHolder {
final TextView[] textViews = new TextView[7];
final ImageView profile, rtUser, verifiedIcon, lockedIcon, rtIcon, favIcon;
TweetHolder(@NonNull View v, GlobalSettings settings) {
super(v);
CardView background = (CardView) v;
profile = v.findViewById(R.id.tweetPb);
verifiedIcon = v.findViewById(R.id.verified_icon);
lockedIcon = v.findViewById(R.id.locked_icon);
rtUser = v.findViewById(R.id.rt_user_icon);
rtIcon = v.findViewById(R.id.rt_icon);
favIcon = v.findViewById(R.id.fav_icon);
textViews[0] = v.findViewById(R.id.username);
textViews[1] = v.findViewById(R.id.screenname);
textViews[2] = v.findViewById(R.id.tweettext);
textViews[3] = v.findViewById(R.id.retweet_number);
textViews[4] = v.findViewById(R.id.favorite_number);
textViews[5] = v.findViewById(R.id.retweeter);
textViews[6] = v.findViewById(R.id.time);
for (TextView tv : textViews) {
tv.setTextColor(settings.getFontColor());
tv.setTypeface(settings.getFontFace());
}
verifiedIcon.setImageResource(R.drawable.verify);
lockedIcon.setImageResource(R.drawable.lock);
rtUser.setImageResource(R.drawable.retweet);
rtIcon.setImageResource(R.drawable.retweet);
favIcon.setImageResource(R.drawable.favorite);
verifiedIcon.setColorFilter(settings.getIconColor(), SRC_IN);
lockedIcon.setColorFilter(settings.getIconColor(), SRC_IN);
rtUser.setColorFilter(settings.getIconColor(), SRC_IN);
background.setCardBackgroundColor(settings.getCardColor());
}
}
/**
* Holder class for the placeholder view
*/
private final class PlaceHolder extends ViewHolder {
final Button loadBtn;
final ProgressBar loadCircle;
PlaceHolder(@NonNull View v, GlobalSettings settings) {
super(v);
CardView background = (CardView) v;
loadBtn = v.findViewById(R.id.placeholder_button);
loadCircle = v.findViewById(R.id.placeholder_loading);
background.setCardBackgroundColor(settings.getCardColor());
AppStyles.setProgressColor(loadCircle, settings.getHighlightColor());
}
}
/** /**
* Listener for tweet click * Listener for tweet click
*/ */
@ -396,7 +336,8 @@ public class TweetAdapter extends Adapter<ViewHolder> {
* @param sinceId the tweet ID of the tweet below the holder * @param sinceId the tweet ID of the tweet below the holder
* @param maxId the tweet ID of the tweet over the holder * @param maxId the tweet ID of the tweet over the holder
* @param pos position of the holder * @param pos position of the holder
* @return true if click was handled
*/ */
void onHolderClick(long sinceId, long maxId, int pos); boolean onHolderClick(long sinceId, long maxId, int pos);
} }
} }

View File

@ -4,31 +4,25 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.MainThread; import androidx.annotation.MainThread;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView.Adapter; import androidx.recyclerview.widget.RecyclerView.Adapter;
import androidx.recyclerview.widget.RecyclerView.ViewHolder; import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.squareup.picasso.Picasso; import com.squareup.picasso.Picasso;
import org.nuclearfog.twidda.R; import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.holder.TwitterUserList; import org.nuclearfog.twidda.adapter.holder.Footer;
import org.nuclearfog.twidda.adapter.holder.UserHolder;
import org.nuclearfog.twidda.backend.items.User; import org.nuclearfog.twidda.backend.items.User;
import org.nuclearfog.twidda.backend.utils.AppStyles; import org.nuclearfog.twidda.backend.lists.UserList;
import org.nuclearfog.twidda.database.GlobalSettings; import org.nuclearfog.twidda.database.GlobalSettings;
import java.text.NumberFormat; import java.text.NumberFormat;
import jp.wasabeef.picasso.transformations.RoundedCornersTransformation; import jp.wasabeef.picasso.transformations.RoundedCornersTransformation;
import static android.graphics.PorterDuff.Mode.SRC_IN;
import static android.view.View.GONE; import static android.view.View.GONE;
import static android.view.View.INVISIBLE; import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
@ -58,11 +52,12 @@ public class UserAdapter extends Adapter<ViewHolder> {
*/ */
private static final int ITEM_GAP = 1; private static final int ITEM_GAP = 1;
private static final NumberFormat FORMATTER = NumberFormat.getIntegerInstance();
private UserClickListener itemClickListener; private UserClickListener itemClickListener;
private GlobalSettings settings; private GlobalSettings settings;
private TwitterUserList items = new TwitterUserList(); private UserList data = new UserList();
private NumberFormat formatter = NumberFormat.getIntegerInstance();
private int loadingIndex = NO_INDEX; private int loadingIndex = NO_INDEX;
private boolean userRemovable = false; private boolean userRemovable = false;
@ -81,27 +76,29 @@ public class UserAdapter extends Adapter<ViewHolder> {
* @param newData new userlist * @param newData new userlist
*/ */
@MainThread @MainThread
public void setData(@NonNull TwitterUserList newData) { public void setData(@NonNull UserList newData) {
if (newData.isEmpty()) { if (newData.isEmpty()) {
if (!items.isEmpty() && items.peekLast() == null) { if (!data.isEmpty() && data.peekLast() == null) {
// remove footer // remove footer
items.pollLast(); int end = data.size() - 1;
data.remove(end);
notifyItemRemoved(end);
} }
} else if (items.isEmpty() || !newData.hasPrevious()) { } else if (data.isEmpty() || !newData.hasPrevious()) {
items.replace(newData); data.replace(newData);
if (newData.hasNext()) { if (newData.hasNext()) {
// add footer // add footer
items.add(null); data.add(null);
} }
notifyDataSetChanged(); notifyDataSetChanged();
} else { } else {
int end = items.size() - 1; int end = data.size() - 1;
if (!newData.hasNext()) { if (!newData.hasNext()) {
// remove footer // remove footer
items.remove(end); data.remove(end);
notifyItemRemoved(end); notifyItemRemoved(end);
} }
items.addListAt(newData, end); data.addAt(newData, end);
notifyItemRangeInserted(end, newData.size()); notifyItemRangeInserted(end, newData.size());
} }
disableLoading(); disableLoading();
@ -114,9 +111,9 @@ public class UserAdapter extends Adapter<ViewHolder> {
*/ */
@MainThread @MainThread
public void updateUser(User user) { public void updateUser(User user) {
int index = items.indexOf(user); int index = data.indexOf(user);
if (index >= 0) { if (index >= 0) {
items.set(index, user); data.set(index, user);
notifyItemChanged(index); notifyItemChanged(index);
} }
} }
@ -124,37 +121,36 @@ public class UserAdapter extends Adapter<ViewHolder> {
/** /**
* remove user from adapter * remove user from adapter
* *
* @param username User to remove * @param screenname User to remove
*/ */
@MainThread @MainThread
public void removeUser(String username) { public void removeUser(String screenname) {
for (int pos = 0; pos < items.size(); pos++) { int pos = data.removeItem(screenname);
if (items.get(pos).getScreenname().equals(username)) { if (pos >= 0) {
items.remove(pos); data.remove(pos);
notifyItemRemoved(pos); notifyItemRemoved(pos);
break;
}
} }
} }
@Override @Override
public int getItemCount() { public int getItemCount() {
return items.size(); return data.size();
} }
@Override @Override
public long getItemId(int index) { public long getItemId(int index) {
if (items.get(index) != null) User user = data.get(index);
return items.get(index).getId(); if (user != null)
return user.getId();
return NO_ID; return NO_ID;
} }
@Override @Override
public int getItemViewType(int index) { public int getItemViewType(int index) {
if (items.get(index) == null) if (data.get(index) == null)
return ITEM_GAP; return ITEM_GAP;
return ITEM_USER; return ITEM_USER;
} }
@ -165,12 +161,12 @@ public class UserAdapter extends Adapter<ViewHolder> {
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == ITEM_USER) { if (viewType == ITEM_USER) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_user, parent, false); View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_user, parent, false);
final ItemHolder vh = new ItemHolder(v, settings); final UserHolder vh = new UserHolder(v, settings);
v.setOnClickListener(new OnClickListener() { v.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
int position = vh.getLayoutPosition(); int position = vh.getLayoutPosition();
User user = items.get(position); User user = data.get(position);
if (position != NO_POSITION && user != null) { if (position != NO_POSITION && user != null) {
itemClickListener.onUserClick(user); itemClickListener.onUserClick(user);
} }
@ -182,7 +178,7 @@ public class UserAdapter extends Adapter<ViewHolder> {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
int position = vh.getLayoutPosition(); int position = vh.getLayoutPosition();
User user = items.get(position); User user = data.get(position);
if (position != NO_POSITION && user != null) { if (position != NO_POSITION && user != null) {
itemClickListener.onDelete(user.getScreenname()); itemClickListener.onDelete(user.getScreenname());
} }
@ -194,18 +190,20 @@ public class UserAdapter extends Adapter<ViewHolder> {
return vh; return vh;
} else { } else {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_placeholder, parent, false); View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_placeholder, parent, false);
final PlaceHolder vh = new PlaceHolder(v, settings); final Footer vh = new Footer(v, settings);
vh.loadBtn.setOnClickListener(new OnClickListener() { vh.loadBtn.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
int position = vh.getLayoutPosition(); int position = vh.getLayoutPosition();
if (position != NO_POSITION) { if (position != NO_POSITION) {
itemClickListener.onFooterClick(items.getNext()); boolean success = itemClickListener.onFooterClick(data.getNext());
if (success) {
vh.loadCircle.setVisibility(VISIBLE); vh.loadCircle.setVisibility(VISIBLE);
vh.loadBtn.setVisibility(INVISIBLE); vh.loadBtn.setVisibility(INVISIBLE);
loadingIndex = position; loadingIndex = position;
} }
} }
}
}); });
return vh; return vh;
} }
@ -214,34 +212,34 @@ public class UserAdapter extends Adapter<ViewHolder> {
@Override @Override
public void onBindViewHolder(@NonNull ViewHolder holder, int index) { public void onBindViewHolder(@NonNull ViewHolder holder, int index) {
User user = items.get(index); User user = data.get(index);
if (holder instanceof ItemHolder && user != null) { if (holder instanceof UserHolder && user != null) {
ItemHolder vh = (ItemHolder) holder; UserHolder userholder = (UserHolder) holder;
vh.username.setText(user.getUsername()); userholder.textViews[0].setText(user.getUsername());
vh.screenname.setText(user.getScreenname()); userholder.textViews[1].setText(user.getScreenname());
vh.following.setText(formatter.format(user.getFollowing())); userholder.textViews[2].setText(FORMATTER.format(user.getFollowing()));
vh.follower.setText(formatter.format(user.getFollower())); userholder.textViews[3].setText(FORMATTER.format(user.getFollower()));
if (user.isVerified()) { if (user.isVerified()) {
vh.verifyIcon.setVisibility(VISIBLE); userholder.verifyIcon.setVisibility(VISIBLE);
} else { } else {
vh.verifyIcon.setVisibility(GONE); userholder.verifyIcon.setVisibility(GONE);
} }
if (user.isLocked()) { if (user.isLocked()) {
vh.lockedIcon.setVisibility(VISIBLE); userholder.lockedIcon.setVisibility(VISIBLE);
} else { } else {
vh.lockedIcon.setVisibility(GONE); userholder.lockedIcon.setVisibility(GONE);
} }
if (settings.getImageLoad() && user.hasProfileImage()) { if (settings.getImageLoad() && user.hasProfileImage()) {
String pbLink = user.getImageLink(); String pbLink = user.getImageLink();
if (!user.hasDefaultProfileImage()) if (!user.hasDefaultProfileImage())
pbLink += settings.getImageSuffix(); pbLink += settings.getImageSuffix();
Picasso.get().load(pbLink).transform(new RoundedCornersTransformation(2, 0)) Picasso.get().load(pbLink).transform(new RoundedCornersTransformation(2, 0))
.error(R.drawable.no_image).into(vh.profileImg); .error(R.drawable.no_image).into(userholder.profileImg);
} else { } else {
vh.profileImg.setImageResource(0); userholder.profileImg.setImageResource(0);
} }
} else if (holder instanceof PlaceHolder) { } else if (holder instanceof Footer) {
PlaceHolder vh = (PlaceHolder) holder; Footer vh = (Footer) holder;
if (loadingIndex != NO_INDEX) { if (loadingIndex != NO_INDEX) {
vh.loadCircle.setVisibility(VISIBLE); vh.loadCircle.setVisibility(VISIBLE);
vh.loadBtn.setVisibility(INVISIBLE); vh.loadBtn.setVisibility(INVISIBLE);
@ -272,71 +270,6 @@ public class UserAdapter extends Adapter<ViewHolder> {
userRemovable = enable; userRemovable = enable;
} }
/**
* Holder for an user view item
*/
private final class ItemHolder extends ViewHolder {
final TextView username, screenname, following, follower;
final ImageView profileImg, verifyIcon, lockedIcon;
final ImageButton delete;
ItemHolder(View v, GlobalSettings settings) {
super(v);
CardView background = (CardView) v;
ImageView followingIcon = v.findViewById(R.id.following_icon);
ImageView followerIcon = v.findViewById(R.id.follower_icon);
username = v.findViewById(R.id.username_detail);
screenname = v.findViewById(R.id.screenname_detail);
following = v.findViewById(R.id.item_user_friends);
follower = v.findViewById(R.id.item_user_follower);
profileImg = v.findViewById(R.id.user_profileimg);
verifyIcon = v.findViewById(R.id.useritem_verified);
lockedIcon = v.findViewById(R.id.useritem_locked);
delete = v.findViewById(R.id.useritem_del_user);
followerIcon.setImageResource(R.drawable.follower);
followingIcon.setImageResource(R.drawable.following);
verifyIcon.setImageResource(R.drawable.verify);
lockedIcon.setImageResource(R.drawable.lock);
delete.setImageResource(R.drawable.cross);
background.setCardBackgroundColor(settings.getCardColor());
followerIcon.setColorFilter(settings.getIconColor(), SRC_IN);
followingIcon.setColorFilter(settings.getIconColor(), SRC_IN);
verifyIcon.setColorFilter(settings.getIconColor(), SRC_IN);
lockedIcon.setColorFilter(settings.getIconColor(), SRC_IN);
delete.setColorFilter(settings.getIconColor(), SRC_IN);
username.setTextColor(settings.getFontColor());
username.setTypeface(settings.getFontFace());
screenname.setTextColor(settings.getFontColor());
screenname.setTypeface(settings.getFontFace());
following.setTextColor(settings.getFontColor());
following.setTypeface(settings.getFontFace());
follower.setTextColor(settings.getFontColor());
follower.setTypeface(settings.getFontFace());
}
}
/**
* Holder for a footer view
*/
private final class PlaceHolder extends ViewHolder {
final ProgressBar loadCircle;
final Button loadBtn;
PlaceHolder(@NonNull View v, GlobalSettings settings) {
super(v);
CardView background = (CardView) v;
loadCircle = v.findViewById(R.id.placeholder_loading);
loadBtn = v.findViewById(R.id.placeholder_button);
background.setCardBackgroundColor(settings.getCardColor());
AppStyles.setProgressColor(loadCircle, settings.getHighlightColor());
}
}
/** /**
* Listener for list click * Listener for list click
*/ */
@ -353,8 +286,9 @@ public class UserAdapter extends Adapter<ViewHolder> {
* handle footer click * handle footer click
* *
* @param cursor next cursor of the list * @param cursor next cursor of the list
* @return true if click was handled
*/ */
void onFooterClick(long cursor); boolean onFooterClick(long cursor);
/** /**
* remove user from a list * remove user from a list

View File

@ -0,0 +1,37 @@
package org.nuclearfog.twidda.adapter.holder;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.database.GlobalSettings;
/**
* ViewHolder class for a footer/placeholder view
*
* @author nuclearfog
*/
public class Footer extends RecyclerView.ViewHolder {
public final ProgressBar loadCircle;
public final Button loadBtn;
public Footer(@NonNull View v, GlobalSettings settings) {
super(v);
CardView background = (CardView) v;
loadCircle = v.findViewById(R.id.placeholder_loading);
loadBtn = v.findViewById(R.id.placeholder_button);
background.setCardBackgroundColor(settings.getCardColor());
loadBtn.setTextColor(settings.getFontColor());
loadBtn.setTypeface(settings.getFontFace());
AppStyles.setButtonColor(loadBtn, settings.getFontColor());
AppStyles.setProgressColor(loadCircle, settings.getHighlightColor());
}
}

View File

@ -0,0 +1,32 @@
package org.nuclearfog.twidda.adapter.holder;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import androidx.recyclerview.widget.RecyclerView;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.database.GlobalSettings;
/**
* Holder for image item
*
* @author nuclearfog
* @see org.nuclearfog.twidda.adapter.ImageAdapter
*/
public final class ImageItem extends RecyclerView.ViewHolder {
public final ImageView preview;
public final ImageButton saveButton;
public ImageItem(View view, GlobalSettings settings) {
super(view);
preview = view.findViewById(R.id.item_image_preview);
saveButton = view.findViewById(R.id.item_image_save);
saveButton.setImageResource(R.drawable.save);
AppStyles.setButtonColor(saveButton, settings.getFontColor());
AppStyles.setDrawableColor(saveButton, settings.getIconColor());
}
}

View File

@ -0,0 +1,65 @@
package org.nuclearfog.twidda.adapter.holder;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.database.GlobalSettings;
import static android.graphics.PorterDuff.Mode.SRC_IN;
/**
* view holder class for an user list item
*
* @author nuclearfog
* @see android.widget.ListAdapter
*/
public class ListHolder extends RecyclerView.ViewHolder {
public final ImageView[] icons = new ImageView[7];
public final TextView[] textViews = new TextView[8];
public final ImageView profile_img;
public ListHolder(View v, GlobalSettings settings) {
super(v);
CardView background = (CardView) v;
profile_img = v.findViewById(R.id.list_owner_profile);
icons[0] = v.findViewById(R.id.list_user_verified);
icons[1] = v.findViewById(R.id.list_user_locked);
icons[2] = v.findViewById(R.id.list_member_icon);
icons[3] = v.findViewById(R.id.list_subscriber_icon);
icons[4] = v.findViewById(R.id.list_date_icon);
icons[5] = v.findViewById(R.id.list_private);
icons[6] = v.findViewById(R.id.list_follow_icon);
textViews[0] = v.findViewById(R.id.list_title);
textViews[1] = v.findViewById(R.id.list_description);
textViews[2] = v.findViewById(R.id.list_ownername);
textViews[3] = v.findViewById(R.id.list_screenname);
textViews[4] = v.findViewById(R.id.list_createdat);
textViews[5] = v.findViewById(R.id.list_member);
textViews[6] = v.findViewById(R.id.list_subscriber);
textViews[7] = v.findViewById(R.id.list_action);
icons[0].setImageResource(R.drawable.verify);
icons[1].setImageResource(R.drawable.lock);
icons[2].setImageResource(R.drawable.user);
icons[3].setImageResource(R.drawable.subscriber);
icons[4].setImageResource(R.drawable.calendar);
icons[5].setImageResource(R.drawable.lock);
icons[6].setImageResource(R.drawable.followback);
for (TextView tv : textViews) {
tv.setTextColor(settings.getFontColor());
tv.setTypeface(settings.getFontFace());
}
for (ImageView icon : icons) {
icon.setColorFilter(settings.getIconColor(), SRC_IN);
}
background.setCardBackgroundColor(settings.getCardColor());
}
}

View File

@ -0,0 +1,63 @@
package org.nuclearfog.twidda.adapter.holder;
import android.text.method.LinkMovementMethod;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.database.GlobalSettings;
import static android.graphics.PorterDuff.Mode.SRC_IN;
/**
* Holder class for a message view
*
* @author nuclearfog
* @see org.nuclearfog.twidda.adapter.MessageAdapter
*/
public class MessageHolder extends RecyclerView.ViewHolder {
public final TextView[] textViews = new TextView[5];
public final Button[] buttons = new Button[2];
public final ImageView profile_img, verifiedIcon, lockedIcon;
public MessageHolder(View v, GlobalSettings settings) {
super(v);
CardView background = (CardView) v;
ImageView receiver_icon = v.findViewById(R.id.dm_receiver_icon);
profile_img = v.findViewById(R.id.dm_profile_img);
verifiedIcon = v.findViewById(R.id.dm_user_verified);
lockedIcon = v.findViewById(R.id.dm_user_locked);
textViews[0] = v.findViewById(R.id.dm_username);
textViews[1] = v.findViewById(R.id.dm_screenname);
textViews[2] = v.findViewById(R.id.dm_receiver);
textViews[3] = v.findViewById(R.id.dm_time);
textViews[4] = v.findViewById(R.id.dm_message);
buttons[0] = v.findViewById(R.id.dm_answer);
buttons[1] = v.findViewById(R.id.dm_delete);
for (TextView tv : textViews) {
tv.setTextColor(settings.getFontColor());
tv.setTypeface(settings.getFontFace());
}
for (Button button : buttons) {
button.setTextColor(settings.getFontColor());
button.setTypeface(settings.getFontFace());
AppStyles.setButtonColor(button, settings.getFontColor());
}
receiver_icon.setImageResource(R.drawable.right);
verifiedIcon.setImageResource(R.drawable.verify);
lockedIcon.setImageResource(R.drawable.lock);
verifiedIcon.setColorFilter(settings.getIconColor(), SRC_IN);
lockedIcon.setColorFilter(settings.getIconColor(), SRC_IN);
receiver_icon.setColorFilter(settings.getIconColor(), SRC_IN);
background.setCardBackgroundColor(settings.getCardColor());
textViews[4].setMovementMethod(LinkMovementMethod.getInstance());
}
}

View File

@ -0,0 +1,35 @@
package org.nuclearfog.twidda.adapter.holder;
import android.view.View;
import android.widget.TextView;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.database.GlobalSettings;
/**
* ViewHolder for a trend item
*
* @author nuclearfog
* @see org.nuclearfog.twidda.adapter.TrendAdapter
*/
public class TrendHolder extends RecyclerView.ViewHolder {
public final TextView[] textViews = new TextView[3];
public TrendHolder(View v, GlobalSettings settings) {
super(v);
CardView background = (CardView) v;
textViews[0] = v.findViewById(R.id.trendpos);
textViews[1] = v.findViewById(R.id.trendname);
textViews[2] = v.findViewById(R.id.trendvol);
background.setCardBackgroundColor(settings.getCardColor());
for (TextView tv : textViews) {
tv.setTextColor(settings.getFontColor());
tv.setTypeface(settings.getFontFace());
}
}
}

View File

@ -0,0 +1,58 @@
package org.nuclearfog.twidda.adapter.holder;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.database.GlobalSettings;
import static android.graphics.PorterDuff.Mode.SRC_IN;
/**
* Holder class for the tweet view
*
* @author nuclearfog
* @see org.nuclearfog.twidda.adapter.TweetAdapter
*/
public class TweetHolder extends RecyclerView.ViewHolder {
public final TextView[] textViews = new TextView[7];
public final ImageView profile, rtUser, verifiedIcon, lockedIcon, rtIcon, favIcon;
public TweetHolder(@NonNull View v, GlobalSettings settings) {
super(v);
CardView background = (CardView) v;
profile = v.findViewById(R.id.tweetPb);
verifiedIcon = v.findViewById(R.id.verified_icon);
lockedIcon = v.findViewById(R.id.locked_icon);
rtUser = v.findViewById(R.id.rt_user_icon);
rtIcon = v.findViewById(R.id.rt_icon);
favIcon = v.findViewById(R.id.fav_icon);
textViews[0] = v.findViewById(R.id.username);
textViews[1] = v.findViewById(R.id.screenname);
textViews[2] = v.findViewById(R.id.tweettext);
textViews[3] = v.findViewById(R.id.retweet_number);
textViews[4] = v.findViewById(R.id.favorite_number);
textViews[5] = v.findViewById(R.id.retweeter);
textViews[6] = v.findViewById(R.id.time);
verifiedIcon.setImageResource(R.drawable.verify);
lockedIcon.setImageResource(R.drawable.lock);
rtUser.setImageResource(R.drawable.retweet);
rtIcon.setImageResource(R.drawable.retweet);
favIcon.setImageResource(R.drawable.favorite);
verifiedIcon.setColorFilter(settings.getIconColor(), SRC_IN);
lockedIcon.setColorFilter(settings.getIconColor(), SRC_IN);
rtUser.setColorFilter(settings.getIconColor(), SRC_IN);
background.setCardBackgroundColor(settings.getCardColor());
for (TextView tv : textViews) {
tv.setTextColor(settings.getFontColor());
tv.setTypeface(settings.getFontFace());
}
}
}

View File

@ -0,0 +1,62 @@
package org.nuclearfog.twidda.adapter.holder;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.database.GlobalSettings;
import static android.graphics.PorterDuff.Mode.SRC_IN;
/**
* View holder class for user item
*
* @author nuclearfog
* @see org.nuclearfog.twidda.adapter.UserAdapter
*/
public class UserHolder extends RecyclerView.ViewHolder {
public final TextView[] textViews = new TextView[4];
public final ImageView profileImg, verifyIcon, lockedIcon;
public final ImageButton delete;
public UserHolder(View v, GlobalSettings settings) {
super(v);
CardView background = (CardView) v;
ImageView followingIcon = v.findViewById(R.id.following_icon);
ImageView followerIcon = v.findViewById(R.id.follower_icon);
textViews[0] = v.findViewById(R.id.username_detail);
textViews[1] = v.findViewById(R.id.screenname_detail);
textViews[2] = v.findViewById(R.id.item_user_friends);
textViews[3] = v.findViewById(R.id.item_user_follower);
profileImg = v.findViewById(R.id.user_profileimg);
verifyIcon = v.findViewById(R.id.useritem_verified);
lockedIcon = v.findViewById(R.id.useritem_locked);
delete = v.findViewById(R.id.useritem_del_user);
followerIcon.setImageResource(R.drawable.follower);
followingIcon.setImageResource(R.drawable.following);
verifyIcon.setImageResource(R.drawable.verify);
lockedIcon.setImageResource(R.drawable.lock);
delete.setImageResource(R.drawable.cross);
background.setCardBackgroundColor(settings.getCardColor());
followerIcon.setColorFilter(settings.getIconColor(), SRC_IN);
followingIcon.setColorFilter(settings.getIconColor(), SRC_IN);
verifyIcon.setColorFilter(settings.getIconColor(), SRC_IN);
lockedIcon.setColorFilter(settings.getIconColor(), SRC_IN);
delete.setColorFilter(settings.getIconColor(), SRC_IN);
AppStyles.setButtonColor(delete, settings.getFontColor());
for (TextView tv : textViews) {
tv.setTextColor(settings.getFontColor());
tv.setTypeface(settings.getFontFace());
}
}
}

View File

@ -5,7 +5,7 @@ import android.os.AsyncTask;
import org.nuclearfog.twidda.activity.ListDetail; import org.nuclearfog.twidda.activity.ListDetail;
import org.nuclearfog.twidda.backend.engine.EngineException; import org.nuclearfog.twidda.backend.engine.EngineException;
import org.nuclearfog.twidda.backend.engine.TwitterEngine; import org.nuclearfog.twidda.backend.engine.TwitterEngine;
import org.nuclearfog.twidda.backend.items.UserList; import org.nuclearfog.twidda.backend.items.TwitterList;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -14,7 +14,7 @@ import java.lang.ref.WeakReference;
* *
* @author nuclearfog * @author nuclearfog
*/ */
public class ListAction extends AsyncTask<Long, Void, UserList> { public class ListAction extends AsyncTask<Long, Void, TwitterList> {
/** /**
* Actions to perform * Actions to perform
@ -58,7 +58,7 @@ public class ListAction extends AsyncTask<Long, Void, UserList> {
@Override @Override
protected UserList doInBackground(Long... ids) { protected TwitterList doInBackground(Long... ids) {
try { try {
long listId = ids[0]; long listId = ids[0];
switch (action) { switch (action) {
@ -83,7 +83,7 @@ public class ListAction extends AsyncTask<Long, Void, UserList> {
@Override @Override
protected void onPostExecute(UserList userList) { protected void onPostExecute(TwitterList userList) {
ListDetail callback = this.callback.get(); ListDetail callback = this.callback.get();
if (callback != null) { if (callback != null) {
if (userList != null) { if (userList != null) {

View File

@ -6,7 +6,7 @@ import androidx.annotation.Nullable;
import org.nuclearfog.twidda.backend.engine.EngineException; import org.nuclearfog.twidda.backend.engine.EngineException;
import org.nuclearfog.twidda.backend.engine.TwitterEngine; import org.nuclearfog.twidda.backend.engine.TwitterEngine;
import org.nuclearfog.twidda.backend.holder.UserListList; import org.nuclearfog.twidda.backend.lists.UserLists;
import org.nuclearfog.twidda.fragment.UserListFragment; import org.nuclearfog.twidda.fragment.UserListFragment;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -18,7 +18,7 @@ import java.lang.ref.WeakReference;
* @author nuclearfog * @author nuclearfog
* @see UserListFragment * @see UserListFragment
*/ */
public class ListLoader extends AsyncTask<Long, Void, UserListList> { public class ListLoader extends AsyncTask<Long, Void, UserLists> {
public static final long NO_CURSOR = -1; public static final long NO_CURSOR = -1;
@ -62,7 +62,7 @@ public class ListLoader extends AsyncTask<Long, Void, UserListList> {
@Override @Override
protected UserListList doInBackground(Long[] param) { protected UserLists doInBackground(Long[] param) {
try { try {
switch (listType) { switch (listType) {
case LOAD_USERLISTS: case LOAD_USERLISTS:
@ -79,7 +79,7 @@ public class ListLoader extends AsyncTask<Long, Void, UserListList> {
@Override @Override
protected void onPostExecute(UserListList result) { protected void onPostExecute(UserLists result) {
if (callback.get() != null) { if (callback.get() != null) {
if (result != null) { if (result != null) {
callback.get().setData(result); callback.get().setData(result);

View File

@ -6,7 +6,7 @@ import org.nuclearfog.twidda.activity.ListEditor;
import org.nuclearfog.twidda.backend.engine.EngineException; import org.nuclearfog.twidda.backend.engine.EngineException;
import org.nuclearfog.twidda.backend.engine.TwitterEngine; import org.nuclearfog.twidda.backend.engine.TwitterEngine;
import org.nuclearfog.twidda.backend.holder.ListHolder; import org.nuclearfog.twidda.backend.holder.ListHolder;
import org.nuclearfog.twidda.backend.items.UserList; import org.nuclearfog.twidda.backend.items.TwitterList;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -16,7 +16,7 @@ import java.lang.ref.WeakReference;
* *
* @author nuclearfog * @author nuclearfog
*/ */
public class ListUpdater extends AsyncTask<ListHolder, Void, UserList> { public class ListUpdater extends AsyncTask<ListHolder, Void, TwitterList> {
private EngineException err; private EngineException err;
@ -32,7 +32,7 @@ public class ListUpdater extends AsyncTask<ListHolder, Void, UserList> {
@Override @Override
protected UserList doInBackground(ListHolder... listHolders) { protected TwitterList doInBackground(ListHolder... listHolders) {
try { try {
ListHolder mList = listHolders[0]; ListHolder mList = listHolders[0];
return mTwitter.updateUserList(mList); return mTwitter.updateUserList(mList);
@ -44,7 +44,7 @@ public class ListUpdater extends AsyncTask<ListHolder, Void, UserList> {
@Override @Override
protected void onPostExecute(UserList result) { protected void onPostExecute(TwitterList result) {
if (callback.get() != null) { if (callback.get() != null) {
if (result != null) { if (result != null) {
callback.get().onSuccess(result); callback.get().onSuccess(result);

View File

@ -6,12 +6,11 @@ import androidx.annotation.Nullable;
import org.nuclearfog.twidda.backend.engine.EngineException; import org.nuclearfog.twidda.backend.engine.EngineException;
import org.nuclearfog.twidda.backend.engine.TwitterEngine; import org.nuclearfog.twidda.backend.engine.TwitterEngine;
import org.nuclearfog.twidda.backend.items.Message; import org.nuclearfog.twidda.backend.lists.MessageList;
import org.nuclearfog.twidda.database.AppDatabase; import org.nuclearfog.twidda.database.AppDatabase;
import org.nuclearfog.twidda.fragment.MessageFragment; import org.nuclearfog.twidda.fragment.MessageFragment;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.List;
/** /**
@ -20,7 +19,7 @@ import java.util.List;
* @author nuclearfog * @author nuclearfog
* @see MessageFragment * @see MessageFragment
*/ */
public class MessageLoader extends AsyncTask<Long, Void, List<Message>> { public class MessageLoader extends AsyncTask<Long, Void, MessageList> {
/** /**
* action to perform * action to perform
@ -47,38 +46,40 @@ public class MessageLoader extends AsyncTask<Long, Void, List<Message>> {
private final AppDatabase db; private final AppDatabase db;
private final Action action; private final Action action;
private String cursor;
private long removeMsgId = -1; private long removeMsgId = -1;
/** /**
* @param callback Callback to update data * @param callback Callback to update data
* @param action what action should be performed * @param action what action should be performed
*/ */
public MessageLoader(MessageFragment callback, Action action) { public MessageLoader(MessageFragment callback, Action action, String cursor) {
super(); super();
this.callback = new WeakReference<>(callback); this.callback = new WeakReference<>(callback);
db = new AppDatabase(callback.getContext()); db = new AppDatabase(callback.getContext());
mTwitter = TwitterEngine.getInstance(callback.getContext()); mTwitter = TwitterEngine.getInstance(callback.getContext());
this.action = action; this.action = action;
this.cursor = cursor;
} }
@Override @Override
protected List<Message> doInBackground(Long[] param) { protected MessageList doInBackground(Long[] param) {
long messageId = 0; long messageId = 0;
try { try {
switch (action) { switch (action) {
case DB: case DB:
List<Message> messages = db.getMessages(); // TODO store cursor in the preferences
MessageList messages = db.getMessages();
if (messages.isEmpty()) { if (messages.isEmpty()) {
messages = mTwitter.getMessages(); messages = mTwitter.getMessages(null);
db.storeMessage(messages); db.storeMessage(messages);
} }
return messages; return messages;
case LOAD: case LOAD:
messages = mTwitter.getMessages(); messages = mTwitter.getMessages(cursor);
db.storeMessage(messages); db.storeMessage(messages);
messages = db.getMessages();
return messages; return messages;
case DEL: case DEL:
@ -100,7 +101,7 @@ public class MessageLoader extends AsyncTask<Long, Void, List<Message>> {
@Override @Override
protected void onPostExecute(@Nullable List<Message> messages) { protected void onPostExecute(@Nullable MessageList messages) {
if (callback.get() != null) { if (callback.get() != null) {
if (messages != null) { if (messages != null) {
callback.get().setData(messages); callback.get().setData(messages);

View File

@ -6,7 +6,7 @@ import androidx.annotation.Nullable;
import org.nuclearfog.twidda.backend.engine.EngineException; import org.nuclearfog.twidda.backend.engine.EngineException;
import org.nuclearfog.twidda.backend.engine.TwitterEngine; import org.nuclearfog.twidda.backend.engine.TwitterEngine;
import org.nuclearfog.twidda.backend.holder.TwitterUserList; import org.nuclearfog.twidda.backend.lists.UserList;
import org.nuclearfog.twidda.fragment.UserFragment; import org.nuclearfog.twidda.fragment.UserFragment;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -17,7 +17,7 @@ import java.lang.ref.WeakReference;
* @author nuclearfog * @author nuclearfog
* @see UserFragment * @see UserFragment
*/ */
public class UserLoader extends AsyncTask<Long, Void, TwitterUserList> { public class UserLoader extends AsyncTask<Long, Void, UserList> {
public static final long NO_CURSOR = -1; public static final long NO_CURSOR = -1;
@ -77,7 +77,7 @@ public class UserLoader extends AsyncTask<Long, Void, TwitterUserList> {
@Override @Override
protected TwitterUserList doInBackground(Long[] param) { protected UserList doInBackground(Long[] param) {
try { try {
long cursor = param[0]; long cursor = param[0];
switch (type) { switch (type) {
@ -112,7 +112,7 @@ public class UserLoader extends AsyncTask<Long, Void, TwitterUserList> {
@Override @Override
protected void onPostExecute(TwitterUserList users) { protected void onPostExecute(UserList users) {
if (callback.get() != null) { if (callback.get() != null) {
if (users != null) { if (users != null) {
callback.get().setData(users); callback.get().setData(users);

View File

@ -10,15 +10,16 @@ import androidx.annotation.Nullable;
import org.nuclearfog.twidda.backend.holder.ListHolder; import org.nuclearfog.twidda.backend.holder.ListHolder;
import org.nuclearfog.twidda.backend.holder.MessageHolder; import org.nuclearfog.twidda.backend.holder.MessageHolder;
import org.nuclearfog.twidda.backend.holder.TweetHolder; import org.nuclearfog.twidda.backend.holder.TweetHolder;
import org.nuclearfog.twidda.backend.holder.TwitterUserList;
import org.nuclearfog.twidda.backend.holder.UserListList;
import org.nuclearfog.twidda.backend.items.Message; import org.nuclearfog.twidda.backend.items.Message;
import org.nuclearfog.twidda.backend.items.Relation; import org.nuclearfog.twidda.backend.items.Relation;
import org.nuclearfog.twidda.backend.items.Trend; import org.nuclearfog.twidda.backend.items.Trend;
import org.nuclearfog.twidda.backend.items.TrendLocation; import org.nuclearfog.twidda.backend.items.TrendLocation;
import org.nuclearfog.twidda.backend.items.Tweet; import org.nuclearfog.twidda.backend.items.Tweet;
import org.nuclearfog.twidda.backend.items.TwitterList;
import org.nuclearfog.twidda.backend.items.User; import org.nuclearfog.twidda.backend.items.User;
import org.nuclearfog.twidda.backend.items.UserList; import org.nuclearfog.twidda.backend.lists.MessageList;
import org.nuclearfog.twidda.backend.lists.UserList;
import org.nuclearfog.twidda.backend.lists.UserLists;
import org.nuclearfog.twidda.database.GlobalSettings; import org.nuclearfog.twidda.database.GlobalSettings;
import java.io.File; import java.io.File;
@ -36,6 +37,7 @@ import javax.net.ssl.SSLParameters;
import io.michaelrocks.paranoid.Obfuscate; import io.michaelrocks.paranoid.Obfuscate;
import twitter4j.DirectMessage; import twitter4j.DirectMessage;
import twitter4j.DirectMessageList;
import twitter4j.GeoLocation; import twitter4j.GeoLocation;
import twitter4j.IDs; import twitter4j.IDs;
import twitter4j.Location; import twitter4j.Location;
@ -320,7 +322,7 @@ public class TwitterEngine {
* @return List of Users * @return List of Users
* @throws EngineException if access is unavailable * @throws EngineException if access is unavailable
*/ */
public TwitterUserList searchUsers(String search, long cursor) throws EngineException { public UserList searchUsers(String search, long cursor) throws EngineException {
try { try {
int currentPage = 1; int currentPage = 1;
if (cursor > 0) if (cursor > 0)
@ -330,7 +332,7 @@ public class TwitterEngine {
List<User> users = convertUserList(twitter.searchUsers(search, currentPage)); List<User> users = convertUserList(twitter.searchUsers(search, currentPage));
if (users.size() < 20) if (users.size() < 20)
nextPage = 0; nextPage = 0;
TwitterUserList result = new TwitterUserList(prevPage, nextPage); UserList result = new UserList(prevPage, nextPage);
result.addAll(users); result.addAll(users);
return result; return result;
} catch (Exception err) { } catch (Exception err) {
@ -585,14 +587,14 @@ public class TwitterEngine {
* @return List of Following User with cursors * @return List of Following User with cursors
* @throws EngineException if Access is unavailable * @throws EngineException if Access is unavailable
*/ */
public TwitterUserList getFollowing(long userId, long cursor) throws EngineException { public UserList getFollowing(long userId, long cursor) throws EngineException {
try { try {
int load = settings.getListSize(); int load = settings.getListSize();
IDs userIDs = twitter.getFriendsIDs(userId, cursor, load); IDs userIDs = twitter.getFriendsIDs(userId, cursor, load);
long[] ids = userIDs.getIDs(); long[] ids = userIDs.getIDs();
long prevCursor = cursor > 0 ? cursor : 0; long prevCursor = cursor > 0 ? cursor : 0;
long nextCursor = userIDs.getNextCursor(); long nextCursor = userIDs.getNextCursor();
TwitterUserList result = new TwitterUserList(prevCursor, nextCursor); UserList result = new UserList(prevCursor, nextCursor);
if (ids.length > 0) { if (ids.length > 0) {
result.addAll(convertUserList(twitter.lookupUsers(ids))); result.addAll(convertUserList(twitter.lookupUsers(ids)));
} }
@ -610,14 +612,14 @@ public class TwitterEngine {
* @return List of Follower with cursors attached * @return List of Follower with cursors attached
* @throws EngineException if Access is unavailable * @throws EngineException if Access is unavailable
*/ */
public TwitterUserList getFollower(long userId, long cursor) throws EngineException { public UserList getFollower(long userId, long cursor) throws EngineException {
try { try {
int load = settings.getListSize(); int load = settings.getListSize();
IDs userIDs = twitter.getFollowersIDs(userId, cursor, load); IDs userIDs = twitter.getFollowersIDs(userId, cursor, load);
long[] ids = userIDs.getIDs(); long[] ids = userIDs.getIDs();
long prevCursor = cursor > 0 ? cursor : 0; long prevCursor = cursor > 0 ? cursor : 0;
long nextCursor = userIDs.getNextCursor(); long nextCursor = userIDs.getNextCursor();
TwitterUserList result = new TwitterUserList(prevCursor, nextCursor); UserList result = new UserList(prevCursor, nextCursor);
if (ids.length > 0) { if (ids.length > 0) {
result.addAll(convertUserList(twitter.lookupUsers(ids))); result.addAll(convertUserList(twitter.lookupUsers(ids)));
} }
@ -806,14 +808,14 @@ public class TwitterEngine {
* @return List of users or empty list if no match * @return List of users or empty list if no match
* @throws EngineException if Access is unavailable * @throws EngineException if Access is unavailable
*/ */
public TwitterUserList getRetweeter(long tweetID, long cursor) throws EngineException { public UserList getRetweeter(long tweetID, long cursor) throws EngineException {
try { try {
int load = settings.getListSize(); int load = settings.getListSize();
IDs userIDs = twitter.getRetweeterIds(tweetID, load, cursor); IDs userIDs = twitter.getRetweeterIds(tweetID, load, cursor);
long[] ids = userIDs.getIDs(); long[] ids = userIDs.getIDs();
long prevCursor = cursor > 0 ? cursor : 0; long prevCursor = cursor > 0 ? cursor : 0;
long nextCursor = userIDs.getNextCursor(); // fixme next cursor always zero long nextCursor = userIDs.getNextCursor(); // fixme next cursor always zero
TwitterUserList result = new TwitterUserList(prevCursor, nextCursor); UserList result = new UserList(prevCursor, nextCursor);
if (ids.length > 0) { if (ids.length > 0) {
result.addAll(convertUserList(twitter.lookupUsers(ids))); result.addAll(convertUserList(twitter.lookupUsers(ids)));
} }
@ -827,14 +829,19 @@ public class TwitterEngine {
/** /**
* get list of Direct Messages * get list of Direct Messages
* *
* @return DM List * @param cursor list cursor
* @return list of messages
* @throws EngineException if access is unavailable * @throws EngineException if access is unavailable
*/ */
public List<Message> getMessages() throws EngineException { public MessageList getMessages(@Nullable String cursor) throws EngineException {
try { try {
DirectMessageList dmList;
int load = settings.getListSize(); int load = settings.getListSize();
List<DirectMessage> dmList = twitter.getDirectMessages(load); if (cursor != null)
List<Message> result = new LinkedList<>(); dmList = twitter.getDirectMessages(load, cursor);
else
dmList = twitter.getDirectMessages(load);
MessageList result = new MessageList(cursor, dmList.getNextCursor());
for (DirectMessage dm : dmList) { for (DirectMessage dm : dmList) {
try { try {
result.add(getMessage(dm)); result.add(getMessage(dm));
@ -918,7 +925,7 @@ public class TwitterEngine {
* @return list information * @return list information
* @throws EngineException if access is unavailable * @throws EngineException if access is unavailable
*/ */
public UserListList getUserList(long userId, String username, long cursor) throws EngineException { public UserLists getUserList(long userId, String username, long cursor) throws EngineException {
try { try {
List<twitter4j.UserList> lists; List<twitter4j.UserList> lists;
if (userId > 0) if (userId > 0)
@ -927,9 +934,9 @@ public class TwitterEngine {
lists = twitter.getUserLists(username); lists = twitter.getUserLists(username);
long prevCursor = cursor > 0 ? cursor : 0; long prevCursor = cursor > 0 ? cursor : 0;
long nextCursor = 0; long nextCursor = 0;
UserListList result = new UserListList(prevCursor, nextCursor); // todo add paging system UserLists result = new UserLists(prevCursor, nextCursor); // todo add paging system
for (twitter4j.UserList list : lists) for (twitter4j.UserList list : lists)
result.add(new UserList(list, twitter.getId())); result.add(new TwitterList(list, twitter.getId()));
return result; return result;
} catch (Exception err) { } catch (Exception err) {
throw new EngineException(err); throw new EngineException(err);
@ -945,7 +952,7 @@ public class TwitterEngine {
* @return a list of user lists * @return a list of user lists
* @throws EngineException if access is unavailable * @throws EngineException if access is unavailable
*/ */
public UserListList getUserListMemberships(long userId, String username, long cursor) throws EngineException { public UserLists getUserListMemberships(long userId, String username, long cursor) throws EngineException {
try { try {
int count = settings.getListSize(); int count = settings.getListSize();
PagableResponseList<twitter4j.UserList> lists; PagableResponseList<twitter4j.UserList> lists;
@ -955,9 +962,9 @@ public class TwitterEngine {
lists = twitter.getUserListMemberships(username, count, cursor); lists = twitter.getUserListMemberships(username, count, cursor);
long prevCursor = cursor > 0 ? cursor : 0; long prevCursor = cursor > 0 ? cursor : 0;
long nextCursor = lists.getNextCursor(); long nextCursor = lists.getNextCursor();
UserListList result = new UserListList(prevCursor, nextCursor); UserLists result = new UserLists(prevCursor, nextCursor);
for (twitter4j.UserList list : lists) for (twitter4j.UserList list : lists)
result.add(new UserList(list, twitter.getId())); result.add(new TwitterList(list, twitter.getId()));
return result; return result;
} catch (Exception err) { } catch (Exception err) {
throw new EngineException(err); throw new EngineException(err);
@ -971,9 +978,9 @@ public class TwitterEngine {
* @return list information * @return list information
* @throws EngineException if access is unavailable * @throws EngineException if access is unavailable
*/ */
public UserList loadUserList(long listId) throws EngineException { public TwitterList loadUserList(long listId) throws EngineException {
try { try {
return new UserList(twitter.showUserList(listId), twitter.getId()); return new TwitterList(twitter.showUserList(listId), twitter.getId());
} catch (Exception err) { } catch (Exception err) {
throw new EngineException(err); throw new EngineException(err);
} }
@ -987,7 +994,7 @@ public class TwitterEngine {
* @return List information * @return List information
* @throws EngineException if access is unavailable * @throws EngineException if access is unavailable
*/ */
public UserList followUserList(long listId, boolean follow) throws EngineException { public TwitterList followUserList(long listId, boolean follow) throws EngineException {
try { try {
twitter4j.UserList list; twitter4j.UserList list;
if (follow) { if (follow) {
@ -995,7 +1002,7 @@ public class TwitterEngine {
} else { } else {
list = twitter.destroyUserListSubscription(listId); list = twitter.destroyUserListSubscription(listId);
} }
return new UserList(list, twitter.getId(), follow); return new TwitterList(list, twitter.getId(), follow);
} catch (Exception err) { } catch (Exception err) {
throw new EngineException(err); throw new EngineException(err);
} }
@ -1008,9 +1015,9 @@ public class TwitterEngine {
* @return List information * @return List information
* @throws EngineException if access is unavailable * @throws EngineException if access is unavailable
*/ */
public UserList deleteUserList(long listId) throws EngineException { public TwitterList deleteUserList(long listId) throws EngineException {
try { try {
return new UserList(twitter.destroyUserList(listId), twitter.getId()); return new TwitterList(twitter.destroyUserList(listId), twitter.getId());
} catch (Exception err) { } catch (Exception err) {
throw new EngineException(err); throw new EngineException(err);
} }
@ -1023,12 +1030,12 @@ public class TwitterEngine {
* @return list of users following the list * @return list of users following the list
* @throws EngineException if access is unavailable * @throws EngineException if access is unavailable
*/ */
public TwitterUserList getListFollower(long listId, long cursor) throws EngineException { public UserList getListFollower(long listId, long cursor) throws EngineException {
try { try {
PagableResponseList<twitter4j.User> followerList = twitter.getUserListSubscribers(listId, cursor); PagableResponseList<twitter4j.User> followerList = twitter.getUserListSubscribers(listId, cursor);
long prevCursor = cursor > 0 ? cursor : 0; long prevCursor = cursor > 0 ? cursor : 0;
long nextCursor = followerList.getNextCursor(); long nextCursor = followerList.getNextCursor();
TwitterUserList result = new TwitterUserList(prevCursor, nextCursor); UserList result = new UserList(prevCursor, nextCursor);
result.addAll(convertUserList(followerList)); result.addAll(convertUserList(followerList));
return result; return result;
} catch (Exception err) { } catch (Exception err) {
@ -1043,12 +1050,12 @@ public class TwitterEngine {
* @return list of users * @return list of users
* @throws EngineException if access is unavailable * @throws EngineException if access is unavailable
*/ */
public TwitterUserList getListMember(long listId, long cursor) throws EngineException { public UserList getListMember(long listId, long cursor) throws EngineException {
try { try {
PagableResponseList<twitter4j.User> users = twitter.getUserListMembers(listId, cursor); PagableResponseList<twitter4j.User> users = twitter.getUserListMembers(listId, cursor);
long prevCursor = cursor > 0 ? cursor : 0; long prevCursor = cursor > 0 ? cursor : 0;
long nextCursor = users.getNextCursor(); long nextCursor = users.getNextCursor();
TwitterUserList result = new TwitterUserList(prevCursor, nextCursor); UserList result = new UserList(prevCursor, nextCursor);
result.addAll(convertUserList(users)); result.addAll(convertUserList(users));
return result; return result;
} catch (Exception err) { } catch (Exception err) {
@ -1105,7 +1112,7 @@ public class TwitterEngine {
* @param list holder for list information * @param list holder for list information
* @throws EngineException if access is unavailable * @throws EngineException if access is unavailable
*/ */
public UserList updateUserList(ListHolder list) throws EngineException { public TwitterList updateUserList(ListHolder list) throws EngineException {
try { try {
twitter4j.UserList result; twitter4j.UserList result;
if (list.exists()) { if (list.exists()) {
@ -1113,7 +1120,7 @@ public class TwitterEngine {
} else { } else {
result = twitter.createUserList(list.getTitle(), list.isPublic(), list.getDescription()); result = twitter.createUserList(list.getTitle(), list.isPublic(), list.getDescription());
} }
return new UserList(result, twitter.getId()); return new TwitterList(result, twitter.getId());
} catch (Exception err) { } catch (Exception err) {
throw new EngineException(err); throw new EngineException(err);
} }

View File

@ -9,7 +9,7 @@ import java.io.Serializable;
* *
* @author nuclearfog * @author nuclearfog
*/ */
public class UserList implements Serializable { public class TwitterList implements Serializable {
private long id; private long id;
private long createdAt; private long createdAt;
@ -28,7 +28,7 @@ public class UserList implements Serializable {
* @param homeId ID of the authenticated user * @param homeId ID of the authenticated user
* @param isFollowing authenticated user is following list * @param isFollowing authenticated user is following list
*/ */
public UserList(twitter4j.UserList list, long homeId, boolean isFollowing) { public TwitterList(twitter4j.UserList list, long homeId, boolean isFollowing) {
id = list.getId(); id = list.getId();
createdAt = list.getCreatedAt().getTime(); createdAt = list.getCreatedAt().getTime();
owner = new User(list.getUser(), homeId); owner = new User(list.getUser(), homeId);
@ -42,7 +42,7 @@ public class UserList implements Serializable {
this.isFollowing = isFollowing; this.isFollowing = isFollowing;
} }
public UserList(twitter4j.UserList list, long homeId) { public TwitterList(twitter4j.UserList list, long homeId) {
this(list, homeId, list.isFollowing()); this(list, homeId, list.isFollowing());
} }
@ -146,8 +146,8 @@ public class UserList implements Serializable {
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (o instanceof UserList) if (o instanceof TwitterList)
return ((UserList) o).id == id; return ((TwitterList) o).id == id;
return false; return false;
} }
} }

View File

@ -0,0 +1,108 @@
package org.nuclearfog.twidda.backend.lists;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.nuclearfog.twidda.backend.items.Message;
import java.util.LinkedList;
/**
* Message list container class containing cursor information
*
* @author nuclearfog
*/
public class MessageList extends LinkedList<Message> {
private String prevCursor, nextCursor;
/**
* @param prevCursor cursor to a previous list
* @param nextCursor cursor to a next list
*/
public MessageList(String prevCursor, String nextCursor) {
super();
this.prevCursor = prevCursor;
this.nextCursor = nextCursor;
}
@Override
@Nullable
public Message get(int index) {
return super.get(index);
}
/**
* replace old list with a new one
*
* @param list new list
*/
public void replaceAll(MessageList list) {
super.clear();
super.addAll(list);
prevCursor = list.prevCursor;
nextCursor = list.nextCursor;
}
/**
* add a new list to the bottom of this list
*
* @param list new list
* @param index Index of the sub list
*/
public void addAt(MessageList list, int index) {
super.addAll(index, list);
nextCursor = list.nextCursor;
}
/**
* remove message item matching with a given ID
*
* @param id message ID
* @return index of the removed message or -1 if not found
*/
public int removeItem(long id) {
for (int index = 0; index < size(); index++) {
Message item = get(index);
if (item != null && item.getId() == id) {
remove(index);
return index;
}
}
return -1;
}
/**
* get next cursor string
*
* @return cursor string
*/
public String getNextCursor() {
return nextCursor;
}
/**
* check if this list has a previous cursor
*
* @return true if list has a previous cursor
*/
public boolean hasPrev() {
return prevCursor != null;
}
/**
* check if this list has a cursor to a next list
*
* @return true if list has a next cursor
*/
public boolean hasNext() {
return nextCursor != null;
}
@Override
@NonNull
public String toString() {
return "size=" + size() + " pre=" + prevCursor + " pos=" + nextCursor;
}
}

View File

@ -1,6 +1,7 @@
package org.nuclearfog.twidda.backend.holder; package org.nuclearfog.twidda.backend.lists;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.nuclearfog.twidda.backend.items.User; import org.nuclearfog.twidda.backend.items.User;
@ -11,12 +12,12 @@ import java.util.LinkedList;
* *
* @author nuclearfog * @author nuclearfog
*/ */
public class TwitterUserList extends LinkedList<User> { public class UserList extends LinkedList<User> {
private long prevCursor = 0; private long prevCursor = 0;
private long nextCursor = 0; private long nextCursor = 0;
public TwitterUserList() { public UserList() {
super(); super();
} }
@ -26,12 +27,35 @@ public class TwitterUserList extends LinkedList<User> {
* @param prevCursor previous cursor of the list * @param prevCursor previous cursor of the list
* @param nextCursor next cursor of the list * @param nextCursor next cursor of the list
*/ */
public TwitterUserList(long prevCursor, long nextCursor) { public UserList(long prevCursor, long nextCursor) {
super(); super();
this.prevCursor = prevCursor; this.prevCursor = prevCursor;
this.nextCursor = nextCursor; this.nextCursor = nextCursor;
} }
@Nullable
@Override
public User get(int index) {
return super.get(index);
}
/**
* remove user item from list matching screen name
*
* @param name screen name of the user
* @return index of the user item or -1 if not found
*/
public int removeItem(String name) {
for (int index = 0; index < size(); index++) {
User item = get(index);
if (item != null && item.getScreenname().equals(name)) {
remove(index);
return index;
}
}
return -1;
}
/** /**
* check if list is linked to a previous list * check if list is linked to a previous list
* *
@ -59,42 +83,27 @@ public class TwitterUserList extends LinkedList<User> {
return nextCursor; return nextCursor;
} }
/**
* get previous cursor
*
* @return previous cursor
*/
public long getPrev() {
return prevCursor;
}
/** /**
* replace whole list including cursors * replace whole list including cursors
* *
* @param list new list * @param list new list
*/ */
public void replace(TwitterUserList list) { public void replace(UserList list) {
super.clear(); super.clear();
super.addAll(list); super.addAll(list);
prevCursor = list.getPrev(); prevCursor = list.prevCursor;
nextCursor = list.getNext(); nextCursor = list.nextCursor;
} }
/** /**
* add a sublist at the bottom of this list including next cursor * add a sublist at the bottom of this list including next cursor
* *
* @param list new sublist * @param list new sublist
* @param index index of the sub list
*/ */
public void addListAt(TwitterUserList list, int index) { public void addAt(UserList list, int index) {
super.addAll(index, list); super.addAll(index, list);
nextCursor = list.getNext(); nextCursor = list.nextCursor;
}
@Override
public void clear() {
prevCursor = 0;
nextCursor = 0;
super.clear();
} }
@Override @Override

View File

@ -1,8 +1,9 @@
package org.nuclearfog.twidda.backend.holder; package org.nuclearfog.twidda.backend.lists;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.nuclearfog.twidda.backend.items.UserList; import org.nuclearfog.twidda.backend.items.TwitterList;
import java.util.LinkedList; import java.util.LinkedList;
@ -11,26 +12,34 @@ import java.util.LinkedList;
* *
* @author nuclearfog * @author nuclearfog
*/ */
public class UserListList extends LinkedList<UserList> { public class UserLists extends LinkedList<TwitterList> {
private long prevCursor = 0; private long prevCursor, nextCursor;
private long nextCursor = 0;
public UserListList() { /**
super(); * create an empty list
*/
public UserLists() {
this(0, 0);
} }
/** /**
* @param prevCursor previous list cursor or 0 if list starts * @param prevCursor previous list cursor or 0 if list starts
* @param nextCursor next cursor or 0 if list ends * @param nextCursor next cursor or 0 if list ends
*/ */
public UserListList(long prevCursor, long nextCursor) { public UserLists(long prevCursor, long nextCursor) {
super(); super();
this.prevCursor = prevCursor; this.prevCursor = prevCursor;
this.nextCursor = nextCursor; this.nextCursor = nextCursor;
} }
@Nullable
@Override
public TwitterList get(int index) {
return super.get(index);
}
/** /**
* check if list is linked to a previous list * check if list is linked to a previous list
* *
@ -49,15 +58,6 @@ public class UserListList extends LinkedList<UserList> {
return nextCursor != 0; return nextCursor != 0;
} }
/**
* get prev link to a list
*
* @return cursor
*/
public long getPrev() {
return prevCursor;
}
/** /**
* get next link to a list * get next link to a list
* *
@ -72,30 +72,39 @@ public class UserListList extends LinkedList<UserList> {
* *
* @param list new list * @param list new list
*/ */
public void replace(UserListList list) { public void replace(UserLists list) {
super.clear(); super.clear();
super.addAll(list); super.addAll(list);
prevCursor = list.getPrev(); prevCursor = list.prevCursor;
nextCursor = list.getNext(); nextCursor = list.nextCursor;
}
/**
* remove an item from list
*
* @param id ID of the item
* @return index of the removed item
*/
public int removeItem(long id) {
for (int index = 0; index < size(); index++) {
TwitterList item = get(index);
if (item != null && item.getId() == id) {
remove(index);
return index;
}
}
return -1;
} }
/** /**
* add a sublist at the bottom of this list including next cursor * add a sublist at the bottom of this list including next cursor
* *
* @param list new sublist * @param list new sublist
* @param index position where to insert at * @param index Index of the sub list
*/ */
public void addListAt(UserListList list, int index) { public void addAt(UserLists list, int index) {
super.addAll(index, list); super.addAll(index, list);
nextCursor = list.getNext(); nextCursor = list.nextCursor;
}
@Override
public void clear() {
// resetting cursors
prevCursor = 0;
nextCursor = 0;
super.clear();
} }
@Override @Override

View File

@ -11,6 +11,7 @@ import org.nuclearfog.twidda.backend.items.Message;
import org.nuclearfog.twidda.backend.items.Trend; import org.nuclearfog.twidda.backend.items.Trend;
import org.nuclearfog.twidda.backend.items.Tweet; import org.nuclearfog.twidda.backend.items.Tweet;
import org.nuclearfog.twidda.backend.items.User; import org.nuclearfog.twidda.backend.items.User;
import org.nuclearfog.twidda.backend.lists.MessageList;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -427,10 +428,10 @@ public class AppDatabase {
* *
* @return list of direct messages * @return list of direct messages
*/ */
public List<Message> getMessages() { public MessageList getMessages() {
String[] args = {Integer.toString(limit)}; String[] args = {Integer.toString(limit)};
// TODO get next cursor from database
List<Message> result = new LinkedList<>(); MessageList result = new MessageList(null, null);
SQLiteDatabase db = getDbRead(); SQLiteDatabase db = getDbRead();
Cursor cursor = db.rawQuery(MESSAGE_QUERY, args); Cursor cursor = db.rawQuery(MESSAGE_QUERY, args);
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {

View File

@ -18,12 +18,11 @@ import org.nuclearfog.twidda.adapter.MessageAdapter.OnItemSelected;
import org.nuclearfog.twidda.backend.MessageLoader; import org.nuclearfog.twidda.backend.MessageLoader;
import org.nuclearfog.twidda.backend.engine.EngineException; import org.nuclearfog.twidda.backend.engine.EngineException;
import org.nuclearfog.twidda.backend.items.Message; import org.nuclearfog.twidda.backend.items.Message;
import org.nuclearfog.twidda.backend.lists.MessageList;
import org.nuclearfog.twidda.backend.utils.DialogBuilder; import org.nuclearfog.twidda.backend.utils.DialogBuilder;
import org.nuclearfog.twidda.backend.utils.DialogBuilder.OnDialogClick; import org.nuclearfog.twidda.backend.utils.DialogBuilder.OnDialogClick;
import org.nuclearfog.twidda.backend.utils.ErrorHandler; import org.nuclearfog.twidda.backend.utils.ErrorHandler;
import java.util.List;
import static android.os.AsyncTask.Status.RUNNING; import static android.os.AsyncTask.Status.RUNNING;
import static android.widget.Toast.LENGTH_SHORT; import static android.widget.Toast.LENGTH_SHORT;
import static org.nuclearfog.twidda.activity.MessageEditor.KEY_DM_PREFIX; import static org.nuclearfog.twidda.activity.MessageEditor.KEY_DM_PREFIX;
@ -59,7 +58,7 @@ public class MessageFragment extends ListFragment implements OnItemSelected, OnD
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
if (messageTask == null) { if (messageTask == null) {
load(MessageLoader.Action.DB); load(MessageLoader.Action.DB, null);
setRefresh(true); setRefresh(true);
} }
} }
@ -67,7 +66,7 @@ public class MessageFragment extends ListFragment implements OnItemSelected, OnD
@Override @Override
protected void onReset() { protected void onReset() {
load(MessageLoader.Action.DB); load(MessageLoader.Action.DB, null);
setRefresh(true); setRefresh(true);
} }
@ -83,7 +82,7 @@ public class MessageFragment extends ListFragment implements OnItemSelected, OnD
@Override @Override
protected void onReload() { protected void onReload() {
if (messageTask != null && messageTask.getStatus() != RUNNING) { if (messageTask != null && messageTask.getStatus() != RUNNING) {
load(MessageLoader.Action.LOAD); load(MessageLoader.Action.LOAD, null);
} }
} }
@ -151,9 +150,19 @@ public class MessageFragment extends ListFragment implements OnItemSelected, OnD
} }
@Override
public boolean onFooterClick(String cursor) {
if (messageTask != null && messageTask.getStatus() != RUNNING) {
load(MessageLoader.Action.LOAD, cursor);
return true;
}
return false;
}
@Override @Override
public void onConfirm(DialogBuilder.DialogType type) { public void onConfirm(DialogBuilder.DialogType type) {
messageTask = new MessageLoader(this, MessageLoader.Action.DEL); messageTask = new MessageLoader(this, MessageLoader.Action.DEL, null);
messageTask.execute(deleteId); messageTask.execute(deleteId);
} }
@ -162,8 +171,8 @@ public class MessageFragment extends ListFragment implements OnItemSelected, OnD
* *
* @param data list of direct messages * @param data list of direct messages
*/ */
public void setData(List<Message> data) { public void setData(MessageList data) {
adapter.replaceAll(data); adapter.setData(data);
setRefresh(false); setRefresh(false);
} }
@ -198,8 +207,8 @@ public class MessageFragment extends ListFragment implements OnItemSelected, OnD
* *
* @param action mode for loading or removing messages * @param action mode for loading or removing messages
*/ */
private void load(MessageLoader.Action action) { private void load(MessageLoader.Action action, String cursor) {
messageTask = new MessageLoader(this, action); messageTask = new MessageLoader(this, action, cursor);
messageTask.execute(); messageTask.execute();
} }
} }

View File

@ -201,10 +201,12 @@ public class TweetFragment extends ListFragment implements TweetClickListener {
@Override @Override
public void onHolderClick(long sinceId, long maxId, int pos) { public boolean onHolderClick(long sinceId, long maxId, int pos) {
if (tweetTask != null && tweetTask.getStatus() != RUNNING) { if (tweetTask != null && tweetTask.getStatus() != RUNNING) {
load(sinceId, maxId, pos); load(sinceId, maxId, pos);
return true;
} }
return false;
} }
/** /**

View File

@ -16,8 +16,8 @@ import org.nuclearfog.twidda.backend.ListManager.ListManagerCallback;
import org.nuclearfog.twidda.backend.UserLoader; import org.nuclearfog.twidda.backend.UserLoader;
import org.nuclearfog.twidda.backend.UserLoader.Type; import org.nuclearfog.twidda.backend.UserLoader.Type;
import org.nuclearfog.twidda.backend.engine.EngineException; import org.nuclearfog.twidda.backend.engine.EngineException;
import org.nuclearfog.twidda.backend.holder.TwitterUserList;
import org.nuclearfog.twidda.backend.items.User; import org.nuclearfog.twidda.backend.items.User;
import org.nuclearfog.twidda.backend.lists.UserList;
import org.nuclearfog.twidda.backend.utils.DialogBuilder; import org.nuclearfog.twidda.backend.utils.DialogBuilder;
import org.nuclearfog.twidda.backend.utils.DialogBuilder.OnDialogClick; import org.nuclearfog.twidda.backend.utils.DialogBuilder.OnDialogClick;
import org.nuclearfog.twidda.backend.utils.ErrorHandler; import org.nuclearfog.twidda.backend.utils.ErrorHandler;
@ -197,10 +197,12 @@ public class UserFragment extends ListFragment implements UserClickListener,
@Override @Override
public void onFooterClick(long cursor) { public boolean onFooterClick(long cursor) {
if (userTask != null && userTask.getStatus() != RUNNING) { if (userTask != null && userTask.getStatus() != RUNNING) {
load(cursor); load(cursor);
return true;
} }
return false;
} }
@ -241,7 +243,7 @@ public class UserFragment extends ListFragment implements UserClickListener,
* *
* @param data list of twitter users * @param data list of twitter users
*/ */
public void setData(TwitterUserList data) { public void setData(UserList data) {
adapter.setData(data); adapter.setData(data);
setRefresh(false); setRefresh(false);
} }

View File

@ -11,9 +11,9 @@ import org.nuclearfog.twidda.adapter.ListAdapter;
import org.nuclearfog.twidda.adapter.ListAdapter.ListClickListener; import org.nuclearfog.twidda.adapter.ListAdapter.ListClickListener;
import org.nuclearfog.twidda.backend.ListLoader; import org.nuclearfog.twidda.backend.ListLoader;
import org.nuclearfog.twidda.backend.engine.EngineException; import org.nuclearfog.twidda.backend.engine.EngineException;
import org.nuclearfog.twidda.backend.holder.UserListList; import org.nuclearfog.twidda.backend.items.TwitterList;
import org.nuclearfog.twidda.backend.items.User; import org.nuclearfog.twidda.backend.items.User;
import org.nuclearfog.twidda.backend.items.UserList; import org.nuclearfog.twidda.backend.lists.UserLists;
import org.nuclearfog.twidda.backend.utils.ErrorHandler; import org.nuclearfog.twidda.backend.utils.ErrorHandler;
import static android.os.AsyncTask.Status.RUNNING; import static android.os.AsyncTask.Status.RUNNING;
@ -135,8 +135,8 @@ public class UserListFragment extends ListFragment implements ListClickListener
adapter.removeItem(removedListId); adapter.removeItem(removedListId);
} else if (resultCode == RETURN_LIST_UPDATED) { } else if (resultCode == RETURN_LIST_UPDATED) {
Object result = data.getSerializableExtra(RESULT_UPDATE_LIST); Object result = data.getSerializableExtra(RESULT_UPDATE_LIST);
if (result instanceof UserList) { if (result instanceof TwitterList) {
UserList update = (UserList) result; TwitterList update = (TwitterList) result;
adapter.updateItem(update); adapter.updateItem(update);
} }
} }
@ -153,7 +153,7 @@ public class UserListFragment extends ListFragment implements ListClickListener
@Override @Override
public void onListClick(UserList listItem) { public void onListClick(TwitterList listItem) {
Intent listIntent = new Intent(requireContext(), ListDetail.class); Intent listIntent = new Intent(requireContext(), ListDetail.class);
listIntent.putExtra(KEY_LIST_DATA, listItem); listIntent.putExtra(KEY_LIST_DATA, listItem);
startActivityForResult(listIntent, REQUEST_OPEN_LIST); startActivityForResult(listIntent, REQUEST_OPEN_LIST);
@ -187,7 +187,7 @@ public class UserListFragment extends ListFragment implements ListClickListener
* *
* @param data List of Twitter list data * @param data List of Twitter list data
*/ */
public void setData(UserListList data) { public void setData(UserLists data) {
adapter.setData(data); adapter.setData(data);
setRefresh(false); setRefresh(false);
} }

View File

@ -9,7 +9,7 @@
<androidx.appcompat.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/listdetail_toolbar" android:id="@+id/listdetail_toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/listdetail_toolbar_height" /> android:layout_height="wrap_content" />
<com.google.android.material.tabs.TabLayout <com.google.android.material.tabs.TabLayout
android:id="@+id/listdetail_tab" android:id="@+id/listdetail_tab"

View File

@ -100,7 +100,7 @@
android:text="@string/userlist_public_sel" android:text="@string/userlist_public_sel"
app:layout_constraintBottom_toBottomOf="@+id/list_edit_public_sw" app:layout_constraintBottom_toBottomOf="@+id/list_edit_public_sw"
app:layout_constraintEnd_toStartOf="@+id/userlist_create_list" app:layout_constraintEnd_toStartOf="@+id/userlist_create_list"
app:layout_constraintHorizontal_weight="1" app:layout_constraintHorizontal_weight="2"
app:layout_constraintStart_toEndOf="@+id/list_edit_public_sw" app:layout_constraintStart_toEndOf="@+id/list_edit_public_sw"
app:layout_constraintTop_toTopOf="@+id/list_edit_public_sw" /> app:layout_constraintTop_toTopOf="@+id/list_edit_public_sw" />
@ -116,7 +116,7 @@
android:text="@string/userlist_create" android:text="@string/userlist_create"
app:layout_constraintBottom_toBottomOf="@+id/userlist_switch_text" app:layout_constraintBottom_toBottomOf="@+id/userlist_switch_text"
app:layout_constraintEnd_toEndOf="@id/userlist_popup_background" app:layout_constraintEnd_toEndOf="@id/userlist_popup_background"
app:layout_constraintHorizontal_weight="2" app:layout_constraintHorizontal_weight="3"
app:layout_constraintStart_toEndOf="@+id/userlist_switch_text" app:layout_constraintStart_toEndOf="@+id/userlist_switch_text"
app:layout_constraintTop_toTopOf="@+id/userlist_switch_text" /> app:layout_constraintTop_toTopOf="@+id/userlist_switch_text" />

View File

@ -137,7 +137,7 @@
<string name="userlist_public_sel">öffentlich</string> <string name="userlist_public_sel">öffentlich</string>
<string name="userlist_enter_title">Titel eingeben</string> <string name="userlist_enter_title">Titel eingeben</string>
<string name="userlist_enter_descr">Beschreibung der Liste eingeben</string> <string name="userlist_enter_descr">Beschreibung der Liste eingeben</string>
<string name="userlist_create">Liste erstellen</string> <string name="userlist_create">erstellen</string>
<string name="error_list_title_empty">Titel ist leer!</string> <string name="error_list_title_empty">Titel ist leer!</string>
<string name="info_list_updated">Liste wurde aktualisiert</string> <string name="info_list_updated">Liste wurde aktualisiert</string>
<string name="info_list_created">Liste wurde erstellt</string> <string name="info_list_created">Liste wurde erstellt</string>
@ -145,7 +145,7 @@
<string name="info_adding_user_to_list">Füge Nutzer zur Liste hinzu</string> <string name="info_adding_user_to_list">Füge Nutzer zur Liste hinzu</string>
<string name="userlist_create_new_list">Neue Liste erstellen</string> <string name="userlist_create_new_list">Neue Liste erstellen</string>
<string name="menu_edit_list">Liste bearbeiten</string> <string name="menu_edit_list">Liste bearbeiten</string>
<string name="update_list">Liste aktualisieren</string> <string name="update_list">aktualisieren</string>
<string name="confirm_remove_user_from_list">Nutzer von der Liste entfernen?</string> <string name="confirm_remove_user_from_list">Nutzer von der Liste entfernen?</string>
<string name="info_user_removed">Nutzer von der Liste entfernt!</string> <string name="info_user_removed">Nutzer von der Liste entfernt!</string>
<string name="descr_remove_user">Nutzer von der Liste entfernen</string> <string name="descr_remove_user">Nutzer von der Liste entfernen</string>