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