Migrate AuthenticatedListFragments to RecyclerView
This commit is contained in:
@@ -3,10 +3,11 @@ package i2p.bote.android;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Build;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
@@ -19,169 +20,286 @@ import java.util.List;
|
||||
import javax.mail.Part;
|
||||
|
||||
import i2p.bote.android.util.BoteHelper;
|
||||
import i2p.bote.android.util.MultiSelectionUtil;
|
||||
import i2p.bote.email.Email;
|
||||
|
||||
public class EmailListAdapter extends ArrayAdapter<Email> {
|
||||
private final LayoutInflater mInflater;
|
||||
private EmailSelector mSelector;
|
||||
public class EmailListAdapter extends MultiSelectionUtil.SelectableAdapter<RecyclerView.ViewHolder> {
|
||||
private static final DateFormat DATE_BEFORE_THIS_YEAR = DateFormat.getDateInstance(DateFormat.MEDIUM);
|
||||
private static final DateFormat DATE_THIS_YEAR = new SimpleDateFormat(
|
||||
((SimpleDateFormat) SimpleDateFormat.getDateInstance(DateFormat.MEDIUM))
|
||||
.toPattern().replaceAll(",?\\W?[Yy]+\\W?", "")
|
||||
);
|
||||
private static final DateFormat DATE_TODAY = DateFormat.getTimeInstance();
|
||||
|
||||
private Calendar BOUNDARY_DAY;
|
||||
private Calendar BOUNDARY_YEAR;
|
||||
|
||||
private Context mCtx;
|
||||
private String mFolderName;
|
||||
private EmailListFragment.OnEmailSelectedListener mListener;
|
||||
private boolean mIsOutbox;
|
||||
private List<Email> mEmails;
|
||||
private int mIncompleteEmails;
|
||||
|
||||
public interface EmailSelector {
|
||||
public boolean inActionMode();
|
||||
public void select(View view);
|
||||
}
|
||||
|
||||
public EmailListAdapter(Context context, EmailSelector selector, boolean isOutbox) {
|
||||
super(context, android.R.layout.simple_list_item_2);
|
||||
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
mSelector = selector;
|
||||
mIsOutbox = isOutbox;
|
||||
}
|
||||
|
||||
public void setData(List<Email> emails) {
|
||||
clear();
|
||||
if (emails != null) {
|
||||
for (Email email : emails) {
|
||||
add(email);
|
||||
}
|
||||
public static class IncompleteEmailViewHolder extends RecyclerView.ViewHolder {
|
||||
public IncompleteEmailViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ViewHolder {
|
||||
ImageView picture;
|
||||
//View emailSelected;
|
||||
TextView subject;
|
||||
TextView address;
|
||||
TextView content;
|
||||
TextView sent;
|
||||
View emailAttachment;
|
||||
TextView emailStatus;
|
||||
View emailDelivered;
|
||||
public static class EmailViewHolder extends RecyclerView.ViewHolder {
|
||||
public ImageView picture;
|
||||
//public ImageView emailSelected;
|
||||
public TextView subject;
|
||||
public TextView address;
|
||||
public TextView content;
|
||||
public TextView sent;
|
||||
public ImageView emailAttachment;
|
||||
public TextView emailStatus;
|
||||
public ImageView emailDelivered;
|
||||
|
||||
public EmailViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
||||
picture = (ImageView) itemView.findViewById(R.id.contact_picture);
|
||||
//emailSelected = view.findViewById(R.id.email_selected);
|
||||
subject = (TextView) itemView.findViewById(R.id.email_subject);
|
||||
address = (TextView) itemView.findViewById(R.id.email_address);
|
||||
content = (TextView) itemView.findViewById(R.id.email_content);
|
||||
sent = (TextView) itemView.findViewById(R.id.email_sent);
|
||||
emailAttachment = (ImageView) itemView.findViewById(R.id.email_attachment);
|
||||
emailStatus = (TextView) itemView.findViewById(R.id.email_status);
|
||||
emailDelivered = (ImageView) itemView.findViewById(R.id.email_delivered);
|
||||
}
|
||||
}
|
||||
|
||||
public EmailListAdapter(Context context, String folderName,
|
||||
EmailListFragment.OnEmailSelectedListener listener) {
|
||||
super();
|
||||
mCtx = context;
|
||||
mFolderName = folderName;
|
||||
mListener = listener;
|
||||
mIsOutbox = BoteHelper.isOutbox(folderName);
|
||||
mIncompleteEmails = 0;
|
||||
setHasStableIds(true);
|
||||
|
||||
setDateBoundaries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the boundaries for date display formats.
|
||||
* <p/>
|
||||
* TODO: call this method at midnight to refresh the UI
|
||||
*/
|
||||
public void setDateBoundaries() {
|
||||
BOUNDARY_DAY = Calendar.getInstance();
|
||||
BOUNDARY_DAY.set(Calendar.HOUR, 0);
|
||||
BOUNDARY_DAY.set(Calendar.MINUTE, 0);
|
||||
BOUNDARY_DAY.set(Calendar.SECOND, 0);
|
||||
|
||||
BOUNDARY_YEAR = Calendar.getInstance();
|
||||
BOUNDARY_YEAR.set(Calendar.MONTH, Calendar.JANUARY);
|
||||
BOUNDARY_YEAR.set(Calendar.DAY_OF_MONTH, 1);
|
||||
BOUNDARY_YEAR.set(Calendar.HOUR, 0);
|
||||
BOUNDARY_YEAR.set(Calendar.MINUTE, 0);
|
||||
BOUNDARY_YEAR.set(Calendar.SECOND, 0);
|
||||
|
||||
if (mEmails != null)
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void setEmails(List<Email> emails) {
|
||||
mEmails = emails;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public Email getEmail(int position) {
|
||||
if (mIncompleteEmails > 0)
|
||||
position--;
|
||||
|
||||
if (position < 0)
|
||||
return null;
|
||||
|
||||
return mEmails.get(position);
|
||||
}
|
||||
|
||||
public void setIncompleteEmails(int incompleteEmails) {
|
||||
if (incompleteEmails > 0) {
|
||||
if (mIncompleteEmails == 0) {
|
||||
mIncompleteEmails = incompleteEmails;
|
||||
notifyItemInserted(0);
|
||||
} else {
|
||||
mIncompleteEmails = incompleteEmails;
|
||||
notifyItemChanged(0);
|
||||
}
|
||||
} else if (mIncompleteEmails > 0) {
|
||||
mIncompleteEmails = 0;
|
||||
notifyItemRemoved(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
ViewHolder holder;
|
||||
View view;
|
||||
public int getItemViewType(int position) {
|
||||
if (mIncompleteEmails > 0)
|
||||
position--;
|
||||
|
||||
if (convertView == null) {
|
||||
holder = new ViewHolder();
|
||||
view = mInflater.inflate(R.layout.listitem_email, parent, false);
|
||||
holder.picture = (ImageView) view.findViewById(R.id.contact_picture);
|
||||
//holder.emailSelected = view.findViewById(R.id.email_selected);
|
||||
holder.subject = (TextView) view.findViewById(R.id.email_subject);
|
||||
holder.address = (TextView) view.findViewById(R.id.email_address);
|
||||
holder.content = (TextView) view.findViewById(R.id.email_content);
|
||||
holder.sent = (TextView) view.findViewById(R.id.email_sent);
|
||||
holder.emailAttachment = view.findViewById(R.id.email_attachment);
|
||||
holder.emailStatus = (TextView) view.findViewById(R.id.email_status);
|
||||
holder.emailDelivered = view.findViewById(R.id.email_delivered);
|
||||
view.setTag(holder);
|
||||
} else {
|
||||
view = convertView;
|
||||
holder = (ViewHolder) view.getTag();
|
||||
return position < 0 ? R.layout.listitem_incomplete : R.layout.listitem_email;
|
||||
}
|
||||
|
||||
// Create new views (invoked by the layout manager)
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext())
|
||||
.inflate(viewType, parent, false);
|
||||
switch (viewType) {
|
||||
case R.layout.listitem_incomplete:
|
||||
return new IncompleteEmailViewHolder(v);
|
||||
case R.layout.listitem_email:
|
||||
default:
|
||||
return new EmailViewHolder(v);
|
||||
}
|
||||
}
|
||||
|
||||
final Email email = getItem(position);
|
||||
// Replace the contents of a view (invoked by the layout manager)
|
||||
@Override
|
||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
||||
switch (holder.getItemViewType()) {
|
||||
case R.layout.listitem_incomplete:
|
||||
((TextView) holder.itemView).setText(
|
||||
mCtx.getResources().getQuantityString(R.plurals.incomplete_emails,
|
||||
mIncompleteEmails, mIncompleteEmails));
|
||||
break;
|
||||
|
||||
if (!mSelector.inActionMode())
|
||||
holder.picture.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View view) {
|
||||
mSelector.select(view);
|
||||
}
|
||||
});
|
||||
case R.layout.listitem_email:
|
||||
final EmailViewHolder evh = (EmailViewHolder) holder;
|
||||
final Email email = getEmail(position);
|
||||
|
||||
// TODO fix
|
||||
//if (mSelectedEmails.get(position)) {
|
||||
// holder.emailSelected.setVisibility(View.VISIBLE);
|
||||
//}
|
||||
|
||||
try {
|
||||
String otherAddress;
|
||||
if (BoteHelper.isSentEmail(email))
|
||||
otherAddress = email.getOneRecipient();
|
||||
else
|
||||
otherAddress = email.getOneFromAddress();
|
||||
|
||||
Bitmap pic = BoteHelper.getPictureForAddress(otherAddress);
|
||||
if (pic != null)
|
||||
holder.picture.setImageBitmap(pic);
|
||||
else if (BoteHelper.isSentEmail(email) || !email.isAnonymous()) {
|
||||
ViewGroup.LayoutParams lp = holder.picture.getLayoutParams();
|
||||
holder.picture.setImageBitmap(BoteHelper.getIdenticonForAddress(otherAddress, lp.width, lp.height));
|
||||
} else
|
||||
holder.picture.setImageDrawable(
|
||||
getContext().getResources().getDrawable(R.drawable.ic_contact_picture));
|
||||
|
||||
holder.subject.setText(email.getSubject());
|
||||
holder.address.setText(BoteHelper.getNameAndShortDestination(otherAddress));
|
||||
|
||||
Date date = email.getSentDate();
|
||||
if (date == null)
|
||||
date = email.getReceivedDate();
|
||||
if (date != null) {
|
||||
DateFormat df;
|
||||
Calendar boundary = Calendar.getInstance();
|
||||
boundary.set(Calendar.HOUR, 0);
|
||||
boundary.set(Calendar.MINUTE, 0);
|
||||
boundary.set(Calendar.SECOND, 0);
|
||||
if (date.before(boundary.getTime())) {
|
||||
boundary.set(Calendar.MONTH, Calendar.JANUARY);
|
||||
boundary.set(Calendar.DAY_OF_MONTH, 1);
|
||||
if (date.before(boundary.getTime())) // Sent before this year
|
||||
df = DateFormat.getDateInstance(DateFormat.MEDIUM);
|
||||
else { // Sent this year before today
|
||||
String yearlessPattern = ((SimpleDateFormat) SimpleDateFormat.getDateInstance(DateFormat.MEDIUM))
|
||||
.toPattern().replaceAll(",?\\W?[Yy]+\\W?", "");
|
||||
df = new SimpleDateFormat(yearlessPattern);
|
||||
evh.picture.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
selectEmail(evh.getPosition(), evh.getItemId(), true);
|
||||
}
|
||||
} else // Sent today
|
||||
df = DateFormat.getTimeInstance();
|
||||
holder.sent.setText(df.format(date));
|
||||
holder.sent.setVisibility(View.VISIBLE);
|
||||
} else
|
||||
holder.sent.setVisibility(View.GONE);
|
||||
});
|
||||
evh.itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
selectEmail(evh.getPosition(), evh.getItemId(), false);
|
||||
}
|
||||
});
|
||||
evh.itemView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View view) {
|
||||
selectEmail(evh.getPosition(), evh.getItemId(), true);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
holder.emailAttachment.setVisibility(View.GONE);
|
||||
List<Part> parts = email.getParts();
|
||||
for (Part part : parts) {
|
||||
if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition())) {
|
||||
holder.emailAttachment.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
holder.subject.setTypeface(email.isUnread() ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
|
||||
holder.address.setTypeface(email.isUnread() ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
|
||||
if (email.isAnonymous() && !BoteHelper.isSentEmail(email)) {
|
||||
if (email.isUnread())
|
||||
holder.address.setTypeface(Typeface.DEFAULT, Typeface.BOLD_ITALIC);
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
|
||||
evh.itemView.setSelected(isSelected(position));
|
||||
else
|
||||
holder.address.setTypeface(Typeface.DEFAULT, Typeface.ITALIC);
|
||||
}
|
||||
evh.itemView.setActivated(isSelected(position));
|
||||
// TODO fix
|
||||
//holder.emailSelected.setVisibility(isSelected(position) ? View.VISIBLE : View.GONE);
|
||||
|
||||
// Set email sending status if this is the outbox,
|
||||
// or set email delivery status if we sent it.
|
||||
if (mIsOutbox) {
|
||||
holder.emailStatus.setText(BoteHelper.getEmailStatusText(
|
||||
getContext(), email, false));
|
||||
holder.emailStatus.setVisibility(View.VISIBLE);
|
||||
} else if (BoteHelper.isSentEmail(email)) {
|
||||
if (email.isDelivered()) {
|
||||
holder.emailStatus.setVisibility(View.GONE);
|
||||
} else {
|
||||
holder.emailStatus.setText(email.getDeliveryPercentage() + "%");
|
||||
holder.emailStatus.setVisibility(View.VISIBLE);
|
||||
try {
|
||||
boolean isSentEmail = BoteHelper.isSentEmail(email);
|
||||
String otherAddress;
|
||||
if (isSentEmail)
|
||||
otherAddress = email.getOneRecipient();
|
||||
else
|
||||
otherAddress = email.getOneFromAddress();
|
||||
|
||||
Bitmap pic = BoteHelper.getPictureForAddress(otherAddress);
|
||||
if (pic != null)
|
||||
evh.picture.setImageBitmap(pic);
|
||||
else if (isSentEmail || !email.isAnonymous()) {
|
||||
ViewGroup.LayoutParams lp = evh.picture.getLayoutParams();
|
||||
evh.picture.setImageBitmap(BoteHelper.getIdenticonForAddress(otherAddress, lp.width, lp.height));
|
||||
} else
|
||||
evh.picture.setImageDrawable(
|
||||
mCtx.getResources().getDrawable(R.drawable.ic_contact_picture));
|
||||
|
||||
evh.subject.setText(email.getSubject());
|
||||
evh.address.setText(BoteHelper.getNameAndShortDestination(otherAddress));
|
||||
|
||||
Date date = email.getSentDate();
|
||||
if (date == null)
|
||||
date = email.getReceivedDate();
|
||||
if (date != null) {
|
||||
DateFormat df;
|
||||
if (date.before(BOUNDARY_DAY.getTime())) {
|
||||
if (date.before(BOUNDARY_YEAR.getTime())) // Sent before this year
|
||||
df = DATE_BEFORE_THIS_YEAR;
|
||||
else // Sent this year before today
|
||||
df = DATE_THIS_YEAR;
|
||||
} else // Sent today
|
||||
df = DATE_TODAY;
|
||||
evh.sent.setText(df.format(date));
|
||||
evh.sent.setVisibility(View.VISIBLE);
|
||||
} else
|
||||
evh.sent.setVisibility(View.GONE);
|
||||
|
||||
evh.emailAttachment.setVisibility(View.GONE);
|
||||
for (Part part : email.getParts()) {
|
||||
if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition())) {
|
||||
evh.emailAttachment.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
evh.subject.setTypeface(email.isUnread() ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
|
||||
evh.address.setTypeface(email.isUnread() ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
|
||||
if (email.isAnonymous() && !isSentEmail) {
|
||||
if (email.isUnread())
|
||||
evh.address.setTypeface(Typeface.DEFAULT, Typeface.BOLD_ITALIC);
|
||||
else
|
||||
evh.address.setTypeface(Typeface.DEFAULT, Typeface.ITALIC);
|
||||
}
|
||||
|
||||
// Set email sending status if this is the outbox,
|
||||
// or set email delivery status if we sent it.
|
||||
if (mIsOutbox) {
|
||||
evh.emailStatus.setText(BoteHelper.getEmailStatusText(
|
||||
mCtx, email, false));
|
||||
evh.emailStatus.setVisibility(View.VISIBLE);
|
||||
} else if (isSentEmail) {
|
||||
if (email.isDelivered()) {
|
||||
evh.emailStatus.setVisibility(View.GONE);
|
||||
} else {
|
||||
evh.emailStatus.setText(email.getDeliveryPercentage() + "%");
|
||||
evh.emailStatus.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
evh.emailDelivered.setVisibility(
|
||||
!mIsOutbox && isSentEmail && email.isDelivered() ?
|
||||
View.VISIBLE : View.GONE);
|
||||
} catch (Exception e) {
|
||||
evh.subject.setText("ERROR: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
holder.emailDelivered.setVisibility(
|
||||
!mIsOutbox && BoteHelper.isSentEmail(email) && email.isDelivered() ?
|
||||
View.VISIBLE : View.GONE);
|
||||
} catch (Exception e) {
|
||||
holder.subject.setText("ERROR: " + e.getMessage());
|
||||
evh.content.setText(email.getText());
|
||||
break;
|
||||
}
|
||||
holder.content.setText(email.getText());
|
||||
}
|
||||
|
||||
return view;
|
||||
private void selectEmail(int position, long id, boolean selectorOnly) {
|
||||
if (selectorOnly || getSelector().inActionMode()) {
|
||||
getSelector().selectItem(position, id);
|
||||
} else {
|
||||
final Email email = getEmail(position);
|
||||
mListener.onEmailSelected(mFolderName, email.getMessageID());
|
||||
}
|
||||
}
|
||||
|
||||
// Return the size of the dataset (invoked by the layout manager)
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
if (mEmails != null)
|
||||
return mIncompleteEmails > 0 ? mEmails.size() + 1 : mEmails.size();
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
Email email = getEmail(position);
|
||||
return email == null ? 0 : email.getMessageID().hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
@@ -12,18 +11,16 @@ import android.support.v4.content.Loader;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.Gravity;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
@@ -37,7 +34,7 @@ import javax.mail.Flags.Flag;
|
||||
import javax.mail.MessagingException;
|
||||
|
||||
import i2p.bote.I2PBote;
|
||||
import i2p.bote.android.util.AuthenticatedListFragment;
|
||||
import i2p.bote.android.util.AuthenticatedFragment;
|
||||
import i2p.bote.android.util.BetterAsyncTaskLoader;
|
||||
import i2p.bote.android.util.BoteHelper;
|
||||
import i2p.bote.android.util.MoveToDialogFragment;
|
||||
@@ -48,10 +45,10 @@ import i2p.bote.fileencryption.PasswordException;
|
||||
import i2p.bote.folder.EmailFolder;
|
||||
import i2p.bote.folder.FolderListener;
|
||||
|
||||
public class EmailListFragment extends AuthenticatedListFragment implements
|
||||
public class EmailListFragment extends AuthenticatedFragment implements
|
||||
LoaderManager.LoaderCallbacks<List<Email>>,
|
||||
MoveToDialogFragment.MoveToDialogListener,
|
||||
EmailListAdapter.EmailSelector, SwipeRefreshLayout.OnRefreshListener {
|
||||
SwipeRefreshLayout.OnRefreshListener {
|
||||
public static final String FOLDER_NAME = "folder_name";
|
||||
|
||||
private static final int EMAIL_LIST_LOADER = 1;
|
||||
@@ -60,9 +57,8 @@ public class EmailListFragment extends AuthenticatedListFragment implements
|
||||
|
||||
private MultiSwipeRefreshLayout mSwipeRefreshLayout;
|
||||
private AsyncTask<Void, Void, Void> mCheckingTask;
|
||||
private TextView mEmptyText;
|
||||
private TextView mNumIncompleteEmails;
|
||||
|
||||
private RecyclerView mEmailsList;
|
||||
private EmailListAdapter mAdapter;
|
||||
private EmailFolder mFolder;
|
||||
|
||||
@@ -101,10 +97,8 @@ public class EmailListFragment extends AuthenticatedListFragment implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
// Create the list fragment's content view by calling the super method
|
||||
final View listFragmentView = super.onCreateView(inflater, container, savedInstanceState);
|
||||
|
||||
public View onCreateAuthenticatedView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
String folderName = getArguments().getString(FOLDER_NAME);
|
||||
mFolder = BoteHelper.getMailFolder(folderName);
|
||||
boolean isInbox = BoteHelper.isInbox(mFolder);
|
||||
@@ -112,8 +106,8 @@ public class EmailListFragment extends AuthenticatedListFragment implements
|
||||
View v = inflater.inflate(
|
||||
isInbox ? R.layout.fragment_list_emails_with_refresh : R.layout.fragment_list_emails,
|
||||
container, false);
|
||||
FrameLayout listContainer = (FrameLayout) v.findViewById(R.id.list_container);
|
||||
listContainer.addView(listFragmentView);
|
||||
|
||||
mEmailsList = (RecyclerView) v.findViewById(R.id.emails_list);
|
||||
|
||||
mNewEmail = (ImageButton) v.findViewById(R.id.promoted_action);
|
||||
mNewEmail.setOnClickListener(new View.OnClickListener() {
|
||||
@@ -126,56 +120,47 @@ public class EmailListFragment extends AuthenticatedListFragment implements
|
||||
if (isInbox) {
|
||||
mSwipeRefreshLayout = (MultiSwipeRefreshLayout) v;
|
||||
|
||||
// Set up the empty view
|
||||
View emptyView = mSwipeRefreshLayout.findViewById(android.R.id.empty);
|
||||
ListView listView = (ListView) mSwipeRefreshLayout.findViewById(android.R.id.list);
|
||||
listView.setEmptyView(emptyView);
|
||||
mEmptyText = (TextView) mSwipeRefreshLayout.findViewById(R.id.empty_text);
|
||||
|
||||
// Set up the MultiSwipeRefreshLayout
|
||||
mSwipeRefreshLayout.setColorSchemeResources(
|
||||
R.color.primary, R.color.accent, R.color.primary, R.color.accent);
|
||||
mSwipeRefreshLayout.setSwipeableChildren(android.R.id.list, android.R.id.empty);
|
||||
mSwipeRefreshLayout.setSwipeableChildren(R.id.emails_list);
|
||||
mSwipeRefreshLayout.setOnRefreshListener(this);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEmptyText(CharSequence text) {
|
||||
if (mEmptyText == null)
|
||||
super.setEmptyText(text);
|
||||
else
|
||||
mEmptyText.setText(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
mAdapter = new EmailListAdapter(getActivity(), this,
|
||||
BoteHelper.isOutbox(mFolder));
|
||||
|
||||
setListAdapter(mAdapter);
|
||||
// Use a linear layout manager
|
||||
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
|
||||
mEmailsList.setLayoutManager(mLayoutManager);
|
||||
|
||||
mEmailsList.setHasFixedSize(true);
|
||||
|
||||
// Set the adapter for the list view
|
||||
mAdapter = new EmailListAdapter(getActivity(), mFolder.getName(), mCallback);
|
||||
mEmailsList.setAdapter(mAdapter);
|
||||
|
||||
// Attach a MultiSelectionUtil.Controller to the ListView, giving it an instance of
|
||||
// ModalChoiceListener (see below)
|
||||
mModalChoiceListener = new ModalChoiceListener();
|
||||
mMultiSelectController = MultiSelectionUtil
|
||||
.attachMultiSelectionController(getListView(), (ActionBarActivity) getActivity(),
|
||||
.attachMultiSelectionController(mEmailsList, (ActionBarActivity) getActivity(),
|
||||
mModalChoiceListener);
|
||||
|
||||
// Allow the Controller to restore itself
|
||||
mMultiSelectController.restoreInstanceState(savedInstanceState);
|
||||
|
||||
if (mFolder == null) {
|
||||
setEmptyText(getResources().getString(
|
||||
R.string.folder_does_not_exist));
|
||||
getActivity().setTitle(getResources().getString(R.string.app_name));
|
||||
} else {
|
||||
getActivity().setTitle(
|
||||
BoteHelper.getFolderDisplayName(getActivity(), mFolder));
|
||||
mFolder = I2PBote.getInstance().getInbox();
|
||||
Toast.makeText(getActivity(), R.string.folder_does_not_exist, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
getActivity().setTitle(
|
||||
BoteHelper.getFolderDisplayName(getActivity(), mFolder));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -206,32 +191,19 @@ public class EmailListFragment extends AuthenticatedListFragment implements
|
||||
* Only called when we have a password cached, or no
|
||||
* password is required.
|
||||
*/
|
||||
protected void onInitializeList() {
|
||||
protected void onInitializeFragment() {
|
||||
if (mFolder == null)
|
||||
return;
|
||||
|
||||
if (BoteHelper.isInbox(mFolder)) {
|
||||
int numIncompleteEmails = I2PBote.getInstance().getNumIncompleteEmails();
|
||||
if (numIncompleteEmails > 0) {
|
||||
mNumIncompleteEmails = (TextView) getActivity().getLayoutInflater().inflate(
|
||||
R.layout.listitem_incomplete, getListView(), false);
|
||||
mNumIncompleteEmails.setText(getResources().getQuantityString(R.plurals.incomplete_emails,
|
||||
numIncompleteEmails, numIncompleteEmails));
|
||||
getListView().addHeaderView(mNumIncompleteEmails, null, false);
|
||||
}
|
||||
mAdapter.setIncompleteEmails(I2PBote.getInstance().getNumIncompleteEmails());
|
||||
}
|
||||
|
||||
setListShown(false);
|
||||
setEmptyText(getResources().getString(
|
||||
R.string.folder_empty));
|
||||
getLoaderManager().initLoader(EMAIL_LIST_LOADER, null, this);
|
||||
}
|
||||
|
||||
protected void onDestroyList() {
|
||||
if (mNumIncompleteEmails != null) {
|
||||
getListView().removeHeaderView(mNumIncompleteEmails);
|
||||
mNumIncompleteEmails = null;
|
||||
}
|
||||
protected void onDestroyFragment() {
|
||||
mAdapter.setIncompleteEmails(0);
|
||||
|
||||
getLoaderManager().destroyLoader(EMAIL_LIST_LOADER);
|
||||
}
|
||||
@@ -292,33 +264,17 @@ public class EmailListFragment extends AuthenticatedListFragment implements
|
||||
startActivity(nei);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onListItemClick(ListView parent, View view, int pos, long id) {
|
||||
super.onListItemClick(parent, view, pos, id);
|
||||
final Email email = (Email) getListView().getItemAtPosition(pos);
|
||||
if (email != null)
|
||||
mCallback.onEmailSelected(mFolder.getName(), email.getMessageID());
|
||||
}
|
||||
|
||||
private class ModalChoiceListener implements MultiSelectionUtil.MultiChoiceModeListener {
|
||||
private boolean areUnread;
|
||||
|
||||
@Override
|
||||
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
|
||||
final ListView listView = getListView();
|
||||
int numChecked = 0;
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
|
||||
final SparseBooleanArray items = listView.getCheckedItemPositions();
|
||||
for (int i = 0; i < items.size(); i++)
|
||||
if (items.valueAt(i))
|
||||
numChecked++;
|
||||
} else
|
||||
numChecked = listView.getCheckedItemCount();
|
||||
int numChecked = mAdapter.getSelectedItemCount();
|
||||
|
||||
mode.setTitle(getResources().getString(R.string.items_selected, numChecked));
|
||||
|
||||
if (checked && numChecked == 1) { // This is the first checked item
|
||||
Email email = (Email) listView.getItemAtPosition(position);
|
||||
Email email = mAdapter.getEmail(position);
|
||||
areUnread = email.isUnread();
|
||||
mode.invalidate();
|
||||
}
|
||||
@@ -326,44 +282,39 @@ public class EmailListFragment extends AuthenticatedListFragment implements
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||
final ListView listView = getListView();
|
||||
// Respond to clicks on the actions in the CAB
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_delete:
|
||||
SparseBooleanArray toDelete = listView.getCheckedItemPositions();
|
||||
List<Integer> toDelete = mAdapter.getSelectedItems();
|
||||
if (toDelete.size() == 0)
|
||||
return false;
|
||||
|
||||
for (int i = (toDelete.size() - 1); i >= 0; i--) {
|
||||
if (toDelete.valueAt(i)) {
|
||||
Email email = (Email) listView.getItemAtPosition(toDelete.keyAt(i));
|
||||
BoteHelper.revokeAttachmentUriPermissions(
|
||||
getActivity(),
|
||||
mFolder.getName(),
|
||||
email);
|
||||
// The Loader will update mAdapter
|
||||
I2PBote.getInstance().deleteEmail(mFolder, email.getMessageID());
|
||||
}
|
||||
Email email = mAdapter.getEmail(toDelete.get(i));
|
||||
BoteHelper.revokeAttachmentUriPermissions(
|
||||
getActivity(),
|
||||
mFolder.getName(),
|
||||
email);
|
||||
// The Loader will update mAdapter
|
||||
I2PBote.getInstance().deleteEmail(mFolder, email.getMessageID());
|
||||
}
|
||||
mode.finish();
|
||||
return true;
|
||||
|
||||
case R.id.action_mark_read:
|
||||
case R.id.action_mark_unread:
|
||||
SparseBooleanArray selected = listView.getCheckedItemPositions();
|
||||
List<Integer> selected = mAdapter.getSelectedItems();
|
||||
for (int i = (selected.size() - 1); i >= 0; i--) {
|
||||
if (selected.valueAt(i)) {
|
||||
Email email = (Email) listView.getItemAtPosition(selected.keyAt(i));
|
||||
try {
|
||||
// The Loader will update mAdapter
|
||||
mFolder.setNew(email, !areUnread);
|
||||
} catch (PasswordException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (GeneralSecurityException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
Email email = mAdapter.getEmail(selected.get(i));
|
||||
try {
|
||||
// The Loader will update mAdapter
|
||||
mFolder.setNew(email, !areUnread);
|
||||
} catch (PasswordException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (GeneralSecurityException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
areUnread = !areUnread;
|
||||
@@ -415,13 +366,10 @@ public class EmailListFragment extends AuthenticatedListFragment implements
|
||||
// Called by EmailListActivity.onFolderSelected()
|
||||
|
||||
public void onFolderSelected(EmailFolder newFolder) {
|
||||
final ListView listView = getListView();
|
||||
SparseBooleanArray toMove = listView.getCheckedItemPositions();
|
||||
List<Integer> toMove = mAdapter.getSelectedItems();
|
||||
for (int i = (toMove.size() - 1); i >= 0; i--) {
|
||||
if (toMove.valueAt(i)) {
|
||||
Email email = (Email) listView.getItemAtPosition(toMove.keyAt(i));
|
||||
mFolder.move(email, newFolder);
|
||||
}
|
||||
Email email = mAdapter.getEmail(toMove.get(i));
|
||||
mFolder.move(email, newFolder);
|
||||
}
|
||||
mMultiSelectController.finish();
|
||||
}
|
||||
@@ -491,7 +439,7 @@ public class EmailListFragment extends AuthenticatedListFragment implements
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
mAdapter.setData(data);
|
||||
mAdapter.setEmails(data);
|
||||
try {
|
||||
getActivity().setTitle(
|
||||
BoteHelper.getFolderDisplayNameWithNew(getActivity(), mFolder));
|
||||
@@ -501,33 +449,14 @@ public class EmailListFragment extends AuthenticatedListFragment implements
|
||||
if (log.shouldLog(Log.WARN))
|
||||
log.warn("Email list loader finished, but password is no longer cached", e);
|
||||
}
|
||||
|
||||
if (isResumed()) {
|
||||
setListShown(true);
|
||||
} else {
|
||||
setListShownNoAnimation(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void onLoaderReset(Loader<List<Email>> loader) {
|
||||
mAdapter.setData(null);
|
||||
mAdapter.setEmails(null);
|
||||
getActivity().setTitle(
|
||||
BoteHelper.getFolderDisplayName(getActivity(), mFolder));
|
||||
}
|
||||
|
||||
// EmailListAdapter.EmailSelector
|
||||
|
||||
public boolean inActionMode() {
|
||||
return mMultiSelectController.inActionMode();
|
||||
}
|
||||
|
||||
public void select(View view) {
|
||||
final ListView listView = getListView();
|
||||
final int position = listView.getPositionForView(view);
|
||||
listView.setItemChecked(position, !listView.isItemChecked(position));
|
||||
view.performLongClick();
|
||||
}
|
||||
|
||||
// SwipeRefreshLayout.OnRefreshListener
|
||||
|
||||
public void onRefresh() {
|
||||
@@ -560,19 +489,7 @@ public class EmailListFragment extends AuthenticatedListFragment implements
|
||||
protected void onPostExecute(Void result) {
|
||||
super.onPostExecute(result);
|
||||
|
||||
int numIncomingEmails = I2PBote.getInstance().getNumIncompleteEmails();
|
||||
if (numIncomingEmails > 0) {
|
||||
if (mNumIncompleteEmails == null) {
|
||||
mNumIncompleteEmails = new TextView(getActivity());
|
||||
getListView().addHeaderView(mNumIncompleteEmails);
|
||||
}
|
||||
mNumIncompleteEmails.setText(getResources().getQuantityString(
|
||||
R.plurals.incomplete_emails,
|
||||
numIncomingEmails, numIncomingEmails));
|
||||
} else if (mNumIncompleteEmails != null) {
|
||||
getListView().removeHeaderView(mNumIncompleteEmails);
|
||||
mNumIncompleteEmails = null;
|
||||
}
|
||||
mAdapter.setIncompleteEmails(I2PBote.getInstance().getNumIncompleteEmails());
|
||||
|
||||
// Notify PullToRefreshLayout that the refresh has finished
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
|
||||
@@ -6,14 +6,14 @@ import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ListView;
|
||||
|
||||
import com.google.zxing.integration.android.IntentIntegrator;
|
||||
|
||||
@@ -21,14 +21,15 @@ import java.util.SortedSet;
|
||||
|
||||
import i2p.bote.I2PBote;
|
||||
import i2p.bote.android.R;
|
||||
import i2p.bote.android.util.AuthenticatedListFragment;
|
||||
import i2p.bote.android.util.AuthenticatedFragment;
|
||||
import i2p.bote.android.util.BetterAsyncTaskLoader;
|
||||
import i2p.bote.fileencryption.PasswordException;
|
||||
import i2p.bote.packet.dht.Contact;
|
||||
|
||||
public class AddressBookFragment extends AuthenticatedListFragment implements
|
||||
public class AddressBookFragment extends AuthenticatedFragment implements
|
||||
LoaderManager.LoaderCallbacks<SortedSet<Contact>> {
|
||||
OnContactSelectedListener mCallback;
|
||||
private RecyclerView mContactsList;
|
||||
private ContactAdapter mAdapter;
|
||||
|
||||
private View mPromotedActions;
|
||||
@@ -60,14 +61,11 @@ public class AddressBookFragment extends AuthenticatedListFragment implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
// Create the list fragment's content view by calling the super method
|
||||
final View listFragmentView = super.onCreateView(inflater, container, savedInstanceState);
|
||||
|
||||
public View onCreateAuthenticatedView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.fragment_list_contacts, container, false);
|
||||
FrameLayout listContainer = (FrameLayout) v.findViewById(R.id.list_container);
|
||||
listContainer.addView(listFragmentView);
|
||||
|
||||
mContactsList = (RecyclerView) v.findViewById(R.id.contacts_list);
|
||||
mPromotedActions = v.findViewById(R.id.promoted_actions);
|
||||
|
||||
ImageButton b = (ImageButton) v.findViewById(R.id.action_new_contact);
|
||||
@@ -92,9 +90,14 @@ public class AddressBookFragment extends AuthenticatedListFragment implements
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
mAdapter = new ContactAdapter(getActivity());
|
||||
|
||||
setListAdapter(mAdapter);
|
||||
// Use a linear layout manager
|
||||
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
|
||||
mContactsList.setLayoutManager(mLayoutManager);
|
||||
|
||||
// Set the adapter for the list view
|
||||
mAdapter = new ContactAdapter(mCallback);
|
||||
mContactsList.setAdapter(mAdapter);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,14 +105,11 @@ public class AddressBookFragment extends AuthenticatedListFragment implements
|
||||
* Only called when we have a password cached, or no
|
||||
* password is required.
|
||||
*/
|
||||
protected void onInitializeList() {
|
||||
setListShown(false);
|
||||
setEmptyText(getResources().getString(
|
||||
R.string.address_book_empty));
|
||||
protected void onInitializeFragment() {
|
||||
getLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
||||
protected void onDestroyList() {
|
||||
protected void onDestroyFragment() {
|
||||
getLoaderManager().destroyLoader(0);
|
||||
}
|
||||
|
||||
@@ -135,13 +135,7 @@ public class AddressBookFragment extends AuthenticatedListFragment implements
|
||||
integrator.initiateScan(IntentIntegrator.QR_CODE_TYPES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onListItemClick(ListView parent, View view, int pos, long id) {
|
||||
mCallback.onContactSelected(mAdapter.getItem(pos));
|
||||
}
|
||||
|
||||
protected void updateContactList() {
|
||||
setListShown(false);
|
||||
getLoaderManager().restartLoader(0, null, this);
|
||||
}
|
||||
|
||||
@@ -184,17 +178,11 @@ public class AddressBookFragment extends AuthenticatedListFragment implements
|
||||
@Override
|
||||
public void onLoadFinished(Loader<SortedSet<Contact>> loader,
|
||||
SortedSet<Contact> data) {
|
||||
mAdapter.setData(data);
|
||||
|
||||
if (isResumed()) {
|
||||
setListShown(true);
|
||||
} else {
|
||||
setListShownNoAnimation(true);
|
||||
}
|
||||
mAdapter.setContacts(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<SortedSet<Contact>> loader) {
|
||||
mAdapter.setData(null);
|
||||
mAdapter.setContacts(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +1,86 @@
|
||||
package i2p.bote.android.addressbook;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.SortedSet;
|
||||
|
||||
import i2p.bote.android.R;
|
||||
import i2p.bote.android.util.BoteHelper;
|
||||
import i2p.bote.packet.dht.Contact;
|
||||
|
||||
public class ContactAdapter extends ArrayAdapter<Contact> {
|
||||
private final LayoutInflater mInflater;
|
||||
public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ViewHolder> {
|
||||
private List<Contact> mContacts;
|
||||
private AddressBookFragment.OnContactSelectedListener mListener;
|
||||
|
||||
public ContactAdapter(Context context) {
|
||||
super(context, android.R.layout.simple_list_item_2);
|
||||
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
}
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
public ImageView mPicture;
|
||||
public TextView mName;
|
||||
|
||||
public void setData(SortedSet<Contact> contacts) {
|
||||
clear();
|
||||
if (contacts != null) {
|
||||
for (Contact contact : contacts) {
|
||||
add(contact);
|
||||
}
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
mPicture = (ImageView) itemView.findViewById(R.id.contact_picture);
|
||||
mName = (TextView) itemView.findViewById(R.id.contact_name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View v = mInflater.inflate(R.layout.listitem_contact, parent, false);
|
||||
Contact contact = getItem(position);
|
||||
public ContactAdapter(AddressBookFragment.OnContactSelectedListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
ImageView picture = (ImageView) v.findViewById(R.id.contact_picture);
|
||||
TextView name = (TextView) v.findViewById(R.id.contact_name);
|
||||
public void setContacts(SortedSet<Contact> contacts) {
|
||||
if (contacts != null) {
|
||||
mContacts = new ArrayList<Contact>();
|
||||
mContacts.addAll(contacts);
|
||||
} else
|
||||
mContacts = null;
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
// Create new views (invoked by the layout manager)
|
||||
@Override
|
||||
public ContactAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
|
||||
int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.listitem_contact, parent, false);
|
||||
return new ViewHolder(v);
|
||||
}
|
||||
|
||||
// Replace the contents of a view (invoked by the layout manager)
|
||||
@Override
|
||||
public void onBindViewHolder(final ViewHolder holder, int position) {
|
||||
Contact contact = mContacts.get(position);
|
||||
|
||||
String pic = contact.getPictureBase64();
|
||||
if (pic != null && !pic.isEmpty())
|
||||
picture.setImageBitmap(BoteHelper.decodePicture(pic));
|
||||
holder.mPicture.setImageBitmap(BoteHelper.decodePicture(pic));
|
||||
else {
|
||||
ViewGroup.LayoutParams lp = picture.getLayoutParams();
|
||||
picture.setImageBitmap(BoteHelper.getIdenticonForAddress(contact.getBase64Dest(), lp.width, lp.height));
|
||||
ViewGroup.LayoutParams lp = holder.mPicture.getLayoutParams();
|
||||
holder.mPicture.setImageBitmap(BoteHelper.getIdenticonForAddress(contact.getBase64Dest(), lp.width, lp.height));
|
||||
}
|
||||
|
||||
name.setText(contact.getName());
|
||||
holder.mName.setText(contact.getName());
|
||||
|
||||
return v;
|
||||
holder.itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mListener.onContactSelected(mContacts.get(holder.getPosition()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Return the size of the dataset (invoked by the layout manager)
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
if (mContacts != null)
|
||||
return mContacts.size();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
package i2p.bote.android.util;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.ListFragment;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import i2p.bote.I2PBote;
|
||||
import i2p.bote.android.R;
|
||||
|
||||
public abstract class AuthenticatedListFragment extends ListFragment {
|
||||
public abstract class AuthenticatedFragment extends Fragment {
|
||||
private FrameLayout mAuthenticatedView;
|
||||
private MenuItem mLogIn;
|
||||
private MenuItem mClearPassword;
|
||||
private boolean mListInitialized;
|
||||
private boolean mFragmentInitialized;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
@@ -20,16 +25,29 @@ public abstract class AuthenticatedListFragment extends ListFragment {
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_authenticated, container, false);
|
||||
|
||||
mAuthenticatedView = (FrameLayout) view.findViewById(R.id.authenticated_view);
|
||||
mAuthenticatedView.addView(onCreateAuthenticatedView(inflater, container, savedInstanceState));
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
protected abstract View onCreateAuthenticatedView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState);
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (I2PBote.getInstance().isPasswordRequired()) {
|
||||
// Ensure any existing data is destroyed.
|
||||
destroyList();
|
||||
destroyFragment();
|
||||
} else {
|
||||
// Password is cached, or not set.
|
||||
initializeList();
|
||||
initializeFragment();
|
||||
}
|
||||
|
||||
getActivity().supportInvalidateOptionsMenu();
|
||||
@@ -37,7 +55,7 @@ public abstract class AuthenticatedListFragment extends ListFragment {
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.authenticated_list, menu);
|
||||
inflater.inflate(R.menu.authenticated_fragment, menu);
|
||||
mLogIn = menu.findItem(R.id.action_log_in);
|
||||
mClearPassword = menu.findItem(R.id.action_log_out);
|
||||
}
|
||||
@@ -56,7 +74,7 @@ public abstract class AuthenticatedListFragment extends ListFragment {
|
||||
BoteHelper.requestPassword(getActivity(), new BoteHelper.RequestPasswordListener() {
|
||||
@Override
|
||||
public void onPasswordVerified() {
|
||||
initializeList();
|
||||
initializeFragment();
|
||||
getActivity().supportInvalidateOptionsMenu();
|
||||
}
|
||||
|
||||
@@ -68,7 +86,7 @@ public abstract class AuthenticatedListFragment extends ListFragment {
|
||||
|
||||
case R.id.action_log_out:
|
||||
BoteHelper.clearPassword();
|
||||
destroyList();
|
||||
destroyFragment();
|
||||
getActivity().supportInvalidateOptionsMenu();
|
||||
return true;
|
||||
|
||||
@@ -77,24 +95,25 @@ public abstract class AuthenticatedListFragment extends ListFragment {
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeList() {
|
||||
if (mListInitialized)
|
||||
private void initializeFragment() {
|
||||
if (mFragmentInitialized)
|
||||
return;
|
||||
|
||||
onInitializeList();
|
||||
onInitializeFragment();
|
||||
|
||||
mListInitialized = true;
|
||||
mAuthenticatedView.setVisibility(View.VISIBLE);
|
||||
|
||||
mFragmentInitialized = true;
|
||||
}
|
||||
|
||||
private void destroyList() {
|
||||
onDestroyList();
|
||||
private void destroyFragment() {
|
||||
onDestroyFragment();
|
||||
|
||||
setEmptyText(getResources().getString(
|
||||
R.string.touch_lock_to_log_in));
|
||||
mAuthenticatedView.setVisibility(View.GONE);
|
||||
|
||||
mListInitialized = false;
|
||||
mFragmentInitialized = false;
|
||||
}
|
||||
|
||||
protected abstract void onInitializeList();
|
||||
protected abstract void onDestroyList();
|
||||
protected abstract void onInitializeFragment();
|
||||
protected abstract void onDestroyFragment();
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
/*
|
||||
/**
|
||||
* Copyright (C) 2015 str4d
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -19,17 +20,16 @@ package i2p.bote.android.util;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Pair;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.Adapter;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utilities for handling multiple selection in list views. Contains functionality similar to {@link
|
||||
@@ -39,30 +39,35 @@ import java.util.HashSet;
|
||||
public class MultiSelectionUtil {
|
||||
|
||||
/**
|
||||
* Attach a Controller to the given <code>listView</code>, <code>activity</code>
|
||||
* Attach a Controller to the given <code>recyclerView</code>, <code>activity</code>
|
||||
* and <code>listener</code>.
|
||||
*
|
||||
* @param listView ListView which displays {@link android.widget.Checkable} items.
|
||||
* @param recyclerView RecyclerView which displays {@link android.widget.Checkable} items.
|
||||
* @param activity Activity which contains the ListView.
|
||||
* @param listener Listener that will manage the selection mode.
|
||||
* @return the attached Controller instance.
|
||||
*/
|
||||
public static Controller attachMultiSelectionController(final ListView listView,
|
||||
public static Controller attachMultiSelectionController(final RecyclerView recyclerView,
|
||||
final ActionBarActivity activity, final MultiChoiceModeListener listener) {
|
||||
return new Controller(listView, activity, listener);
|
||||
if (!(recyclerView.getAdapter() instanceof SelectableAdapter))
|
||||
throw new IllegalArgumentException("Adapter must extend SelectableAdapter");
|
||||
|
||||
return new Controller(recyclerView, activity, listener);
|
||||
}
|
||||
|
||||
public interface Selector {
|
||||
public boolean inActionMode();
|
||||
public void selectItem(int position, long id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class which provides functionality similar to {@link AbsListView#CHOICE_MODE_MULTIPLE_MODAL}
|
||||
* for the {@link ListView} provided to it. A
|
||||
* {@link android.widget.AdapterView.OnItemLongClickListener} is set on the ListView so that
|
||||
* when an item is long-clicked an ActionBarCompat Action Mode is started. Once started, a
|
||||
* {@link android.widget.AdapterView.OnItemClickListener} is set so that an item click toggles
|
||||
* that item's checked state.
|
||||
* for the {@link RecyclerView} provided to it.
|
||||
*/
|
||||
public static class Controller {
|
||||
public static class Controller implements Selector {
|
||||
|
||||
private final ListView mListView;
|
||||
private final RecyclerView mRecyclerView;
|
||||
private final SelectableAdapter mAdapter;
|
||||
private final ActionBarActivity mActivity;
|
||||
private final MultiChoiceModeListener mListener;
|
||||
private final Callbacks mCallbacks;
|
||||
@@ -73,32 +78,47 @@ public class MultiSelectionUtil {
|
||||
// Keeps record of any items that should be checked on the next action mode creation
|
||||
private HashSet<Pair<Integer, Long>> mItemsToCheck;
|
||||
|
||||
// Reference to the replace OnItemClickListener (so it can be restored later)
|
||||
private AdapterView.OnItemClickListener mOldItemClickListener;
|
||||
|
||||
private final Runnable mSetChoiceModeNoneRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mListView.setChoiceMode(AbsListView.CHOICE_MODE_NONE);
|
||||
}
|
||||
};
|
||||
|
||||
private Controller(ListView listView, ActionBarActivity activity,
|
||||
MultiChoiceModeListener listener) {
|
||||
mListView = listView;
|
||||
private Controller(RecyclerView recyclerView, ActionBarActivity activity,
|
||||
MultiChoiceModeListener listener) {
|
||||
mRecyclerView = recyclerView;
|
||||
mAdapter = (SelectableAdapter) recyclerView.getAdapter();
|
||||
mActivity = activity;
|
||||
mListener = listener;
|
||||
mCallbacks = new Callbacks();
|
||||
|
||||
// We set ourselves as the OnItemLongClickListener so we know when to start
|
||||
// an Action Mode
|
||||
listView.setOnItemLongClickListener(mCallbacks);
|
||||
mAdapter.setSelector(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inActionMode() {
|
||||
return mActionMode != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectItem(int position, long id) {
|
||||
if (mActionMode == null) {
|
||||
mItemsToCheck = new HashSet<Pair<Integer, Long>>();
|
||||
mItemsToCheck.add(new Pair<Integer, Long>(position, id));
|
||||
mActionMode = mActivity.startSupportActionMode(mCallbacks);
|
||||
} else {
|
||||
mAdapter.toggleSelection(position);
|
||||
|
||||
// Check to see what the new checked state is, and then notify the listener
|
||||
final boolean checked = mAdapter.isSelected(position);
|
||||
mListener.onItemCheckedStateChanged(mActionMode, position, id, checked);
|
||||
|
||||
boolean hasCheckedItem = checked;
|
||||
|
||||
// Check to see if we have any checked items
|
||||
if (!hasCheckedItem)
|
||||
hasCheckedItem = mAdapter.getSelectedItemCount() > 0;
|
||||
|
||||
// If we don't have any checked items, finish the action mode
|
||||
if (!hasCheckedItem)
|
||||
mActionMode.finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish the current Action Mode (if there is one).
|
||||
*/
|
||||
@@ -138,31 +158,35 @@ public class MultiSelectionUtil {
|
||||
* @param outState - The state passed to your Activity or Fragment.
|
||||
*/
|
||||
public void saveInstanceState(Bundle outState) {
|
||||
if (mActionMode != null && mListView.getAdapter().hasStableIds()) {
|
||||
outState.putLongArray(getStateKey(), mListView.getCheckedItemIds());
|
||||
if (mActionMode != null && mAdapter.hasStableIds()) {
|
||||
List<Integer> selectedItems = mAdapter.getSelectedItems();
|
||||
long[] selectedItemIds = new long[selectedItems.size()];
|
||||
for (int i = 0; i < selectedItems.size(); i++) {
|
||||
selectedItemIds[i] = mAdapter.getItemId(selectedItems.get(i));
|
||||
}
|
||||
outState.putLongArray(getStateKey(), selectedItemIds);
|
||||
}
|
||||
}
|
||||
|
||||
// Internal utility methods
|
||||
|
||||
private String getStateKey() {
|
||||
return MultiSelectionUtil.class.getSimpleName() + "_" + mListView.getId();
|
||||
return MultiSelectionUtil.class.getSimpleName() + "_" + mRecyclerView.getId();
|
||||
}
|
||||
|
||||
private void tryRestoreInstanceState(HashSet<Long> idsToCheckOnRestore) {
|
||||
if (idsToCheckOnRestore == null || mListView.getAdapter() == null) {
|
||||
if (idsToCheckOnRestore == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean idsFound = false;
|
||||
Adapter adapter = mListView.getAdapter();
|
||||
for (int pos = adapter.getCount() - 1; pos >= 0; pos--) {
|
||||
if (idsToCheckOnRestore.contains(adapter.getItemId(pos))) {
|
||||
for (int pos = mAdapter.getItemCount() - 1; pos >= 0; pos--) {
|
||||
if (idsToCheckOnRestore.contains(mAdapter.getItemId(pos))) {
|
||||
idsFound = true;
|
||||
if (mItemsToCheck == null) {
|
||||
mItemsToCheck = new HashSet<Pair<Integer, Long>>();
|
||||
}
|
||||
mItemsToCheck.add(new Pair<Integer, Long>(pos, adapter.getItemId(pos)));
|
||||
mItemsToCheck.add(new Pair<Integer, Long>(pos, mAdapter.getItemId(pos)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,25 +200,16 @@ public class MultiSelectionUtil {
|
||||
/**
|
||||
* This class encapsulates all of the callbacks necessary for the controller class.
|
||||
*/
|
||||
final class Callbacks implements ActionMode.Callback, AdapterView.OnItemClickListener,
|
||||
AdapterView.OnItemLongClickListener {
|
||||
|
||||
final class Callbacks implements ActionMode.Callback {
|
||||
@Override
|
||||
public final boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
|
||||
if (mListener.onCreateActionMode(actionMode, menu)) {
|
||||
mActionMode = actionMode;
|
||||
// Keep a reference to the existing OnItemClickListener so we can restore it
|
||||
mOldItemClickListener = mListView.getOnItemClickListener();
|
||||
|
||||
// Set-up the ListView to emulate CHOICE_MODE_MULTIPLE_MODAL
|
||||
mListView.setOnItemClickListener(this);
|
||||
mListView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
|
||||
mListView.removeCallbacks(mSetChoiceModeNoneRunnable);
|
||||
|
||||
// If there are some items to check, do it now
|
||||
if (mItemsToCheck != null) {
|
||||
for (Pair<Integer, Long> posAndId : mItemsToCheck) {
|
||||
mListView.setItemChecked(posAndId.first, true);
|
||||
mAdapter.toggleSelection(posAndId.first);
|
||||
// Notify the listener that the item has been checked
|
||||
mListener.onItemCheckedStateChanged(mActionMode, posAndId.first,
|
||||
posAndId.second, true);
|
||||
@@ -222,62 +237,10 @@ public class MultiSelectionUtil {
|
||||
mListener.onDestroyActionMode(actionMode);
|
||||
|
||||
// Clear all the checked items
|
||||
SparseBooleanArray checkedPositions = mListView.getCheckedItemPositions();
|
||||
if (checkedPositions != null) {
|
||||
for (int i = 0; i < checkedPositions.size(); i++) {
|
||||
mListView.setItemChecked(checkedPositions.keyAt(i), false);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the original onItemClickListener
|
||||
mListView.setOnItemClickListener(mOldItemClickListener);
|
||||
mAdapter.clearSelections();
|
||||
|
||||
// Clear the Action Mode
|
||||
mActionMode = null;
|
||||
|
||||
// Reset the ListView's Choice Mode
|
||||
mListView.post(mSetChoiceModeNoneRunnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
|
||||
// Check to see what the new checked state is, and then notify the listener
|
||||
final boolean checked = mListView.isItemChecked(position);
|
||||
mListener.onItemCheckedStateChanged(mActionMode, position, id, checked);
|
||||
|
||||
boolean hasCheckedItem = checked;
|
||||
|
||||
// Check to see if we have any checked items
|
||||
if (!hasCheckedItem) {
|
||||
SparseBooleanArray checkedItemPositions = mListView.getCheckedItemPositions();
|
||||
if (checkedItemPositions != null) {
|
||||
// Iterate through the SparseBooleanArray to see if there is a checked item
|
||||
int i = 0;
|
||||
while (!hasCheckedItem && i < checkedItemPositions.size()) {
|
||||
hasCheckedItem = checkedItemPositions.valueAt(i++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't have any checked items, finish the action mode
|
||||
if (!hasCheckedItem) {
|
||||
mActionMode.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int position,
|
||||
long id) {
|
||||
// If we already have an action mode started return false
|
||||
// (onItemClick will be called anyway)
|
||||
if (mActionMode != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mItemsToCheck = new HashSet<Pair<Integer, Long>>();
|
||||
mItemsToCheck.add(new Pair<Integer, Long>(position, id));
|
||||
mActionMode = mActivity.startSupportActionMode(this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -294,4 +257,52 @@ public class MultiSelectionUtil {
|
||||
public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
|
||||
boolean checked);
|
||||
}
|
||||
|
||||
public static abstract class SelectableAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {
|
||||
private Selector mSelector;
|
||||
private SparseBooleanArray selectedItems;
|
||||
|
||||
public SelectableAdapter() {
|
||||
selectedItems = new SparseBooleanArray();
|
||||
}
|
||||
|
||||
public void setSelector(Selector selector) {
|
||||
mSelector = selector;
|
||||
}
|
||||
|
||||
public Selector getSelector() {
|
||||
return mSelector;
|
||||
}
|
||||
|
||||
public void toggleSelection(int position) {
|
||||
if (selectedItems.get(position, false)) {
|
||||
selectedItems.delete(position);
|
||||
} else {
|
||||
selectedItems.put(position, true);
|
||||
}
|
||||
notifyItemChanged(position);
|
||||
}
|
||||
|
||||
public boolean isSelected(int position) {
|
||||
return selectedItems.get(position, false);
|
||||
}
|
||||
|
||||
public void clearSelections() {
|
||||
selectedItems.clear();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public int getSelectedItemCount() {
|
||||
return selectedItems.size();
|
||||
}
|
||||
|
||||
public List<Integer> getSelectedItems() {
|
||||
List<Integer> items =
|
||||
new ArrayList<Integer>(selectedItems.size());
|
||||
for (int i = 0; i < selectedItems.size(); i++) {
|
||||
items.add(selectedItems.keyAt(i));
|
||||
}
|
||||
return items;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@color/listitem_pressed" android:state_pressed="true" />
|
||||
<item android:drawable="@color/listitem_selected" android:state_activated="true" />
|
||||
<item android:drawable="@color/listitem_selected" android:state_checked="true" />
|
||||
<item android:drawable="@color/listitem_selected" android:state_selected="true" />
|
||||
<item android:drawable="@android:color/transparent" />
|
||||
</selector>
|
||||
@@ -5,10 +5,11 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/list_container"
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/contacts_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbars="vertical"/>
|
||||
|
||||
<net.i2p.android.ext.floatingactionbutton.FloatingActionsMenu
|
||||
android:id="@+id/promoted_actions"
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/list_container"
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/emails_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbars="vertical"/>
|
||||
|
||||
<net.i2p.android.ext.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/promoted_action"
|
||||
@@ -21,5 +23,5 @@
|
||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
||||
app:fab_colorNormal="@color/accent"
|
||||
app:fab_colorPressed="@color/accent_dark"
|
||||
app:fab_icon="@drawable/ic_create_white_24dp" />
|
||||
app:fab_icon="@drawable/ic_create_white_24dp"/>
|
||||
</RelativeLayout>
|
||||
@@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<i2p.bote.android.util.MultiSwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
<i2p.bote.android.util.MultiSwipeRefreshLayout
|
||||
android:id="@+id/swipe_refresh"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
@@ -9,23 +10,11 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/list_container"
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/emails_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<ScrollView
|
||||
android:id="@android:id/empty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/empty_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:text="@string/folder_empty" />
|
||||
</ScrollView>
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbars="vertical"/>
|
||||
|
||||
<net.i2p.android.ext.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/promoted_action"
|
||||
@@ -39,7 +28,7 @@
|
||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
||||
app:fab_colorNormal="@color/accent"
|
||||
app:fab_colorPressed="@color/accent_dark"
|
||||
app:fab_icon="@drawable/ic_create_white_24dp" />
|
||||
app:fab_icon="@drawable/ic_create_white_24dp"/>
|
||||
</RelativeLayout>
|
||||
|
||||
</i2p.bote.android.util.MultiSwipeRefreshLayout>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<color name="accent_dark">#ff9100</color><!-- Orange A400 -->
|
||||
|
||||
<color name="listitem_selected">#e0e0e0</color><!-- Grey 300 -->
|
||||
<color name="listitem_pressed">#bdbdbd</color><!-- Grey 400 -->
|
||||
<color name="uva_color">#c31756</color>
|
||||
<color name="error_color">#f00</color>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user