Make empty inbox swipeable
This commit is contained in:
@@ -9,7 +9,6 @@ import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.support.v7.view.ActionMode;
|
||||
@@ -20,6 +19,7 @@ import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
@@ -39,6 +39,7 @@ import i2p.bote.android.util.BetterAsyncTaskLoader;
|
||||
import i2p.bote.android.util.BoteHelper;
|
||||
import i2p.bote.android.util.MoveToDialogFragment;
|
||||
import i2p.bote.android.util.MultiSelectionUtil;
|
||||
import i2p.bote.android.util.MultiSwipeRefreshLayout;
|
||||
import i2p.bote.email.Email;
|
||||
import i2p.bote.fileencryption.PasswordException;
|
||||
import i2p.bote.folder.EmailFolder;
|
||||
@@ -54,7 +55,8 @@ public class EmailListFragment extends AuthenticatedListFragment implements
|
||||
|
||||
OnEmailSelectedListener mCallback;
|
||||
|
||||
private SwipeRefreshLayout mSwipeRefreshLayout;
|
||||
private MultiSwipeRefreshLayout mSwipeRefreshLayout;
|
||||
private TextView mEmptyText;
|
||||
private TextView mNumIncompleteEmails;
|
||||
|
||||
private EmailListAdapter mAdapter;
|
||||
@@ -102,83 +104,35 @@ public class EmailListFragment extends AuthenticatedListFragment implements
|
||||
mFolder = BoteHelper.getMailFolder(folderName);
|
||||
|
||||
if (BoteHelper.isInbox(mFolder)) {
|
||||
// Now create a SwipeRefreshLayout to wrap the fragment's content view
|
||||
mSwipeRefreshLayout = new ListFragmentSwipeRefreshLayout(container.getContext());
|
||||
// Inflate the MultiSwipeRefreshLayout
|
||||
mSwipeRefreshLayout = (MultiSwipeRefreshLayout) inflater.inflate(
|
||||
R.layout.fragment_list_emails, container, false);
|
||||
FrameLayout listContainer = (FrameLayout) mSwipeRefreshLayout.findViewById(R.id.list_container);
|
||||
listContainer.addView(listFragmentView);
|
||||
|
||||
// Add the list fragment's content view to the SwipeRefreshLayout, making sure that it fills
|
||||
// the SwipeRefreshLayout
|
||||
mSwipeRefreshLayout.addView(listFragmentView,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
// 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);
|
||||
|
||||
// Make sure that the SwipeRefreshLayout will fill the fragment
|
||||
mSwipeRefreshLayout.setLayoutParams(
|
||||
new ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
|
||||
// Set up the SwipeRefreshLayout
|
||||
// Set up the MultiSwipeRefreshLayout
|
||||
mSwipeRefreshLayout.setSwipeableChildren(android.R.id.list, android.R.id.empty);
|
||||
mSwipeRefreshLayout.setOnRefreshListener(this);
|
||||
mSwipeRefreshLayout.setRefreshing(I2PBote.getInstance().isCheckingForMail());
|
||||
|
||||
// Now return the SwipeRefreshLayout as this fragment's content view
|
||||
// Now return the MultiSwipeRefreshLayout as this fragment's content view
|
||||
return mSwipeRefreshLayout;
|
||||
} else
|
||||
return listFragmentView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sub-class of {@link android.support.v4.widget.SwipeRefreshLayout} for use in this
|
||||
* {@link android.support.v4.app.ListFragment}. The reason that this is needed is because
|
||||
* {@link android.support.v4.widget.SwipeRefreshLayout} only supports a single child, which it
|
||||
* expects to be the one which triggers refreshes. In our case the layout's child is the content
|
||||
* view returned from
|
||||
* {@link android.support.v4.app.ListFragment#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)}
|
||||
* which is a {@link android.view.ViewGroup}.
|
||||
*
|
||||
* <p>To enable 'swipe-to-refresh' support via the {@link android.widget.ListView} we need to
|
||||
* override the default behavior and properly signal when a gesture is possible. This is done by
|
||||
* overriding {@link #canChildScrollUp()}.
|
||||
*/
|
||||
private class ListFragmentSwipeRefreshLayout extends SwipeRefreshLayout {
|
||||
|
||||
public ListFragmentSwipeRefreshLayout(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* As mentioned above, we need to override this method to properly signal when a
|
||||
* 'swipe-to-refresh' is possible.
|
||||
*
|
||||
* @return true if the {@link android.widget.ListView} is visible and can scroll up.
|
||||
*/
|
||||
@Override
|
||||
public boolean canChildScrollUp() {
|
||||
final ListView listView = getListView();
|
||||
if (listView.getVisibility() == View.VISIBLE) {
|
||||
return canListViewScrollUp(listView);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to check whether a {@link ListView} can scroll up from it's current position.
|
||||
* Handles platform version differences, providing backwards compatible functionality where
|
||||
* needed.
|
||||
*/
|
||||
private static boolean canListViewScrollUp(ListView listView) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 14) {
|
||||
// For ICS and above we can call canScrollVertically() to determine this
|
||||
return ViewCompat.canScrollVertically(listView, -1);
|
||||
} else {
|
||||
// Pre-ICS we need to manually check the first visible item and the child view's top
|
||||
// value
|
||||
return listView.getChildCount() > 0 &&
|
||||
(listView.getFirstVisiblePosition() > 0
|
||||
|| listView.getChildAt(0).getTop() < listView.getPaddingTop());
|
||||
}
|
||||
@Override
|
||||
public void setEmptyText(CharSequence text) {
|
||||
if (mEmptyText == null)
|
||||
super.setEmptyText(text);
|
||||
else
|
||||
mEmptyText.setText(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package i2p.bote.android.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.AbsListView;
|
||||
|
||||
/**
|
||||
* A descendant of {@link android.support.v4.widget.SwipeRefreshLayout} which supports multiple
|
||||
* child views triggering a refresh gesture. You set the views which can trigger the gesture via
|
||||
* {@link #setSwipeableChildren(int...)}, providing it the child ids.
|
||||
*/
|
||||
public class MultiSwipeRefreshLayout extends SwipeRefreshLayout {
|
||||
|
||||
private View[] mSwipeableChildren;
|
||||
|
||||
public MultiSwipeRefreshLayout(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public MultiSwipeRefreshLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the children which can trigger a refresh by swiping down when they are visible. These
|
||||
* views need to be a descendant of this view.
|
||||
*/
|
||||
public void setSwipeableChildren(final int... ids) {
|
||||
assert ids != null;
|
||||
|
||||
// Iterate through the ids and find the Views
|
||||
mSwipeableChildren = new View[ids.length];
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
mSwipeableChildren[i] = findViewById(ids[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method controls when the swipe-to-refresh gesture is triggered. By returning false here
|
||||
* we are signifying that the view is in a state where a refresh gesture can start.
|
||||
*
|
||||
* <p>As {@link android.support.v4.widget.SwipeRefreshLayout} only supports one direct child by
|
||||
* default, we need to manually iterate through our swipeable children to see if any are in a
|
||||
* state to trigger the gesture. If so we return false to start the gesture.
|
||||
*/
|
||||
@Override
|
||||
public boolean canChildScrollUp() {
|
||||
if (mSwipeableChildren != null && mSwipeableChildren.length > 0) {
|
||||
// Iterate through the scrollable children and check if any of them can not scroll up
|
||||
for (View view : mSwipeableChildren) {
|
||||
if (view != null && view.isShown() && !canViewScrollUp(view)) {
|
||||
// If the view is shown, and can not scroll upwards, return false and start the
|
||||
// gesture.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to check whether a {@link View} can scroll up from it's current position.
|
||||
* Handles platform version differences, providing backwards compatible functionality where
|
||||
* needed.
|
||||
*/
|
||||
private static boolean canViewScrollUp(View view) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 14) {
|
||||
// For ICS and above we can call canScrollVertically() to determine this
|
||||
return ViewCompat.canScrollVertically(view, -1);
|
||||
} else {
|
||||
if (view instanceof AbsListView) {
|
||||
// Pre-ICS we need to manually check the first visible item and the child view's top
|
||||
// value
|
||||
final AbsListView listView = (AbsListView) view;
|
||||
return listView.getChildCount() > 0 &&
|
||||
(listView.getFirstVisiblePosition() > 0
|
||||
|| listView.getChildAt(0).getTop() < listView.getPaddingTop());
|
||||
} else {
|
||||
// For all other view types we just check the getScrollY() value
|
||||
return view.getScrollY() > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user