diff --git a/app/build.gradle b/app/build.gradle index 6ed1036..389fc55 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,13 +27,8 @@ dependencies { compile 'net.i2p.android:client:0.1.1@aar' compile project(':botejars') compile fileTree(dir: 'libs', include: '*.jar') - compile 'com.android.support:support-v4:19.1.0' - compile 'com.android.support:appcompat-v7:19.1.0' - // TODO temporary fix for broken developer preview of Support Library - compile ('com.github.chrisbanes.actionbarpulltorefresh:extra-abc:+') { - exclude group: 'com.android.support', module: 'support-v4' - exclude group: 'com.android.support', module: 'appcompat-v7' - } + compile 'com.android.support:support-v4:20.0.0' + compile 'com.android.support:appcompat-v7:20.0.0' compile ('com.mcxiaoke.viewpagerindicator:library:2.4.1') { exclude group: 'com.android.support', module: 'support-v4' } @@ -43,11 +38,8 @@ dependencies { dependencyVerification { verify = [ 'net.i2p.android:client:29815aa778f19bbf2c9d003e30289cca214481bb0fb17afe8adb77641aa7875f', - 'com.android.support:support-v4:3f40fa7b3a4ead01ce15dce9453b061646e7fe2e7c51cb75ca01ee1e77037f3f', - 'com.android.support:appcompat-v7:9df7637c5219202ddbbbf0924c2d5a9e6d64379166795a89d8f75d1e3f3372df', - 'com.github.chrisbanes.actionbarpulltorefresh:extra-abc:a94f35b8b701d2cf761bb6b731d0921f73fb552a69a9c1d1379a19c2a9418ec6', - 'com.github.chrisbanes.actionbarpulltorefresh:library:ea06f4e8cf7ae959cbe37028acf2631a6950714d2112e16531e4528daf69b6db', - 'com.github.castorflex.smoothprogressbar:library:eb35b46d99a7f56d3d173cafe5f0f31beaaba0ce606258f81b07b0607ee3c32f', + 'com.android.support:support-v4:81f2b1c2c94efd5a4ec7fcd97b6cdcd00e87a933905c5c86103c7319eb024572', + 'com.android.support:appcompat-v7:736f576ab0b68d27bdf18b1e7931566e6d8254b73965175313e87f8866b91547', 'com.mcxiaoke.viewpagerindicator:library:470bbd2bec1ede64ad21efd6f02676807d22f1b526c4ac6a5b41a428c6e47e67', 'com.google.zxing:android-integration:89e56aadf1164bd71e57949163c53abf90af368b51669c0d4a47a163335f95c4', ] diff --git a/app/src/main/java/i2p/bote/android/EmailListFragment.java b/app/src/main/java/i2p/bote/android/EmailListFragment.java index e6990cc..03283ad 100644 --- a/app/src/main/java/i2p/bote/android/EmailListFragment.java +++ b/app/src/main/java/i2p/bote/android/EmailListFragment.java @@ -9,9 +9,12 @@ 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; import android.util.SparseBooleanArray; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -40,22 +43,18 @@ import i2p.bote.email.Email; import i2p.bote.fileencryption.PasswordException; import i2p.bote.folder.EmailFolder; import i2p.bote.folder.FolderListener; -import uk.co.senab.actionbarpulltorefresh.extras.actionbarcompat.PullToRefreshLayout; -import uk.co.senab.actionbarpulltorefresh.library.ActionBarPullToRefresh; -import uk.co.senab.actionbarpulltorefresh.library.Options; -import uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener; public class EmailListFragment extends AuthenticatedListFragment implements LoaderManager.LoaderCallbacks>, MoveToDialogFragment.MoveToDialogListener, - EmailListAdapter.EmailSelector, OnRefreshListener { + EmailListAdapter.EmailSelector, SwipeRefreshLayout.OnRefreshListener { public static final String FOLDER_NAME = "folder_name"; private static final int EMAIL_LIST_LOADER = 1; OnEmailSelectedListener mCallback; - private PullToRefreshLayout mPullToRefreshLayout; + private SwipeRefreshLayout mSwipeRefreshLayout; private TextView mNumIncompleteEmails; private EmailListAdapter mAdapter; @@ -95,36 +94,90 @@ public class EmailListFragment extends AuthenticatedListFragment implements } @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); + 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); + String folderName = getArguments().getString(FOLDER_NAME); mFolder = BoteHelper.getMailFolder(folderName); if (BoteHelper.isInbox(mFolder)) { - // This is the View which is created by ListFragment - ViewGroup viewGroup = (ViewGroup) view; + // Now create a SwipeRefreshLayout to wrap the fragment's content view + mSwipeRefreshLayout = new ListFragmentSwipeRefreshLayout(container.getContext()); - // We need to create a PullToRefreshLayout manually - mPullToRefreshLayout = new PullToRefreshLayout(viewGroup.getContext()); + // 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); - // We can now setup the PullToRefreshLayout - ActionBarPullToRefresh.from(getActivity()) + // Make sure that the SwipeRefreshLayout will fill the fragment + mSwipeRefreshLayout.setLayoutParams( + new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); - // We need to insert the PullToRefreshLayout into the Fragment's ViewGroup - .insertLayoutInto(viewGroup) + // Set up the SwipeRefreshLayout + mSwipeRefreshLayout.setOnRefreshListener(this); + mSwipeRefreshLayout.setRefreshing(I2PBote.getInstance().isCheckingForMail()); - // We need to mark the ListView and its Empty View as pullable - // This is because they are not direct children of the ViewGroup - .theseChildrenArePullable(getListView(), getListView().getEmptyView()) + // Now return the SwipeRefreshLayout as this fragment's content view + return mSwipeRefreshLayout; + } else + return listFragmentView; + } - // We can now complete the setup as desired - .listener(this) - .options(Options.create() - .refreshOnUp(true) - .build()) - .setup(mPullToRefreshLayout); + /** + * 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}. + * + *

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 { - mPullToRefreshLayout.setRefreshing(I2PBote.getInstance().isCheckingForMail()); + 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()); } } @@ -461,9 +514,9 @@ public class EmailListFragment extends AuthenticatedListFragment implements view.performLongClick(); } - // OnRefreshListener + // SwipeRefreshLayout.OnRefreshListener - public void onRefreshStarted(View view) { + public void onRefresh() { I2PBote bote = I2PBote.getInstance(); if (bote.isConnected()) { try { @@ -500,7 +553,7 @@ public class EmailListFragment extends AuthenticatedListFragment implements } // Notify PullToRefreshLayout that the refresh has finished - mPullToRefreshLayout.setRefreshComplete(); + mSwipeRefreshLayout.setRefreshing(false); } }.execute(); } catch (PasswordException e) { @@ -514,6 +567,6 @@ public class EmailListFragment extends AuthenticatedListFragment implements e.printStackTrace(); } } else - mPullToRefreshLayout.setRefreshComplete(); + mSwipeRefreshLayout.setRefreshing(false); } } diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 0a1da3f..22d4b4a 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -17,21 +17,6 @@ - - -