@ -1,6 +1,9 @@
package org.purplei2p.i2pd ;
package org.purplei2p.i2pd ;
import java.io.File ;
import java.io.File ;
import java.io.FileReader ;
import java.io.FileWriter ;
import java.io.BufferedReader ;
import java.io.FileOutputStream ;
import java.io.FileOutputStream ;
import java.io.IOException ;
import java.io.IOException ;
import java.io.InputStream ;
import java.io.InputStream ;
@ -25,21 +28,27 @@ import android.view.MenuItem;
import android.widget.TextView ;
import android.widget.TextView ;
import android.widget.Toast ;
import android.widget.Toast ;
// For future package update checking
import org.purplei2p.i2pd.BuildConfig ;
public class I2PDActivity extends Activity {
public class I2PDActivity extends Activity {
private static final String TAG = "i2pdActvt" ;
private static final String TAG = "i2pdActvt" ;
public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000 ;
public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000 ;
private TextView textView ;
private TextView textView ;
private boolean assetsCopied ;
private String i2pdpath = Environment . getExternalStorageDirectory ( ) . getAbsolutePath ( ) + "/i2pd/" ;
private static final DaemonSingleton daemon = DaemonSingleton . getInstance ( ) ;
private static final DaemonSingleton daemon = DaemonSingleton . getInstance ( ) ;
private final DaemonSingleton . StateUpdateListener daemonStateUpdatedListener =
private final DaemonSingleton . StateUpdateListener daemonStateUpdatedListener =
new DaemonSingleton . StateUpdateListener ( ) {
new DaemonSingleton . StateUpdateListener ( ) {
@Override
@Override
public void daemonStateUpdate ( ) {
public void daemonStateUpdate ( )
{
processAssets ( ) ;
runOnUiThread ( new Runnable ( ) {
runOnUiThread ( new Runnable ( ) {
@Override
@Override
public void run ( ) {
public void run ( ) {
try {
try {
@ -55,7 +64,7 @@ public class I2PDActivity extends Activity {
( DaemonSingleton . State . startFailed . equals ( state ) ? ": " + daemon . getDaemonStartResult ( ) : "" ) +
( DaemonSingleton . State . startFailed . equals ( state ) ? ": " + daemon . getDaemonStartResult ( ) : "" ) +
( DaemonSingleton . State . gracefulShutdownInProgress . equals ( state ) ? ": " + formatGraceTimeRemaining ( ) + " " + getText ( R . string . remaining ) : "" )
( DaemonSingleton . State . gracefulShutdownInProgress . equals ( state ) ? ": " + formatGraceTimeRemaining ( ) + " " + getText ( R . string . remaining ) : "" )
) ;
) ;
} catch ( Throwable tr ) {
} catch ( Throwable tr ) {
Log . e ( TAG , "error ignored" , tr ) ;
Log . e ( TAG , "error ignored" , tr ) ;
}
}
}
}
@ -64,7 +73,7 @@ public class I2PDActivity extends Activity {
} ;
} ;
private static volatile long graceStartedMillis ;
private static volatile long graceStartedMillis ;
private static final Object graceStartedMillis_LOCK = new Object ( ) ;
private static final Object graceStartedMillis_LOCK = new Object ( ) ;
private static String formatGraceTimeRemaining ( ) {
private static String formatGraceTimeRemaining ( ) {
long remainingSeconds ;
long remainingSeconds ;
synchronized ( graceStartedMillis_LOCK ) {
synchronized ( graceStartedMillis_LOCK ) {
@ -74,25 +83,19 @@ public class I2PDActivity extends Activity {
long remSec = remainingSeconds - remainingMinutes * 60 ;
long remSec = remainingSeconds - remainingMinutes * 60 ;
return remainingMinutes + ":" + ( remSec / 10 ) + remSec % 10 ;
return remainingMinutes + ":" + ( remSec / 10 ) + remSec % 10 ;
}
}
@Override
@Override
public void onCreate ( Bundle savedInstanceState ) {
public void onCreate ( Bundle savedInstanceState ) {
super . onCreate ( savedInstanceState ) ;
super . onCreate ( savedInstanceState ) ;
// copy assets
copyAsset ( "certificates" ) ;
copyAsset ( "i2pd.conf" ) ;
copyAsset ( "subsciptions.txt" ) ;
copyAsset ( "tunnels.conf" ) ;
textView = new TextView ( this ) ;
textView = new TextView ( this ) ;
setContentView ( textView ) ;
setContentView ( textView ) ;
daemon . addStateChangeListener ( daemonStateUpdatedListener ) ;
daemon . addStateChangeListener ( daemonStateUpdatedListener ) ;
daemonStateUpdatedListener . daemonStateUpdate ( ) ;
daemonStateUpdatedListener . daemonStateUpdate ( ) ;
// set the app be foreground
// set the app be foreground
doBindService ( ) ;
doBindService ( ) ;
final Timer gracefulQuitTimer = getGracefulQuitTimer ( ) ;
final Timer gracefulQuitTimer = getGracefulQuitTimer ( ) ;
if ( gracefulQuitTimer ! = null ) {
if ( gracefulQuitTimer ! = null ) {
long gracefulStopAtMillis ;
long gracefulStopAtMillis ;
@ -102,7 +105,7 @@ public class I2PDActivity extends Activity {
rescheduleGraceStop ( gracefulQuitTimer , gracefulStopAtMillis ) ;
rescheduleGraceStop ( gracefulQuitTimer , gracefulStopAtMillis ) ;
}
}
}
}
@Override
@Override
protected void onDestroy ( ) {
protected void onDestroy ( ) {
super . onDestroy ( ) ;
super . onDestroy ( ) ;
@ -115,7 +118,7 @@ public class I2PDActivity extends Activity {
Log . e ( TAG , "" , tr ) ;
Log . e ( TAG , "" , tr ) ;
}
}
}
}
private static void cancelGracefulStop ( ) {
private static void cancelGracefulStop ( ) {
Timer gracefulQuitTimer = getGracefulQuitTimer ( ) ;
Timer gracefulQuitTimer = getGracefulQuitTimer ( ) ;
if ( gracefulQuitTimer ! = null ) {
if ( gracefulQuitTimer ! = null ) {
@ -123,7 +126,7 @@ public class I2PDActivity extends Activity {
setGracefulQuitTimer ( null ) ;
setGracefulQuitTimer ( null ) ;
}
}
}
}
private CharSequence throwableToString ( Throwable tr ) {
private CharSequence throwableToString ( Throwable tr ) {
StringWriter sw = new StringWriter ( 8192 ) ;
StringWriter sw = new StringWriter ( 8192 ) ;
PrintWriter pw = new PrintWriter ( sw ) ;
PrintWriter pw = new PrintWriter ( sw ) ;
@ -131,9 +134,9 @@ public class I2PDActivity extends Activity {
pw . close ( ) ;
pw . close ( ) ;
return sw . toString ( ) ;
return sw . toString ( ) ;
}
}
// private LocalService mBoundService;
// private LocalService mBoundService;
private ServiceConnection mConnection = new ServiceConnection ( ) {
private ServiceConnection mConnection = new ServiceConnection ( ) {
public void onServiceConnected ( ComponentName className , IBinder service ) {
public void onServiceConnected ( ComponentName className , IBinder service ) {
// This is called when the connection with the service has been
// This is called when the connection with the service has been
@ -142,12 +145,12 @@ public class I2PDActivity extends Activity {
// service that we know is running in our own process, we can
// service that we know is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
// cast its IBinder to a concrete class and directly access it.
// mBoundService = ((LocalService.LocalBinder)service).getService();
// mBoundService = ((LocalService.LocalBinder)service).getService();
// Tell the user about this for our demo.
// Tell the user about this for our demo.
// Toast.makeText(Binding.this, R.string.local_service_connected,
// Toast.makeText(Binding.this, R.string.local_service_connected,
// Toast.LENGTH_SHORT).show();
// Toast.LENGTH_SHORT).show();
}
}
public void onServiceDisconnected ( ComponentName className ) {
public void onServiceDisconnected ( ComponentName className ) {
// This is called when the connection with the service has been
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
// unexpectedly disconnected -- that is, its process crashed.
@ -158,10 +161,9 @@ public class I2PDActivity extends Activity {
// Toast.LENGTH_SHORT).show();
// Toast.LENGTH_SHORT).show();
}
}
} ;
} ;
private static volatile boolean mIsBound ;
private static volatile boolean mIsBound ;
private void doBindService ( ) {
private void doBindService ( ) {
synchronized ( I2PDActivity . class ) {
synchronized ( I2PDActivity . class ) {
if ( mIsBound ) return ;
if ( mIsBound ) return ;
@ -173,7 +175,7 @@ public class I2PDActivity extends Activity {
mIsBound = true ;
mIsBound = true ;
}
}
}
}
private void doUnbindService ( ) {
private void doUnbindService ( ) {
synchronized ( I2PDActivity . class ) {
synchronized ( I2PDActivity . class ) {
if ( mIsBound ) {
if ( mIsBound ) {
@ -183,21 +185,21 @@ public class I2PDActivity extends Activity {
}
}
}
}
}
}
@Override
@Override
public boolean onCreateOptionsMenu ( Menu menu ) {
public boolean onCreateOptionsMenu ( Menu menu ) {
// Inflate the menu; this adds items to the action bar if it is present.
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater ( ) . inflate ( R . menu . options_main , menu ) ;
getMenuInflater ( ) . inflate ( R . menu . options_main , menu ) ;
return true ;
return true ;
}
}
@Override
@Override
public boolean onOptionsItemSelected ( MenuItem item ) {
public boolean onOptionsItemSelected ( MenuItem item ) {
// Handle action bar item clicks here. The action bar will
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
// as you specify a parent activity in AndroidManifest.xml.
int id = item . getItemId ( ) ;
int id = item . getItemId ( ) ;
switch ( id ) {
switch ( id ) {
case R . id . action_stop :
case R . id . action_stop :
i2pdStop ( ) ;
i2pdStop ( ) ;
@ -206,14 +208,14 @@ public class I2PDActivity extends Activity {
i2pdGracefulStop ( ) ;
i2pdGracefulStop ( ) ;
return true ;
return true ;
}
}
return super . onOptionsItemSelected ( item ) ;
return super . onOptionsItemSelected ( item ) ;
}
}
private void i2pdStop ( ) {
private void i2pdStop ( ) {
cancelGracefulStop ( ) ;
cancelGracefulStop ( ) ;
new Thread ( new Runnable ( ) {
new Thread ( new Runnable ( ) {
@Override
@Override
public void run ( ) {
public void run ( ) {
Log . d ( TAG , "stopping" ) ;
Log . d ( TAG , "stopping" ) ;
@ -223,12 +225,12 @@ public class I2PDActivity extends Activity {
Log . e ( TAG , "" , tr ) ;
Log . e ( TAG , "" , tr ) ;
}
}
}
}
} , "stop" ) . start ( ) ;
} , "stop" ) . start ( ) ;
}
}
private static volatile Timer gracefulQuitTimer ;
private static volatile Timer gracefulQuitTimer ;
private void i2pdGracefulStop ( ) {
private void i2pdGracefulStop ( ) {
if ( daemon . getState ( ) = = DaemonSingleton . State . stopped ) {
if ( daemon . getState ( ) = = DaemonSingleton . State . stopped ) {
Toast . makeText ( this , R . string . already_stopped ,
Toast . makeText ( this , R . string . already_stopped ,
@ -243,7 +245,7 @@ public class I2PDActivity extends Activity {
Toast . makeText ( this , R . string . graceful_stop_is_in_progress ,
Toast . makeText ( this , R . string . graceful_stop_is_in_progress ,
Toast . LENGTH_SHORT ) . show ( ) ;
Toast . LENGTH_SHORT ) . show ( ) ;
new Thread ( new Runnable ( ) {
new Thread ( new Runnable ( ) {
@Override
@Override
public void run ( ) {
public void run ( ) {
try {
try {
@ -263,21 +265,21 @@ public class I2PDActivity extends Activity {
Log . e ( TAG , "" , tr ) ;
Log . e ( TAG , "" , tr ) ;
}
}
}
}
} , "gracInit" ) . start ( ) ;
} , "gracInit" ) . start ( ) ;
}
}
private void rescheduleGraceStop ( Timer gracefulQuitTimerOld , long gracefulStopAtMillis ) {
private void rescheduleGraceStop ( Timer gracefulQuitTimerOld , long gracefulStopAtMillis ) {
if ( gracefulQuitTimerOld ! = null ) gracefulQuitTimerOld . cancel ( ) ;
if ( gracefulQuitTimerOld ! = null ) gracefulQuitTimerOld . cancel ( ) ;
final Timer gracefulQuitTimer = new Timer ( true ) ;
final Timer gracefulQuitTimer = new Timer ( true ) ;
setGracefulQuitTimer ( gracefulQuitTimer ) ;
setGracefulQuitTimer ( gracefulQuitTimer ) ;
gracefulQuitTimer . schedule ( new TimerTask ( ) {
gracefulQuitTimer . schedule ( new TimerTask ( ) {
@Override
@Override
public void run ( ) {
public void run ( ) {
i2pdStop ( ) ;
i2pdStop ( ) ;
}
}
} , Math . max ( 0 , gracefulStopAtMillis - System . currentTimeMillis ( ) ) ) ;
} , Math . max ( 0 , gracefulStopAtMillis - System . currentTimeMillis ( ) ) ) ;
final TimerTask tickerTask = new TimerTask ( ) {
final TimerTask tickerTask = new TimerTask ( ) {
@Override
@Override
@ -287,30 +289,30 @@ public class I2PDActivity extends Activity {
} ;
} ;
gracefulQuitTimer . scheduleAtFixedRate ( tickerTask , 0 /*start delay*/ , 1000 /*millis period*/ ) ;
gracefulQuitTimer . scheduleAtFixedRate ( tickerTask , 0 /*start delay*/ , 1000 /*millis period*/ ) ;
}
}
private static Timer getGracefulQuitTimer ( ) {
private static Timer getGracefulQuitTimer ( ) {
return gracefulQuitTimer ;
return gracefulQuitTimer ;
}
}
private static void setGracefulQuitTimer ( Timer gracefulQuitTimer ) {
private static void setGracefulQuitTimer ( Timer gracefulQuitTimer ) {
I2PDActivity . gracefulQuitTimer = gracefulQuitTimer ;
I2PDActivity . gracefulQuitTimer = gracefulQuitTimer ;
}
}
/ * *
/ * *
* Copy the asset at the specified path to this app ' s data directory . If the
* Copy the asset at the specified path to this app ' s data directory . If the
* asset is a directory , its contents are also copied .
* asset is a directory , its contents are also copied .
*
*
* @param path
* @param path
* Path to asset , relative to app ' s assets directory .
* Path to asset , relative to app ' s assets directory .
* /
* /
private void copyAsset ( String path ) {
private void copyAsset ( String path ) {
AssetManager manager = getAssets ( ) ;
AssetManager manager = getAssets ( ) ;
// If we have a directory, we make it and recurse. If a file, we copy its
// If we have a directory, we make it and recurse. If a file, we copy its
// contents.
// contents.
try {
try {
String [ ] contents = manager . list ( path ) ;
String [ ] contents = manager . list ( path ) ;
// The documentation suggests that list throws an IOException, but doesn't
// The documentation suggests that list throws an IOException, but doesn't
// say under what conditions. It'd be nice if it did so when the path was
// say under what conditions. It'd be nice if it did so when the path was
// to a file. That doesn't appear to be the case. If the returned array is
// to a file. That doesn't appear to be the case. If the returned array is
@ -318,30 +320,30 @@ public class I2PDActivity extends Activity {
// directories will get turned into files.
// directories will get turned into files.
if ( contents = = null | | contents . length = = 0 )
if ( contents = = null | | contents . length = = 0 )
throw new IOException ( ) ;
throw new IOException ( ) ;
// Make the directory.
// Make the directory.
File dir = new File ( Environment . getExternalStorageDirectory ( ) . getAbsolutePath ( ) + "/i2pd/" , path ) ;
File dir = new File ( i2pdpath , path ) ;
dir . mkdirs ( ) ;
dir . mkdirs ( ) ;
// Recurse on the contents.
// Recurse on the contents.
for ( String entry : contents ) {
for ( String entry : contents ) {
copyAsset ( path + "/" + entry ) ;
copyAsset ( path + "/" + entry ) ;
}
}
} catch ( IOException e ) {
} catch ( IOException e ) {
copyFileAsset ( path ) ;
copyFileAsset ( path ) ;
}
}
}
}
/ * *
/ * *
* Copy the asset file specified by path to app ' s data directory . Assumes
* Copy the asset file specified by path to app ' s data directory . Assumes
* parent directories have already been created .
* parent directories have already been created .
*
*
* @param path
* @param path
* Path to asset , relative to app ' s assets directory .
* Path to asset , relative to app ' s assets directory .
* /
* /
private void copyFileAsset ( String path ) {
private void copyFileAsset ( String path ) {
File file = new File ( Environment . getExternalStorageDirectory ( ) . getAbsolutePath ( ) + "/i2pd/" , path ) ;
File file = new File ( i2pdpath , path ) ;
try {
if ( ! file . exists ( ) ) try {
InputStream in = getAssets ( ) . open ( path ) ;
InputStream in = getAssets ( ) . open ( path ) ;
OutputStream out = new FileOutputStream ( file ) ;
OutputStream out = new FileOutputStream ( file ) ;
byte [ ] buffer = new byte [ 1024 ] ;
byte [ ] buffer = new byte [ 1024 ] ;
@ -352,8 +354,66 @@ public class I2PDActivity extends Activity {
}
}
out . close ( ) ;
out . close ( ) ;
in . close ( ) ;
in . close ( ) ;
} catch ( IOException e ) {
} catch ( IOException e ) {
Log . e ( TAG , "" , e ) ;
Log . e ( TAG , "" , e ) ;
}
}
}
}
private void deleteRecursive ( File fileOrDirectory ) {
if ( fileOrDirectory . isDirectory ( ) ) {
for ( File child : fileOrDirectory . listFiles ( ) ) {
deleteRecursive ( child ) ;
}
}
fileOrDirectory . delete ( ) ;
}
private void processAssets ( ) {
if ( ! assetsCopied ) try {
assetsCopied = true ; // prevent from running on every state update
File holderfile = new File ( i2pdpath , "assets.ready" ) ;
String versionName = BuildConfig . VERSION_NAME ; // here will be app version, like 2.XX.XX
StringBuilder text = new StringBuilder ( ) ;
if ( holderfile . exists ( ) ) try { // if holder file exists, read assets version string
BufferedReader br = new BufferedReader ( new FileReader ( holderfile ) ) ;
String line ;
while ( ( line = br . readLine ( ) ) ! = null ) {
text . append ( line ) ;
}
br . close ( ) ;
}
catch ( IOException e ) {
Log . e ( TAG , "" , e ) ;
}
// if version differs from current app version or null, try to delete certificates folder
if ( ! text . toString ( ) . contains ( versionName ) ) try {
holderfile . delete ( ) ;
File certpath = new File ( i2pdpath , "certificates" ) ;
deleteRecursive ( certpath ) ;
}
catch ( Throwable tr ) {
Log . e ( TAG , "" , tr ) ;
}
// copy assets. If processed file exists, it won't be overwrited
copyAsset ( "certificates" ) ;
copyAsset ( "i2pd.conf" ) ;
copyAsset ( "subscriptions.txt" ) ;
copyAsset ( "tunnels.conf" ) ;
// update holder file about successful copying
FileWriter writer = new FileWriter ( holderfile ) ;
writer . append ( versionName ) ;
writer . flush ( ) ;
writer . close ( ) ;
}
catch ( Throwable tr )
{
Log . e ( TAG , "copy assets" , tr ) ;
}
}
}
}