From ae2fa4dce7667e4eab09c17127dd02296a54d4d9 Mon Sep 17 00:00:00 2001 From: str4d <str4d@mail.i2p> Date: Sun, 18 Aug 2013 12:46:13 +0000 Subject: [PATCH] Fill I2PTunnel list with a Loader Until changes to the list of TunnelControllers can be monitored, the Loader refreshes the view every 10 seconds. --- .../router/fragment/I2PTunnelFragment.java | 57 ++++--- .../router/loader/TunnelControllerLoader.java | 147 ++++++++++++++++++ 2 files changed, 184 insertions(+), 20 deletions(-) create mode 100644 src/net/i2p/android/router/loader/TunnelControllerLoader.java diff --git a/src/net/i2p/android/router/fragment/I2PTunnelFragment.java b/src/net/i2p/android/router/fragment/I2PTunnelFragment.java index aa61338a0..096e3a4d6 100644 --- a/src/net/i2p/android/router/fragment/I2PTunnelFragment.java +++ b/src/net/i2p/android/router/fragment/I2PTunnelFragment.java @@ -1,23 +1,30 @@ package net.i2p.android.router.fragment; -import java.util.ArrayList; import java.util.List; import net.i2p.android.router.R; import net.i2p.android.router.adapter.TunnelControllerAdapter; +import net.i2p.android.router.loader.TunnelControllerLoader; import net.i2p.i2ptunnel.TunnelController; import net.i2p.i2ptunnel.TunnelControllerGroup; import android.os.Bundle; import android.support.v4.app.ListFragment; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.Loader; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; -public class I2PTunnelFragment extends ListFragment { +public class I2PTunnelFragment extends ListFragment + implements LoaderManager.LoaderCallbacks<List<TunnelController>> { public static final String SHOW_CLIENT_TUNNELS = "show_client_tunnels"; + private static final int CLIENT_LOADER_ID = 1; + private static final int SERVER_LOADER_ID = 2; + private TunnelControllerGroup mGroup; private TunnelControllerAdapter mAdapter; + private boolean mClientTunnels; @Override public void onCreate(Bundle savedInstanceState) { @@ -29,6 +36,7 @@ public class I2PTunnelFragment extends ListFragment { public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mAdapter = new TunnelControllerAdapter(getActivity()); + mClientTunnels = getArguments().getBoolean(SHOW_CLIENT_TUNNELS); String error; try { @@ -39,36 +47,45 @@ public class I2PTunnelFragment extends ListFragment { error = iae.toString(); } - boolean clientTunnels = getArguments().getBoolean(SHOW_CLIENT_TUNNELS); if (mGroup == null) { setEmptyText(error); } else { - if (clientTunnels) + if (mClientTunnels) setEmptyText("No configured client tunnels."); else setEmptyText("No configured server tunnels."); } - mAdapter.setData(getControllers(clientTunnels)); + setListAdapter(mAdapter); + setListShown(false); + + getLoaderManager().initLoader(mClientTunnels ? CLIENT_LOADER_ID + : SERVER_LOADER_ID, null, this); + } + + public Loader<List<TunnelController>> onCreateLoader(int id, Bundle args) { + return new TunnelControllerLoader(getActivity(), mGroup, mClientTunnels); } - private List<TunnelController> getControllers(boolean clientTunnels) { - List<TunnelController> ret = new ArrayList<TunnelController>(); - for (TunnelController controller : mGroup.getControllers()) - if ( (clientTunnels && isClient(controller.getType())) || - (!clientTunnels && !isClient(controller.getType())) ) - ret.add(controller); - return ret; + public void onLoadFinished(Loader<List<TunnelController>> loader, + List<TunnelController> data) { + if (loader.getId() == (mClientTunnels ? + CLIENT_LOADER_ID : SERVER_LOADER_ID)) { + mAdapter.setData(data); + + if (isResumed()) { + setListShown(true); + } else { + setListShownNoAnimation(true); + } + } } - private static boolean isClient(String type) { - return ( ("client".equals(type)) || - ("httpclient".equals(type)) || - ("sockstunnel".equals(type)) || - ("socksirctunnel".equals(type)) || - ("connectclient".equals(type)) || - ("streamrclient".equals(type)) || - ("ircclient".equals(type))); + public void onLoaderReset(Loader<List<TunnelController>> loader) { + if (loader.getId() == (mClientTunnels ? + CLIENT_LOADER_ID : SERVER_LOADER_ID)) { + mAdapter.setData(null); + } } @Override diff --git a/src/net/i2p/android/router/loader/TunnelControllerLoader.java b/src/net/i2p/android/router/loader/TunnelControllerLoader.java new file mode 100644 index 000000000..6e43fe137 --- /dev/null +++ b/src/net/i2p/android/router/loader/TunnelControllerLoader.java @@ -0,0 +1,147 @@ +package net.i2p.android.router.loader; + +import java.util.ArrayList; +import java.util.List; + +import net.i2p.i2ptunnel.TunnelController; +import net.i2p.i2ptunnel.TunnelControllerGroup; + +import android.content.Context; +import android.os.Handler; +import android.support.v4.content.AsyncTaskLoader; + +public class TunnelControllerLoader extends AsyncTaskLoader<List<TunnelController>> { + private TunnelControllerGroup mGroup; + private boolean mClientTunnels; + private List<TunnelController> mData; + private Handler mHandler; + private TunnelControllerMonitor mMonitor; + + public TunnelControllerLoader(Context context, TunnelControllerGroup tcg, boolean clientTunnels) { + super(context); + mGroup = tcg; + mClientTunnels = clientTunnels; + mHandler = new Handler(); + } + + @Override + public List<TunnelController> loadInBackground() { + List<TunnelController> ret = new ArrayList<TunnelController>(); + for (TunnelController controller : mGroup.getControllers()) + if ( (mClientTunnels && isClient(controller.getType())) || + (!mClientTunnels && !isClient(controller.getType())) ) + ret.add(controller); + return ret; + } + + private static boolean isClient(String type) { + return ( ("client".equals(type)) || + ("httpclient".equals(type)) || + ("sockstunnel".equals(type)) || + ("socksirctunnel".equals(type)) || + ("connectclient".equals(type)) || + ("streamrclient".equals(type)) || + ("ircclient".equals(type))); + } + + @Override + public void deliverResult(List<TunnelController> data) { + if (isReset()) { + // The Loader has been reset; ignore the result and invalidate the data. + if (data != null) { + releaseResources(data); + return; + } + } + + // Hold a reference to the old data so it doesn't get garbage collected. + // We must protect it until the new data has been delivered. + List<TunnelController> oldData = mData; + mData = data; + + if (isStarted()) { + // If the Loader is in a started state, have the superclass deliver the + // results to the client. + super.deliverResult(data); + } + + // Invalidate the old data as we don't need it any more. + if (oldData != null && oldData != data) { + releaseResources(oldData); + } + } + + @Override + protected void onStartLoading() { + if (mData != null) { + // Deliver any previously loaded data immediately. + deliverResult(mData); + } + + // Begin monitoring the underlying data source. + mMonitor = new TunnelControllerMonitor(); + mHandler.postDelayed(mMonitor, 50); + + if (takeContentChanged() || mData == null) { + // When the observer detects a change, it should call onContentChanged() + // on the Loader, which will cause the next call to takeContentChanged() + // to return true. If this is ever the case (or if the current data is + // null), we force a new load. + forceLoad(); + } + } + + @Override + protected void onStopLoading() { + // The Loader is in a stopped state, so we should attempt to cancel the + // current load (if there is one). + cancelLoad(); + + // Note that we leave the observer as is. Loaders in a stopped state + // should still monitor the data source for changes so that the Loader + // will know to force a new load if it is ever started again. + } + + @Override + protected void onReset() { + // Ensure the loader has been stopped. + onStopLoading(); + + // At this point we can release the resources associated with 'mData'. + if (mData != null) { + releaseResources(mData); + mData = null; + } + + // The Loader is being reset, so we should stop monitoring for changes. + if (mMonitor != null) { + mHandler.removeCallbacks(mMonitor); + mMonitor = null; + } + } + + @Override + public void onCanceled(List<TunnelController> data) { + // Attempt to cancel the current asynchronous load. + super.onCanceled(data); + + // The load has been canceled, so we should release the resources + // associated with 'data'. + releaseResources(data); + } + + private void releaseResources(List<TunnelController> data) { + // For a simple List, there is nothing to do. For something like a Cursor, we + // would close it in this method. All resources associated with the Loader + // should be released here. + } + + private class TunnelControllerMonitor implements Runnable { + public void run() { + // There is no way (yet) to monitor for changes to the list of + // TunnelControllers, so just force a refresh every 10 seconds. + onContentChanged(); + mHandler.postDelayed(this, 10 * 1000); + } + } +} -- GitLab