From 2a15994a76b58b74eadc609a8e9704cf704cfa30 Mon Sep 17 00:00:00 2001 From: str4d <str4d@mail.i2p> Date: Mon, 11 Nov 2013 20:23:07 +0000 Subject: [PATCH] Load real stats into NetDb ViewPager getRouterContext() returns null in onActivityCreated(), there is a race condition to fix somewhere, use refresh action to load. --- .../fragment/NetDbSummaryPagerFragment.java | 85 ++++++++- .../fragment/NetDbSummaryTableFragment.java | 39 +++- .../router/loader/NetDbStatsLoader.java | 178 ++++++++++++++++++ 3 files changed, 293 insertions(+), 9 deletions(-) create mode 100644 src/net/i2p/android/router/loader/NetDbStatsLoader.java diff --git a/src/net/i2p/android/router/fragment/NetDbSummaryPagerFragment.java b/src/net/i2p/android/router/fragment/NetDbSummaryPagerFragment.java index 872e782d3..c5c5e4813 100644 --- a/src/net/i2p/android/router/fragment/NetDbSummaryPagerFragment.java +++ b/src/net/i2p/android/router/fragment/NetDbSummaryPagerFragment.java @@ -1,19 +1,36 @@ package net.i2p.android.router.fragment; +import java.util.List; + import net.i2p.android.router.R; +import net.i2p.android.router.loader.NetDbStatsLoader; +import net.i2p.android.router.util.Util; +import net.i2p.util.ObjectCounter; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.Loader; import android.support.v4.view.ViewPager; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -public class NetDbSummaryPagerFragment extends Fragment { +public class NetDbSummaryPagerFragment extends I2PFragmentBase implements + LoaderManager.LoaderCallbacks<List<ObjectCounter<String>>> { NetDbPagerAdapter mNetDbPagerAdapter; ViewPager mViewPager; + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.parentfragment_viewpager, container, false); @@ -39,19 +56,66 @@ public class NetDbSummaryPagerFragment extends Fragment { }); } + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + LoaderManager lm = getLoaderManager(); + // If the Router is running, or there is an existing Loader + if (getRouterContext() != null || lm.getLoader(0) != null) { + lm.initLoader(0, null, this); + } else { + // Router is not running or is not bound yet. + Util.i("Router not running or not bound to NetDbSummaryPagerFragment"); + } + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.fragment_netdb_list_actions, menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle presses on the action bar items + switch (item.getItemId()) { + case R.id.action_refresh: + Util.i("Refresh called, restarting Loader"); + mNetDbPagerAdapter.setData(null); + mViewPager.invalidate(); + getLoaderManager().restartLoader(0, null, this); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + public class NetDbPagerAdapter extends FragmentStatePagerAdapter { + private List<ObjectCounter<String>> mData; + public NetDbPagerAdapter(FragmentManager fm) { super(fm); } + public void setData(List<ObjectCounter<String>> data) { + mData = data; + notifyDataSetChanged(); + } + @Override public Fragment getItem(int i) { - return NetDbSummaryTableFragment.newInstance(i); + if (mData == null) + return null; + + return NetDbSummaryTableFragment.newInstance(i, mData.get(i)); } @Override public int getCount() { - return 3; + if (mData == null) + return 0; + else + return 3; } @Override @@ -66,4 +130,19 @@ public class NetDbSummaryPagerFragment extends Fragment { } } } + + // LoaderManager.LoaderCallbacks<List<ObjectCounter<String>>> + + public Loader<List<ObjectCounter<String>>> onCreateLoader(int id, Bundle args) { + return new NetDbStatsLoader(getActivity(), getRouterContext()); + } + + public void onLoadFinished(Loader<List<ObjectCounter<String>>> loader, + List<ObjectCounter<String>> data) { + mNetDbPagerAdapter.setData(data); + } + + public void onLoaderReset(Loader<List<ObjectCounter<String>>> loader) { + mNetDbPagerAdapter.setData(null); + } } diff --git a/src/net/i2p/android/router/fragment/NetDbSummaryTableFragment.java b/src/net/i2p/android/router/fragment/NetDbSummaryTableFragment.java index ee01e4698..6c8dda327 100644 --- a/src/net/i2p/android/router/fragment/NetDbSummaryTableFragment.java +++ b/src/net/i2p/android/router/fragment/NetDbSummaryTableFragment.java @@ -1,6 +1,12 @@ package net.i2p.android.router.fragment; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import net.i2p.android.router.R; +import net.i2p.util.ObjectCounter; +import net.i2p.util.VersionComparator; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; @@ -12,14 +18,18 @@ import android.widget.TextView; public class NetDbSummaryTableFragment extends Fragment { private static final String CATEGORY = "category"; + private static final String COUNTS = "counts"; private int mCategory; + private ObjectCounter<String> mCounts; private TableLayout mTable; - public static NetDbSummaryTableFragment newInstance(int category) { + public static NetDbSummaryTableFragment newInstance(int category, + ObjectCounter<String> counts) { NetDbSummaryTableFragment f = new NetDbSummaryTableFragment(); Bundle args = new Bundle(); args.putInt(CATEGORY, category); + args.putSerializable(COUNTS, counts); f.setArguments(args); return f; } @@ -29,13 +39,30 @@ public class NetDbSummaryTableFragment extends Fragment { View v = inflater.inflate(R.layout.fragment_table, container, false); mCategory = getArguments().getInt(CATEGORY); + mCounts = (ObjectCounter<String>) getArguments().getSerializable(COUNTS); mTable = (TableLayout) v.findViewById(R.id.table); - createTableTitle(); - addTableRow("foo", "123"); - addTableRow("bar", "45"); - if (mCategory == 2) - addTableRow("bing", "67"); + + List<String> objects = new ArrayList<String>(mCounts.objects()); + if (!objects.isEmpty()) { + createTableTitle(); + + switch (mCategory) { + case 1: + case 2: + Collections.sort(objects); + break; + default: + Collections.sort(objects, + Collections.reverseOrder(new VersionComparator())); + break; + } + + for (String object : objects) { + int num = mCounts.count(object); + addTableRow(object, ""+num); + } + } return v; } diff --git a/src/net/i2p/android/router/loader/NetDbStatsLoader.java b/src/net/i2p/android/router/loader/NetDbStatsLoader.java new file mode 100644 index 000000000..aadfa68d7 --- /dev/null +++ b/src/net/i2p/android/router/loader/NetDbStatsLoader.java @@ -0,0 +1,178 @@ +package net.i2p.android.router.loader; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import net.i2p.data.Hash; +import net.i2p.data.RouterAddress; +import net.i2p.data.RouterInfo; +import net.i2p.router.RouterContext; +import net.i2p.util.ObjectCounter; +import android.content.Context; +import android.support.v4.content.AsyncTaskLoader; + +public class NetDbStatsLoader extends AsyncTaskLoader<List<ObjectCounter<String>>> { + private RouterContext mRContext; + private List<ObjectCounter<String>> mData; + + public NetDbStatsLoader(Context context, RouterContext rContext) { + super(context); + mRContext = rContext; + } + + private static class RouterInfoComparator implements Comparator<RouterInfo> { + public int compare(RouterInfo l, RouterInfo r) { + return l.getHash().toBase64().compareTo(r.getHash().toBase64()); + } + } + + @Override + public List<ObjectCounter<String>> loadInBackground() { + List<ObjectCounter<String>> ret = new ArrayList<ObjectCounter<String>>(); + + ObjectCounter<String> versions = new ObjectCounter<String>(); + ObjectCounter<String> countries = new ObjectCounter<String>(); + ObjectCounter<String> transports = new ObjectCounter<String>(); + + if (mRContext.netDb().isInitialized()) { + Hash us = mRContext.routerHash(); + + Set<RouterInfo> routers = new TreeSet<RouterInfo>(new RouterInfoComparator()); + routers.addAll(mRContext.netDb().getRouters()); + for (RouterInfo ri : routers) { + Hash key = ri.getHash(); + if (!key.equals(us)) { + String routerVersion = ri.getOption("router.version"); + if (routerVersion != null) + versions.increment(routerVersion); + String country = mRContext.commSystem().getCountry(key); + if(country != null) + countries.increment(country); + transports.increment(classifyTransports(ri)); + } + } + } + + ret.add(versions); + ret.add(countries); + ret.add(transports); + + return ret; + } + + private static final int SSU = 1; + private static final int SSUI = 2; + private static final int NTCP = 4; + private static final int IPV6 = 8; + private static final String[] TNAMES = { "Hidden or starting up", "SSU", "SSU with introducers", "", + "NTCP", "NTCP and SSU", "NTCP and SSU with introducers", "", + "", "IPv6 SSU", "IPv6 Only SSU, introducers", "IPv6 SSU, introducers", + "IPv6 NTCP", "IPv6 NTCP, SSU", "IPv6 Only NTCP, SSU, introducers", "IPv6 NTCP, SSU, introducers" }; + /** + * what transport types + */ + private static String classifyTransports(RouterInfo info) { + int rv = 0; + for (RouterAddress addr : info.getAddresses()) { + String style = addr.getTransportStyle(); + if (style.equals("NTCP")) { + rv |= NTCP; + } else if (style.equals("SSU")) { + if (addr.getOption("iport0") != null) + rv |= SSUI; + else + rv |= SSU; + } + String host = addr.getHost(); + if (host != null && host.contains(":")) + rv |= IPV6; + + } + return TNAMES[rv]; + } + + @Override + public void deliverResult(List<ObjectCounter<String>> 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<ObjectCounter<String>> 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); + } + + 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; + } + } + + @Override + public void onCanceled(List<ObjectCounter<String>> 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<ObjectCounter<String>> 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. + } +} -- GitLab