I2P Android integration
This commit is contained in:
@@ -8,6 +8,8 @@
|
||||
<string name="action_stop_bote">Stop Bote</string>
|
||||
<string name="action_settings">Settings</string>
|
||||
|
||||
<string name="start_i2p_android">It appears that I2P Android is not running. Would you like to start it?</string>
|
||||
|
||||
<string name="items_selected">%s selected</string>
|
||||
<string name="action_delete">Delete</string>
|
||||
<string name="action_mark_read">Mark read</string>
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
package i2p.bote.android;
|
||||
|
||||
import net.i2p.android.router.service.IRouterState;
|
||||
import i2p.bote.I2PBote;
|
||||
import i2p.bote.android.addressbook.AddressBookActivity;
|
||||
import i2p.bote.android.config.SettingsActivity;
|
||||
import i2p.bote.android.service.BoteService;
|
||||
import i2p.bote.android.service.Init;
|
||||
import i2p.bote.android.service.Init.RouterChoice;
|
||||
import i2p.bote.android.util.MoveToDialogFragment;
|
||||
import i2p.bote.folder.EmailFolder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.ActivityManager.RunningServiceInfo;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.drawable.Drawable;
|
||||
@@ -46,11 +55,15 @@ public class EmailListActivity extends ActionBarActivity implements
|
||||
private ListView mFolderList;
|
||||
private TextView mNetworkStatus;
|
||||
private ActionBarDrawerToggle mDrawerToggle;
|
||||
RouterChoice mRouterChoice;
|
||||
IRouterState mStateService = null;
|
||||
|
||||
private static final String SHARED_PREFS = "i2p.bote";
|
||||
private static final String PREF_NAV_DRAWER_OPENED = "navDrawerOpened";
|
||||
private static final String ACTIVE_FOLDER = "activeFolder";
|
||||
|
||||
private static final int REQUEST_START_I2P = 1;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -232,6 +245,28 @@ public class EmailListActivity extends ActionBarActivity implements
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
if (prefs.getBoolean("i2pbote.router.auto", true) ||
|
||||
prefs.getString("i2pbote.router.use", "internal").equals("android")) {
|
||||
// Try to bind to I2P Android
|
||||
Intent i2pIntent = new Intent(IRouterState.class.getName());
|
||||
i2pIntent.setClassName("net.i2p.android.router",
|
||||
"net.i2p.android.router.service.RouterService");
|
||||
mTriedBindState = bindService(i2pIntent, mStateConnection, BIND_AUTO_CREATE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
if (mTriedBindState)
|
||||
unbindService(mStateConnection);
|
||||
mTriedBindState = false;
|
||||
}
|
||||
|
||||
private boolean isBoteServiceRunning() {
|
||||
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
|
||||
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
|
||||
@@ -251,9 +286,46 @@ public class EmailListActivity extends ActionBarActivity implements
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_start_bote:
|
||||
Intent start = new Intent(this, BoteService.class);
|
||||
startService(start);
|
||||
supportInvalidateOptionsMenu();
|
||||
// Init from settings
|
||||
Init init = new Init(this);
|
||||
mRouterChoice = init.initialize(mStateService);
|
||||
|
||||
if (mRouterChoice == RouterChoice.ANDROID) {
|
||||
try {
|
||||
if (mStateService == null) {
|
||||
// I2P Android not installed
|
||||
// TODO: handle
|
||||
} else if (!mStateService.isStarted()) {
|
||||
// Ask user to start I2P Android
|
||||
DialogFragment df = new DialogFragment() {
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setMessage(R.string.start_i2p_android)
|
||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
Intent i = new Intent("net.i2p.android.router");
|
||||
i.setAction("net.i2p.android.router.START_I2P");
|
||||
startActivityForResult(i, REQUEST_START_I2P);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
return builder.create();
|
||||
}
|
||||
};
|
||||
df.show(getSupportFragmentManager(), "starti2p");
|
||||
} else
|
||||
startBote();
|
||||
} catch (RemoteException e) {
|
||||
// TODO log
|
||||
}
|
||||
} else
|
||||
startBote();
|
||||
return true;
|
||||
|
||||
case R.id.action_stop_bote:
|
||||
@@ -272,6 +344,24 @@ public class EmailListActivity extends ActionBarActivity implements
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == REQUEST_START_I2P) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
startBote();
|
||||
}
|
||||
} else {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
private void startBote() {
|
||||
Intent start = new Intent(this, BoteService.class);
|
||||
start.putExtra(BoteService.ROUTER_CHOICE, mRouterChoice);
|
||||
startService(start);
|
||||
supportInvalidateOptionsMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(CharSequence title) {
|
||||
mTitle = title;
|
||||
@@ -329,4 +419,23 @@ public class EmailListActivity extends ActionBarActivity implements
|
||||
EmailListFragment f = (EmailListFragment) getSupportFragmentManager().findFragmentById(R.id.list_fragment);
|
||||
f.onFolderSelected(newFolder);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// I2P Android helpers
|
||||
//
|
||||
|
||||
private boolean mTriedBindState;
|
||||
private ServiceConnection mStateConnection = new ServiceConnection() {
|
||||
public void onServiceConnected(ComponentName className,
|
||||
IBinder service) {
|
||||
mStateService = IRouterState.Stub.asInterface(service);
|
||||
}
|
||||
|
||||
public void onServiceDisconnected(ComponentName className) {
|
||||
// This is called when the connection with the service has been
|
||||
// unexpectedly disconnected -- that is, its process crashed.
|
||||
mStateService = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,20 +6,21 @@ import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.RouterLaunch;
|
||||
import i2p.bote.I2PBote;
|
||||
import i2p.bote.android.service.Init.RouterChoice;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
|
||||
public class BoteService extends Service {
|
||||
boolean mUseInternalRouter;
|
||||
public static final String ROUTER_CHOICE = "router_choice";
|
||||
|
||||
RouterChoice mRouterChoice;
|
||||
RouterContext mRouterContext;
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
// Init from settings
|
||||
Init init = new Init(this);
|
||||
mUseInternalRouter = init.initialize();
|
||||
if (mUseInternalRouter)
|
||||
mRouterChoice = (RouterChoice) intent.getSerializableExtra(ROUTER_CHOICE);
|
||||
if (mRouterChoice == RouterChoice.INTERNAL)
|
||||
new Thread(new RouterStarter()).start();
|
||||
I2PBote.getInstance().startUp();
|
||||
return START_STICKY;
|
||||
@@ -33,10 +34,15 @@ public class BoteService extends Service {
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
I2PBote.getInstance().shutDown();
|
||||
if (mUseInternalRouter)
|
||||
if (mRouterChoice == RouterChoice.INTERNAL)
|
||||
new Thread(new RouterStopper()).start();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Internal router helpers
|
||||
//
|
||||
|
||||
private class RouterStarter implements Runnable {
|
||||
public void run() {
|
||||
RouterLaunch.main(null);
|
||||
|
||||
74
src/i2p/bote/android/service/Init.java
Normal file
74
src/i2p/bote/android/service/Init.java
Normal file
@@ -0,0 +1,74 @@
|
||||
package i2p.bote.android.service;
|
||||
|
||||
import net.i2p.android.router.service.IRouterState;
|
||||
import net.i2p.client.I2PClient;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
public class Init {
|
||||
private final Context ctx;
|
||||
private final String myDir;
|
||||
|
||||
public enum RouterChoice {
|
||||
INTERNAL,
|
||||
ANDROID,
|
||||
REMOTE;
|
||||
}
|
||||
|
||||
public Init(Context c) {
|
||||
ctx = c;
|
||||
// This needs to be changed so that we can have an alternative place
|
||||
myDir = c.getFilesDir().getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses settings and prepares the system for starting the Bote service.
|
||||
* @return true if we should use the internal router, false otherwise.
|
||||
*/
|
||||
public RouterChoice initialize(IRouterState mStateService) {
|
||||
// Set up the locations so Router and WorkingDir can find them
|
||||
// We do this again here, in the event settings were changed.
|
||||
System.setProperty("i2p.dir.base", myDir);
|
||||
System.setProperty("i2p.dir.config", myDir);
|
||||
System.setProperty("wrapper.logfile", myDir + "/wrapper.log");
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
|
||||
RouterChoice routerChoice;
|
||||
String i2cpHost, i2cpPort;
|
||||
if (prefs.getBoolean("i2pbote.router.auto", true)) {
|
||||
if (mStateService != null) {
|
||||
routerChoice = RouterChoice.ANDROID;
|
||||
// TODO fetch settings from I2P Android
|
||||
i2cpHost = "127.0.0.1";
|
||||
i2cpPort = "7654";
|
||||
} else {
|
||||
routerChoice = RouterChoice.INTERNAL;
|
||||
i2cpHost = "internal";
|
||||
i2cpPort = "internal";
|
||||
}
|
||||
} else {
|
||||
// Check manual settings
|
||||
String which = prefs.getString("i2pbote.router.use", "internal");
|
||||
if ("internal".equals(which)) {
|
||||
routerChoice = RouterChoice.INTERNAL;
|
||||
i2cpHost = "internal";
|
||||
i2cpPort = "internal";
|
||||
} else if ("android".equals(which)) {
|
||||
routerChoice = RouterChoice.ANDROID;
|
||||
// TODO fetch settings from I2P Android
|
||||
i2cpHost = "127.0.0.1";
|
||||
i2cpPort = "7654";
|
||||
} else { // Remote router
|
||||
routerChoice = RouterChoice.REMOTE;
|
||||
i2cpHost = prefs.getString("i2pbote.i2cp.tcp.host", "127.0.0.1");
|
||||
i2cpPort = prefs.getString("i2pbote.i2cp.tcp.port", "7654");
|
||||
}
|
||||
}
|
||||
// Set the I2CP host/port
|
||||
System.setProperty(I2PClient.PROP_TCP_HOST, i2cpHost);
|
||||
System.setProperty(I2PClient.PROP_TCP_PORT, i2cpPort);
|
||||
|
||||
return routerChoice;
|
||||
}
|
||||
}
|
||||
33
src/net/i2p/android/router/service/IRouterState.aidl
Normal file
33
src/net/i2p/android/router/service/IRouterState.aidl
Normal file
@@ -0,0 +1,33 @@
|
||||
package net.i2p.android.router.service;
|
||||
|
||||
import net.i2p.android.router.service.IRouterStateCallback;
|
||||
|
||||
/**
|
||||
* An interface for determining the state of the I2P RouterService.
|
||||
*/
|
||||
interface IRouterState {
|
||||
|
||||
/**
|
||||
* This allows I2P to inform on state changes.
|
||||
*/
|
||||
void registerCallback(IRouterStateCallback cb);
|
||||
|
||||
/**
|
||||
* Remove registered callback interface.
|
||||
*/
|
||||
void unregisterCallback(IRouterStateCallback cb);
|
||||
|
||||
/**
|
||||
* Determines whether the RouterService has been started. If it hasn't, no
|
||||
* state changes will ever occur from this RouterService instance, and the
|
||||
* client should unbind and inform the user that the I2P router is not
|
||||
* running (and optionally send a net.i2p.android.router.START_I2P Intent).
|
||||
*/
|
||||
boolean isStarted();
|
||||
|
||||
/**
|
||||
* Get the state of the I2P router
|
||||
**/
|
||||
String getState();
|
||||
|
||||
}
|
||||
13
src/net/i2p/android/router/service/IRouterStateCallback.aidl
Normal file
13
src/net/i2p/android/router/service/IRouterStateCallback.aidl
Normal file
@@ -0,0 +1,13 @@
|
||||
package net.i2p.android.router.service;
|
||||
|
||||
/**
|
||||
* Callback interface used to send synchronous notifications of the current
|
||||
* RouterService state back to registered clients. Note that this is a
|
||||
* one-way interface so the server does not block waiting for the client.
|
||||
*/
|
||||
oneway interface IRouterStateCallback {
|
||||
/**
|
||||
* Called when the state of the I2P router changes
|
||||
*/
|
||||
void stateChanged(String newState);
|
||||
}
|
||||
Reference in New Issue
Block a user