From 8b51c26a6b41a5398c4abef170e40186b5a1a5fc Mon Sep 17 00:00:00 2001 From: str4d <str4d@mail.i2p> Date: Mon, 11 Nov 2013 10:02:52 +0000 Subject: [PATCH] Expanded NetDB detail pages to match /netdb --- res/layout/fragment_netdb_leaseset_detail.xml | 44 ++++- res/layout/fragment_netdb_router_detail.xml | 51 ++++- .../router/activity/NetDbActivity.java | 10 +- .../router/activity/NetDbDetailActivity.java | 16 +- .../router/fragment/NetDbDetailFragment.java | 186 +++++++++++++++++- .../router/fragment/NetDbListFragment.java | 7 +- .../i2p/android/router/loader/NetDbEntry.java | 56 +++++- 7 files changed, 342 insertions(+), 28 deletions(-) diff --git a/res/layout/fragment_netdb_leaseset_detail.xml b/res/layout/fragment_netdb_leaseset_detail.xml index 93575eb7d..87bbdddd9 100644 --- a/res/layout/fragment_netdb_leaseset_detail.xml +++ b/res/layout/fragment_netdb_leaseset_detail.xml @@ -14,11 +14,53 @@ android:textAppearance="?android:attr/textAppearanceMedium" /> <TextView - android:id="@+id/dbentry_hash" + android:id="@+id/ls_type" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_below="@+id/ls_nickname" + android:text="Destination" /> + + <TextView + android:id="@+id/dbentry_hash" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_below="@+id/ls_type" android:text="LeaseSet hash" /> + <TextView + android:id="@+id/label_ls_expiry" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_below="@+id/dbentry_hash" + android:text="Expires in:" /> + + <TextView + android:id="@+id/ls_expiry" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBaseline="@+id/label_ls_expiry" + android:layout_alignBottom="@+id/label_ls_expiry" + android:layout_alignParentRight="true" + android:text="X min" /> + + <TextView + android:id="@+id/label_leases" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_below="@+id/label_ls_expiry" + android:text="Leases:" /> + + <LinearLayout + android:id="@+id/ls_leases" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_below="@+id/label_leases" + android:orientation="vertical" > + </LinearLayout> + </RelativeLayout> diff --git a/res/layout/fragment_netdb_router_detail.xml b/res/layout/fragment_netdb_router_detail.xml index f0eb57eb1..938cee4f2 100644 --- a/res/layout/fragment_netdb_router_detail.xml +++ b/res/layout/fragment_netdb_router_detail.xml @@ -10,4 +10,53 @@ android:layout_alignParentLeft="true" android:text="Router hash" /> -</RelativeLayout> + <TextView + android:id="@+id/label_ri_published" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_below="@+id/dbentry_hash" + android:text="Published:" /> + + <TextView + android:id="@+id/ri_published" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBaseline="@+id/label_ri_published" + android:layout_alignBottom="@+id/label_ri_published" + android:layout_alignParentRight="true" + android:text="X ago" /> + + <TextView + android:id="@+id/label_ri_addresses" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_below="@+id/label_ri_published" + android:text="Address(es):" /> + + <LinearLayout + android:id="@+id/ri_addresses" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_below="@+id/label_ri_addresses" + android:orientation="vertical" > + </LinearLayout> + + <TextView + android:id="@+id/label_ri_stats" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_below="@+id/ri_addresses" + android:text="Stats:" /> + + <TableLayout + android:id="@+id/ri_stats" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_below="@+id/label_ri_stats" /> + +</RelativeLayout> \ No newline at end of file diff --git a/src/net/i2p/android/router/activity/NetDbActivity.java b/src/net/i2p/android/router/activity/NetDbActivity.java index e52256482..c4b79ca44 100644 --- a/src/net/i2p/android/router/activity/NetDbActivity.java +++ b/src/net/i2p/android/router/activity/NetDbActivity.java @@ -4,7 +4,7 @@ import net.i2p.android.router.R; import net.i2p.android.router.fragment.NetDbDetailFragment; import net.i2p.android.router.fragment.NetDbListFragment; import net.i2p.android.router.fragment.NetDbSummaryPagerFragment; -import net.i2p.android.router.loader.NetDbEntry; +import net.i2p.data.Hash; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -110,22 +110,22 @@ public class NetDbActivity extends I2PActivityBase implements // NetDbListFragment.OnEntrySelectedListener - public void onEntrySelected(NetDbEntry entry) { + public void onEntrySelected(boolean isRouterInfo, Hash entryHash) { if (mTwoPane) { // In two-pane mode, show the detail view in this activity by // adding or replacing the detail fragment using a // fragment transaction. NetDbDetailFragment detailFrag = NetDbDetailFragment.newInstance( - entry.isRouterInfo(), entry.getHash()); + isRouterInfo, entryHash); getSupportFragmentManager().beginTransaction() .replace(R.id.detail_fragment, detailFrag).commit(); } else { // In single-pane mode, simply start the detail activity // for the selected item ID. Intent detailIntent = new Intent(this, NetDbDetailActivity.class); - detailIntent.putExtra(NetDbDetailFragment.IS_RI, entry.isRouterInfo()); + detailIntent.putExtra(NetDbDetailFragment.IS_RI, isRouterInfo); detailIntent.putExtra(NetDbDetailFragment.ENTRY_HASH, - entry.getHash().toBase64()); + entryHash.toBase64()); startActivity(detailIntent); } } diff --git a/src/net/i2p/android/router/activity/NetDbDetailActivity.java b/src/net/i2p/android/router/activity/NetDbDetailActivity.java index 62c8f7d1c..b670dcc49 100644 --- a/src/net/i2p/android/router/activity/NetDbDetailActivity.java +++ b/src/net/i2p/android/router/activity/NetDbDetailActivity.java @@ -2,13 +2,16 @@ package net.i2p.android.router.activity; import net.i2p.android.router.R; import net.i2p.android.router.fragment.NetDbDetailFragment; +import net.i2p.android.router.fragment.NetDbListFragment; import net.i2p.android.router.service.RouterService; import net.i2p.android.router.util.Util; import net.i2p.data.DataFormatException; import net.i2p.data.Hash; +import android.content.Intent; import android.os.Bundle; -public class NetDbDetailActivity extends I2PActivityBase { +public class NetDbDetailActivity extends I2PActivityBase implements + NetDbListFragment.OnEntrySelectedListener { NetDbDetailFragment mDetailFrag; @Override @@ -34,4 +37,15 @@ public class NetDbDetailActivity extends I2PActivityBase { protected void onRouterBind(RouterService svc) { mDetailFrag.onRouterBind(); } + + // NetDbListFragment.OnEntrySelectedListener + + public void onEntrySelected(boolean isRouterInfo, Hash entryHash) { + // Start the detail activity for the selected item ID. + Intent detailIntent = new Intent(this, NetDbDetailActivity.class); + detailIntent.putExtra(NetDbDetailFragment.IS_RI, isRouterInfo); + detailIntent.putExtra(NetDbDetailFragment.ENTRY_HASH, + entryHash.toBase64()); + startActivity(detailIntent); + } } diff --git a/src/net/i2p/android/router/fragment/NetDbDetailFragment.java b/src/net/i2p/android/router/fragment/NetDbDetailFragment.java index f9f7b6bf0..0b1fac803 100644 --- a/src/net/i2p/android/router/fragment/NetDbDetailFragment.java +++ b/src/net/i2p/android/router/fragment/NetDbDetailFragment.java @@ -1,22 +1,36 @@ package net.i2p.android.router.fragment; +import java.util.Map; +import java.util.Set; + import net.i2p.android.router.R; +import net.i2p.android.router.fragment.NetDbListFragment.OnEntrySelectedListener; import net.i2p.android.router.loader.NetDbEntry; import net.i2p.android.router.util.Util; import net.i2p.data.DataFormatException; +import net.i2p.data.DataHelper; import net.i2p.data.Hash; +import net.i2p.data.Lease; import net.i2p.data.LeaseSet; +import net.i2p.data.RouterAddress; import net.i2p.data.RouterInfo; +import android.app.Activity; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; +import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TableLayout; +import android.widget.TableRow; import android.widget.TextView; public class NetDbDetailFragment extends I2PFragmentBase { public static final String IS_RI = "is_routerinfo"; public static final String ENTRY_HASH = "entry_hash"; + OnEntrySelectedListener mEntrySelectedCallback; private NetDbEntry mEntry; public static NetDbDetailFragment newInstance(boolean isRI, Hash hash) { @@ -28,6 +42,21 @@ public class NetDbDetailFragment extends I2PFragmentBase { return f; } + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + // This makes sure that the container activity has implemented + // the callback interface. If not, it throws an exception + try { + mEntrySelectedCallback = (OnEntrySelectedListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + + " must implement OnEntrySelectedListener"); + } + + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -60,21 +89,162 @@ public class NetDbDetailFragment extends I2PFragmentBase { try { hash.fromBase64(getArguments().getString(ENTRY_HASH)); if (getArguments().getBoolean(IS_RI)) { + // Load RouterInfo RouterInfo ri = getNetDb().lookupRouterInfoLocally(hash); - mEntry = NetDbEntry.fromRouterInfo(getRouterContext(), ri); + loadRouterInfo(ri); } else { + // Load LeaseSet LeaseSet ls = getNetDb().lookupLeaseSetLocally(hash); - mEntry = NetDbEntry.fromLeaseSet(getRouterContext(), ls); - - TextView nickname = (TextView) getView().findViewById(R.id.ls_nickname); - nickname.setText(mEntry.getNickname()); + loadLeaseSet(ls); } - - TextView entryHash = (TextView) getView().findViewById(R.id.dbentry_hash); - entryHash.setText(hash.toBase64()); } catch (DataFormatException e) { Util.e(e.toString()); } } } + + private void loadRouterInfo(RouterInfo ri) { + mEntry = NetDbEntry.fromRouterInfo(getRouterContext(), ri); + + if (mEntry.isUs()) + getActivity().setTitle("Our info"); + else + getActivity().setTitle("Peer info"); + + TextView entryHash = (TextView) getView().findViewById(R.id.dbentry_hash); + entryHash.setText(mEntry.getHash().toBase64()); + + if (mEntry.isUs() && getRouter().isHidden()) { + TextView pubLabel = (TextView) getView().findViewById(R.id.label_ri_published); + pubLabel.setText("Hidden, Updated:"); + } + + TextView published = (TextView) getView().findViewById(R.id.ri_published); + long age = getRouterContext().clock().now() - ri.getPublished(); + if (age > 0) { + published.setText(DataHelper.formatDuration(age) + " ago"); + } else { + // shouldn't happen + published.setText(DataHelper.formatDuration(0-age) + " ago???"); + } + + LinearLayout addresses = (LinearLayout) getView().findViewById(R.id.ri_addresses); + for (RouterAddress addr : ri.getAddresses()) { + addAddress(addresses, addr); + } + + TableLayout stats = (TableLayout) getView().findViewById(R.id.ri_stats); + Map<String, String> p = ri.getOptionsMap(); + for (Map.Entry<String,String> e : (Set<Map.Entry<String,String>>) p.entrySet()) { + String key = e.getKey(); + String val = e.getValue(); + addTableRow(stats, DataHelper.stripHTML(key), DataHelper.stripHTML(val)); + } + } + + private void addAddress(LinearLayout addresses, RouterAddress addr) { + TableLayout table = new TableLayout(getActivity()); + + String style = addr.getTransportStyle(); + addTableRow(table, "Style", style); + + int cost = addr.getCost(); + if (!((style.equals("SSU") && cost == 5) || (style.equals("NTCP") && cost == 10))) + addTableRow(table, "cost", ""+cost); + + Map<String, String> p = addr.getOptionsMap(); + for (Map.Entry<String,String> e : (Set<Map.Entry<String,String>>) p.entrySet()) { + String key = e.getKey(); + String val = e.getValue(); + addTableRow(table, DataHelper.stripHTML(key), DataHelper.stripHTML(val)); + } + + addresses.addView(table); + } + + private void loadLeaseSet(LeaseSet ls) { + mEntry = NetDbEntry.fromLeaseSet(getRouterContext(), ls); + + getActivity().setTitle("LeaseSet"); + + TextView nickname = (TextView) getView().findViewById(R.id.ls_nickname); + nickname.setText(mEntry.getNickname()); + + TextView type = (TextView) getView().findViewById(R.id.ls_type); + if (mEntry.isLocal()) { + if (mEntry.isUnpublished()) + type.setText("Local Unpublished Destination"); + else + type.setText("Local Destination"); + } + + TextView entryHash = (TextView) getView().findViewById(R.id.dbentry_hash); + entryHash.setText(mEntry.getHash().toBase64()); + + TextView expiry = (TextView) getView().findViewById(R.id.ls_expiry); + long exp = ls.getLatestLeaseDate() - getRouterContext().clock().now(); + if (exp > 0) { + expiry.setText(DataHelper.formatDuration(exp)); + } else { + TextView expiryLabel = (TextView) getView().findViewById(R.id.label_ls_expiry); + expiryLabel.setText("Expired:"); + expiry.setText(DataHelper.formatDuration(exp) + " ago"); + } + + LinearLayout leases = (LinearLayout) getView().findViewById(R.id.ls_leases); + for (int i = 0; i < ls.getLeaseCount(); i++) { + Lease lease = ls.getLease(i); + addLease(leases, lease, i); + } + } + + private void addLease(LinearLayout leases, Lease lease, int i) { + TableLayout table = new TableLayout(getActivity()); + + addTableRow(table, "Lease", ""+(i+1)); + + TableRow gateway = new TableRow(getActivity()); + gateway.setPadding(10, 0, 0, 0); + + TextView gatewayLabel = new TextView(getActivity()); + gatewayLabel.setText("Gateway"); + + Button gatewayButton = new Button(getActivity()); + gatewayButton.setText(lease.getGateway().toBase64().substring(0, 4)); + final Hash gatewayHash = lease.getGateway(); + gatewayButton.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + mEntrySelectedCallback.onEntrySelected( + true, gatewayHash); + } + }); + + gateway.addView(gatewayLabel); + gateway.addView(gatewayButton); + + table.addView(gateway); + + addTableRow(table, "Tunnel", ""+lease.getTunnelId().getTunnelId()); + + leases.addView(table); + } + + private void addTableRow(TableLayout table, String key, String val) { + TableRow row; + TextView tl1, tl2; + + row = new TableRow(getActivity()); + row.setPadding(10, 0, 0, 0); + + tl1 = new TextView(getActivity()); + tl2 = new TextView(getActivity()); + + tl1.setText(key); + tl2.setText(val); + + row.addView(tl1); + row.addView(tl2); + + table.addView(row); + } } diff --git a/src/net/i2p/android/router/fragment/NetDbListFragment.java b/src/net/i2p/android/router/fragment/NetDbListFragment.java index 1328aabe4..4d336ce70 100644 --- a/src/net/i2p/android/router/fragment/NetDbListFragment.java +++ b/src/net/i2p/android/router/fragment/NetDbListFragment.java @@ -6,6 +6,7 @@ import net.i2p.android.router.R; import net.i2p.android.router.adapter.NetDbEntryAdapter; import net.i2p.android.router.loader.NetDbEntry; import net.i2p.android.router.loader.NetDbEntryLoader; +import net.i2p.data.Hash; import net.i2p.router.RouterContext; import android.app.Activity; @@ -48,7 +49,7 @@ public class NetDbListFragment extends ListFragment // Container Activity must implement this interface public interface OnEntrySelectedListener { - public void onEntrySelected(NetDbEntry entry); + public void onEntrySelected(boolean isRouterInfo, Hash entryHash); } @Override @@ -124,7 +125,9 @@ public class NetDbListFragment extends ListFragment @Override public void onListItemClick(ListView parent, View view, int pos, long id) { super.onListItemClick(parent, view, pos, id); - mEntrySelectedCallback.onEntrySelected(mAdapter.getItem(pos)); + NetDbEntry entry = mAdapter.getItem(pos); + mEntrySelectedCallback.onEntrySelected( + entry.isRouterInfo(), entry.getHash()); } @Override diff --git a/src/net/i2p/android/router/loader/NetDbEntry.java b/src/net/i2p/android/router/loader/NetDbEntry.java index 0328f7759..d37131d83 100644 --- a/src/net/i2p/android/router/loader/NetDbEntry.java +++ b/src/net/i2p/android/router/loader/NetDbEntry.java @@ -15,21 +15,31 @@ public class NetDbEntry { private final boolean mIsRI; private final DatabaseEntry mEntry; + private final boolean mIsUs; private final String mCountry; private final String mNick; + private final boolean mLocal; + private final boolean mUnpublished; public static NetDbEntry fromRouterInfo(RouterContext ctx, RouterInfo ri) { + Hash us = ctx.routerHash(); + boolean isUs = ri.getHash().equals(us); String country = ctx.commSystem().getCountry(ri.getIdentity().getHash()); - return new NetDbEntry(true, ri, country, ""); + return new NetDbEntry(ri, isUs, country); } public static NetDbEntry fromLeaseSet(RouterContext ctx, LeaseSet ls) { - String nick; + String nick = ""; + boolean local = false; + boolean unpublished = false; Destination dest = ls.getDestination(); + Hash key = dest.calculateHash(); if (ctx.clientManager().isLocal(dest)) { - TunnelPoolSettings in = ctx.tunnelManager().getInboundSettings( - dest.calculateHash()); + local = true; + if (! ctx.clientManager().shouldPublishLeaseSet(key)) + unpublished = true; + TunnelPoolSettings in = ctx.tunnelManager().getInboundSettings(key); if (in != null && in.getDestinationNickname() != null) nick = in.getDestinationNickname(); else @@ -41,18 +51,32 @@ public class NetDbEntry { else nick = dest.toBase64().substring(0, 6); } - return new NetDbEntry(false, ls, "", nick); + return new NetDbEntry(ls, nick, local, unpublished); } - public NetDbEntry(boolean isRI, DatabaseEntry entry, - String country, - String nick) { - mIsRI = isRI; - mEntry = entry; + public NetDbEntry(RouterInfo ri, + boolean isUs, String country) { + mIsRI = true; + mEntry = ri; + mIsUs = isUs; mCountry = country; + mNick = ""; + mLocal = mUnpublished = false; + } + + public NetDbEntry(LeaseSet ls, + String nick, boolean local, boolean unpublished) { + mIsRI = false; + mEntry = ls; + mNick = nick; + mLocal = local; + mUnpublished = unpublished; + + mIsUs = false; + mCountry = ""; } public boolean isRouterInfo() { @@ -67,6 +91,10 @@ public class NetDbEntry { // RouterInfo-specific methods + public boolean isUs() { + return mIsUs; + } + public int getCountryIcon() { // http://daniel-codes.blogspot.com/2009/12/dynamically-retrieving-resources-in.html try { @@ -84,4 +112,12 @@ public class NetDbEntry { public String getNickname() { return mNick; } + + public boolean isLocal() { + return mLocal; + } + + public boolean isUnpublished() { + return mUnpublished; + } } -- GitLab