I2P Address: [http://git.idk.i2p]

Skip to content
Snippets Groups Projects
Commit c03debf3 authored by str4d's avatar str4d
Browse files

Implemented Entry/Loader/Adapter for NetDbListFragment

parent fbc56d4e
No related branches found
No related tags found
No related merge requests found
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5dp" >
<!-- The nickname of the LeaseSet -->
<TextView android:id="@+id/ls_nickname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="3dp"
android:textSize="16sp"
android:layout_alignParentLeft="true"
android:layout_marginRight="5dp"
android:text="LeaseSet nickname" />
<!-- The hash of the LeaseSet -->
<TextView android:id="@+id/ls_hash"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/ls_nickname"
android:text="LeaseSet hash" />
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5dp" >
<!-- The hash of the RouterInfo -->
<TextView android:id="@+id/ri_hash"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="3dp"
android:textSize="16sp"
android:layout_alignParentLeft="true"
android:layout_marginRight="5dp"
android:text="RouterInfo hash" />
<!-- Country flag -->
<ImageView android:id="@+id/ri_country"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:contentDescription="Country" />
</RelativeLayout>
...@@ -45,8 +45,13 @@ ...@@ -45,8 +45,13 @@
<string name="action_reload">Reload</string> <string name="action_reload">Reload</string>
<string name="hint_search_addressbook">Search addressbook</string> <string name="hint_search_addressbook">Search addressbook</string>
<string name="router_not_running">The router is not running.</string>
<string name="graphs_not_ready">Graphs are not ready, or the router is not running. Try again later.</string> <string name="graphs_not_ready">Graphs are not ready, or the router is not running. Try again later.</string>
<string name="netdb_routers_empty">No routers in your NetDB.</string>
<string name="netdb_leases_empty">No LeaseSets in your NetDB.</string>
<string name="menu_settings">Settings</string> <string name="menu_settings">Settings</string>
<string name="settings_enable">Enable</string> <string name="settings_enable">Enable</string>
<string name="settings_desc_subscriptions">Subscription URLs</string> <string name="settings_desc_subscriptions">Subscription URLs</string>
......
...@@ -3,13 +3,16 @@ package net.i2p.android.router.activity; ...@@ -3,13 +3,16 @@ package net.i2p.android.router.activity;
import net.i2p.android.router.R; import net.i2p.android.router.R;
import net.i2p.android.router.fragment.NetDbListFragment; import net.i2p.android.router.fragment.NetDbListFragment;
import net.i2p.android.router.fragment.NetDbSummaryPagerFragment; import net.i2p.android.router.fragment.NetDbSummaryPagerFragment;
import net.i2p.android.router.loader.NetDbEntry;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction; import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBar.Tab; import android.support.v7.app.ActionBar.Tab;
public class NetDbActivity extends I2PActivityBase { public class NetDbActivity extends I2PActivityBase implements
NetDbListFragment.RouterContextProvider,
NetDbListFragment.OnEntrySelectedListener {
/** /**
* Whether or not the activity is in two-pane mode, i.e. running on a tablet * Whether or not the activity is in two-pane mode, i.e. running on a tablet
* device. * device.
...@@ -72,8 +75,8 @@ public class NetDbActivity extends I2PActivityBase { ...@@ -72,8 +75,8 @@ public class NetDbActivity extends I2PActivityBase {
// In two-pane mode, list items should be given the // In two-pane mode, list items should be given the
// 'activated' state when touched. // 'activated' state when touched.
//rf.setActivateOnItemClick(true); rf.setActivateOnItemClick(true);
//lf.setActivateOnItemClick(true); lf.setActivateOnItemClick(true);
} }
} }
...@@ -102,4 +105,9 @@ public class NetDbActivity extends I2PActivityBase { ...@@ -102,4 +105,9 @@ public class NetDbActivity extends I2PActivityBase {
super.onTabSelected(tab, ft); super.onTabSelected(tab, ft);
} }
} }
// NetDbListFragment.OnEntrySelectedListener
public void onEntrySelected(NetDbEntry entry) {
}
} }
package net.i2p.android.router.adapter;
import java.util.List;
import net.i2p.android.router.R;
import net.i2p.android.router.loader.NetDbEntry;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class NetDbEntryAdapter extends ArrayAdapter<NetDbEntry> {
private final LayoutInflater mInflater;
public NetDbEntryAdapter(Context context) {
super(context, android.R.layout.simple_list_item_2);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void setData(List<NetDbEntry> entries) {
clear();
if (entries != null) {
for (NetDbEntry entry : entries) {
add(entry);
}
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v;
NetDbEntry entry = getItem(position);
if (entry.isRouterInfo()) {
v = mInflater.inflate(R.layout.listitem_routerinfo, parent, false);
TextView hash = (TextView) v.findViewById(R.id.ri_hash);
hash.setText(entry.getHash());
int countryIcon = entry.getCountryIcon();
if (countryIcon > 0) {
ImageView country = (ImageView) v.findViewById(R.id.ri_country);
country.setImageDrawable(getContext().getResources()
.getDrawable(countryIcon));
}
} else {
v = mInflater.inflate(R.layout.listitem_leaseset, parent, false);
TextView hash = (TextView) v.findViewById(R.id.ls_hash);
hash.setText(entry.getHash());
TextView nickname = (TextView) v.findViewById(R.id.ls_nickname);
nickname.setText(entry.getNickname());
}
return v;
}
}
package net.i2p.android.router.fragment; package net.i2p.android.router.fragment;
import android.support.v4.app.Fragment; import java.util.List;
public class NetDbListFragment extends Fragment { 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.router.RouterContext;
import android.app.Activity;
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.View;
import android.widget.ListView;
public class NetDbListFragment extends ListFragment
implements LoaderManager.LoaderCallbacks<List<NetDbEntry>> {
public static final String SHOW_ROUTERS = "show_routers"; public static final String SHOW_ROUTERS = "show_routers";
private static final int ROUTER_LOADER_ID = 1;
private static final int LEASESET_LOADER_ID = 2;
/**
* The serialization (saved instance state) Bundle key representing the
* activated item position. Only used on tablets.
*/
private static final String STATE_ACTIVATED_POSITION = "activated_position";
RouterContextProvider mRouterContextProvider;
OnEntrySelectedListener mEntrySelectedCallback;
private NetDbEntryAdapter mAdapter;
private boolean mRouters;
/**
* The current activated item position. Only used on tablets.
*/
private int mActivatedPosition = ListView.INVALID_POSITION;
private boolean mActivateOnItemClick = false;
// Container Activity must implement this interface
public interface RouterContextProvider {
public RouterContext getRouterContext();
}
// Container Activity must implement this interface
public interface OnEntrySelectedListener {
public void onEntrySelected(NetDbEntry entry);
}
@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 {
mRouterContextProvider = (RouterContextProvider) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement RouterContextProvider");
}
// 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 void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// Restore the previously serialized activated item position.
if (savedInstanceState != null
&& savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
setActivatedPosition(savedInstanceState
.getInt(STATE_ACTIVATED_POSITION));
}
// When setting CHOICE_MODE_SINGLE, ListView will automatically
// give items the 'activated' state when touched.
getListView().setChoiceMode(
mActivateOnItemClick ? ListView.CHOICE_MODE_SINGLE
: ListView.CHOICE_MODE_NONE);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mAdapter = new NetDbEntryAdapter(getActivity());
mRouters = getArguments().getBoolean(SHOW_ROUTERS);
setListAdapter(mAdapter);
if (getRouterContext() == null) {
setEmptyText(getResources().getString(
R.string.router_not_running));
} else {
setEmptyText(getResources().getString((mRouters ?
R.string.netdb_routers_empty :
R.string.netdb_leases_empty)));
setListShown(false);
getLoaderManager().initLoader(mRouters ? ROUTER_LOADER_ID
: LEASESET_LOADER_ID, null, this);
}
}
@Override
public void onListItemClick(ListView parent, View view, int pos, long id) {
super.onListItemClick(parent, view, pos, id);
mEntrySelectedCallback.onEntrySelected(mAdapter.getItem(pos));
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mActivatedPosition != ListView.INVALID_POSITION) {
// Serialize and persist the activated item position.
outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
}
}
/**
* Turns on activate-on-click mode. When this mode is on, list items will be
* given the 'activated' state when touched.
*/
public void setActivateOnItemClick(boolean activateOnItemClick) {
mActivateOnItemClick = activateOnItemClick;
}
private void setActivatedPosition(int position) {
if (position == ListView.INVALID_POSITION) {
getListView().setItemChecked(mActivatedPosition, false);
} else {
getListView().setItemChecked(position, true);
}
mActivatedPosition = position;
}
// Duplicated from I2PFragmentBase because this extends ListFragment
private RouterContext getRouterContext() {
return mRouterContextProvider.getRouterContext();
}
// LoaderManager.LoaderCallbacks<List<NetDbEntry>>
public Loader<List<NetDbEntry>> onCreateLoader(int id, Bundle args) {
return new NetDbEntryLoader(getActivity(),
getRouterContext(), mRouters);
}
public void onLoadFinished(Loader<List<NetDbEntry>> loader,
List<NetDbEntry> data) {
if (loader.getId() == (mRouters ?
ROUTER_LOADER_ID : LEASESET_LOADER_ID)) {
mAdapter.setData(data);
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
}
public void onLoaderReset(Loader<List<NetDbEntry>> loader) {
if (loader.getId() == (mRouters ?
ROUTER_LOADER_ID : LEASESET_LOADER_ID)) {
mAdapter.setData(null);
}
}
} }
package net.i2p.android.router.loader;
import java.lang.reflect.Field;
import net.i2p.android.router.R;
import net.i2p.data.LeaseSet;
import net.i2p.data.RouterInfo;
public class NetDbEntry {
private final boolean mIsRI;
private final RouterInfo mRI;
private final String mCountry;
private final LeaseSet mLS;
private final String mNick;
public static NetDbEntry fromRouterInfo(RouterInfo ri, String country) {
return new NetDbEntry(true, ri, country, null, "");
}
public static NetDbEntry fromLeaseSet(LeaseSet ls, String nick) {
return new NetDbEntry(false, null, "", ls, nick);
}
public NetDbEntry(boolean isRI,
RouterInfo ri, String country,
LeaseSet ls, String nick) {
mIsRI = isRI;
mRI = ri;
mCountry = country;
mLS = ls;
mNick = nick;
}
public boolean isRouterInfo() {
return mIsRI;
}
public RouterInfo getRouterInfo() {
return mRI;
}
public LeaseSet getLeaseSet() {
return mLS;
}
public String getHash() {
if (mIsRI) {
return mRI.getIdentity().getHash().toBase64();
} else {
return mLS.getDestination().calculateHash().toBase64();
}
}
public int getCountryIcon() {
// http://daniel-codes.blogspot.com/2009/12/dynamically-retrieving-resources-in.html
try {
Class res = R.drawable.class;
Field field = res.getField("flag_" + mCountry);
return field.getInt(null);
}
catch (Exception e) {
return 0;
}
}
public String getNickname() {
return mNick;
}
}
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.Destination;
import net.i2p.data.LeaseSet;
import net.i2p.data.RouterInfo;
import net.i2p.router.RouterContext;
import net.i2p.router.TunnelPoolSettings;
import android.content.Context;
import android.os.Handler;
import android.support.v4.content.AsyncTaskLoader;
public class NetDbEntryLoader extends AsyncTaskLoader<List<NetDbEntry>> {
private RouterContext mRContext;
private boolean mRouters;
private List<NetDbEntry> mData;
private Handler mHandler;
private NetDbMonitor mMonitor;
public NetDbEntryLoader(Context context, RouterContext rContext, boolean routers) {
super(context);
mRContext = rContext;
mRouters = routers;
mHandler = new Handler();
}
private static class RouterInfoComparator implements Comparator<RouterInfo> {
public int compare(RouterInfo l, RouterInfo r) {
return l.getIdentity().getHash().toBase64().compareTo(r.getIdentity().getHash().toBase64());
}
}
private class LeaseSetComparator implements Comparator<LeaseSet> {
public int compare(LeaseSet l, LeaseSet r) {
Destination dl = l.getDestination();
Destination dr = r.getDestination();
boolean locall = mRContext.clientManager().isLocal(dl);
boolean localr = mRContext.clientManager().isLocal(dr);
if (locall && !localr) return -1;
if (localr && !locall) return 1;
return dl.calculateHash().toBase64().compareTo(dr.calculateHash().toBase64());
}
}
@Override
public List<NetDbEntry> loadInBackground() {
List<NetDbEntry> ret = new ArrayList<NetDbEntry>();
if (mRContext.netDb().isInitialized()) {
if (mRouters) {
Set<RouterInfo> routers = new TreeSet<RouterInfo>(new RouterInfoComparator());
routers.addAll(mRContext.netDb().getRouters());
for (RouterInfo ri : routers) {
String country = mRContext.commSystem().getCountry(ri.getIdentity().getHash());
NetDbEntry entry = NetDbEntry.fromRouterInfo(ri, country);
ret.add(entry);
}
} else {
Set<LeaseSet> leases = new TreeSet<LeaseSet>(new LeaseSetComparator());
leases.addAll(mRContext.netDb().getLeases());
for (LeaseSet ls : leases) {
String nick;
Destination dest = ls.getDestination();
if (mRContext.clientManager().isLocal(dest)) {
TunnelPoolSettings in = mRContext.tunnelManager().getInboundSettings(
dest.calculateHash());
if (in != null && in.getDestinationNickname() != null)
nick = in.getDestinationNickname();
else
nick = dest.toBase64().substring(0, 6);
} else {
String host = mRContext.namingService().reverseLookup(dest);
if (host != null)
nick = host;
else
nick = dest.toBase64().substring(0, 6);
}
NetDbEntry entry = NetDbEntry.fromLeaseSet(ls, nick);
ret.add(entry);
}
}
}
return ret;
}
@Override
public void deliverResult(List<NetDbEntry> 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<NetDbEntry> 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 NetDbMonitor();
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<NetDbEntry> 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<NetDbEntry> 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 NetDbMonitor implements Runnable {
public void run() {
// There is no way (yet) to monitor for changes to the NetDB,
// so just force a refresh every 10 seconds.
onContentChanged();
mHandler.postDelayed(this, 10 * 1000);
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment