Make empty inbox swipeable

This commit is contained in:
str4d
2014-09-24 12:41:38 +00:00
parent 4d21318619
commit 8c35e7254a
3 changed files with 140 additions and 69 deletions

View File

@@ -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

View File

@@ -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;
}
}
}
}