mirror of
				https://github.com/PurpleI2P/i2pd.git
				synced 2025-11-04 08:30:46 +00:00 
			
		
		
		
	store and install assets on android
This commit is contained in:
		
							parent
							
								
									fc4787da4e
								
							
						
					
					
						commit
						db5b45222a
					
				
					 2 changed files with 284 additions and 209 deletions
				
			
		| 
						 | 
				
			
			@ -37,6 +37,7 @@ android {
 | 
			
		|||
            java.srcDirs = ['src']
 | 
			
		||||
            res.srcDirs = ['res']
 | 
			
		||||
            jniLibs.srcDirs = ['libs']
 | 
			
		||||
            assets.srcDirs = ['assets']
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    signingConfigs {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,10 @@
 | 
			
		|||
package org.purplei2p.i2pd;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileOutputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.io.OutputStream;
 | 
			
		||||
import java.io.PrintWriter;
 | 
			
		||||
import java.io.StringWriter;
 | 
			
		||||
import java.util.Timer;
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +15,9 @@ import android.content.ComponentName;
 | 
			
		|||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.content.ServiceConnection;
 | 
			
		||||
import android.content.res.AssetManager;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.os.Environment;
 | 
			
		||||
import android.os.IBinder;
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
import android.view.Menu;
 | 
			
		||||
| 
						 | 
				
			
			@ -19,24 +26,24 @@ import android.widget.TextView;
 | 
			
		|||
import android.widget.Toast;
 | 
			
		||||
 | 
			
		||||
public class I2PDActivity extends Activity {
 | 
			
		||||
    private static final String TAG = "i2pdActvt";
 | 
			
		||||
    public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000;
 | 
			
		||||
 | 
			
		||||
    private TextView textView;
 | 
			
		||||
 | 
			
		||||
	private static final String TAG = "i2pdActvt";
 | 
			
		||||
	public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000;
 | 
			
		||||
	
 | 
			
		||||
	private TextView textView;
 | 
			
		||||
	
 | 
			
		||||
	private static final DaemonSingleton daemon = DaemonSingleton.getInstance();
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	private final DaemonSingleton.StateUpdateListener daemonStateUpdatedListener =
 | 
			
		||||
			new DaemonSingleton.StateUpdateListener() {
 | 
			
		||||
 | 
			
		||||
	new DaemonSingleton.StateUpdateListener() {
 | 
			
		||||
		
 | 
			
		||||
		@Override
 | 
			
		||||
		public void daemonStateUpdate() {
 | 
			
		||||
			runOnUiThread(new Runnable(){
 | 
			
		||||
 | 
			
		||||
				
 | 
			
		||||
				@Override
 | 
			
		||||
				public void run() {
 | 
			
		||||
					try {
 | 
			
		||||
						if(textView==null)return;
 | 
			
		||||
						if(textView==null) return;
 | 
			
		||||
						Throwable tr = daemon.getLastThrowable();
 | 
			
		||||
						if(tr!=null) {
 | 
			
		||||
							textView.setText(throwableToString(tr));
 | 
			
		||||
| 
						 | 
				
			
			@ -44,242 +51,309 @@ public class I2PDActivity extends Activity {
 | 
			
		|||
						}
 | 
			
		||||
						DaemonSingleton.State state = daemon.getState();
 | 
			
		||||
						textView.setText(
 | 
			
		||||
						        String.valueOf(state)+
 | 
			
		||||
                                    (DaemonSingleton.State.startFailed.equals(state)?": "+daemon.getDaemonStartResult():"")+
 | 
			
		||||
                                    (DaemonSingleton.State.gracefulShutdownInProgress.equals(state)?":  "+formatGraceTimeRemaining()+" "+getText(R.string.remaining):"")
 | 
			
		||||
                        );
 | 
			
		||||
					} catch (Throwable tr) {
 | 
			
		||||
						String.valueOf(state)+
 | 
			
		||||
						(DaemonSingleton.State.startFailed.equals(state)?": "+daemon.getDaemonStartResult():"")+
 | 
			
		||||
						(DaemonSingleton.State.gracefulShutdownInProgress.equals(state)?":  "+formatGraceTimeRemaining()+" "+getText(R.string.remaining):"")
 | 
			
		||||
						);
 | 
			
		||||
						} catch (Throwable tr) {
 | 
			
		||||
						Log.e(TAG,"error ignored",tr);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
    private static volatile long graceStartedMillis;
 | 
			
		||||
    private static final Object graceStartedMillis_LOCK=new Object();
 | 
			
		||||
 | 
			
		||||
    private static String formatGraceTimeRemaining() {
 | 
			
		||||
        long remainingSeconds;
 | 
			
		||||
        synchronized (graceStartedMillis_LOCK){
 | 
			
		||||
            remainingSeconds=Math.round(Math.max(0,graceStartedMillis+GRACEFUL_DELAY_MILLIS-System.currentTimeMillis())/1000.0D);
 | 
			
		||||
        }
 | 
			
		||||
        long remainingMinutes=(long)Math.floor(remainingSeconds/60.0D);
 | 
			
		||||
        long remSec=remainingSeconds-remainingMinutes*60;
 | 
			
		||||
        return remainingMinutes+":"+(remSec/10)+remSec%10;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
 | 
			
		||||
        textView = new TextView(this);
 | 
			
		||||
        setContentView(textView);
 | 
			
		||||
        daemon.addStateChangeListener(daemonStateUpdatedListener);
 | 
			
		||||
        daemonStateUpdatedListener.daemonStateUpdate();
 | 
			
		||||
 | 
			
		||||
        //set the app be foreground
 | 
			
		||||
        doBindService();
 | 
			
		||||
 | 
			
		||||
        final Timer gracefulQuitTimer = getGracefulQuitTimer();
 | 
			
		||||
        if(gracefulQuitTimer!=null){
 | 
			
		||||
            long gracefulStopAtMillis;
 | 
			
		||||
            synchronized (graceStartedMillis_LOCK) {
 | 
			
		||||
                gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS;
 | 
			
		||||
            }
 | 
			
		||||
            rescheduleGraceStop(gracefulQuitTimer, gracefulStopAtMillis);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
	private static volatile long graceStartedMillis;
 | 
			
		||||
	private static final Object graceStartedMillis_LOCK=new Object();
 | 
			
		||||
	
 | 
			
		||||
	private static String formatGraceTimeRemaining() {
 | 
			
		||||
		long remainingSeconds;
 | 
			
		||||
		synchronized (graceStartedMillis_LOCK){
 | 
			
		||||
			remainingSeconds=Math.round(Math.max(0,graceStartedMillis+GRACEFUL_DELAY_MILLIS-System.currentTimeMillis())/1000.0D);
 | 
			
		||||
		}
 | 
			
		||||
		long remainingMinutes=(long)Math.floor(remainingSeconds/60.0D);
 | 
			
		||||
		long remSec=remainingSeconds-remainingMinutes*60;
 | 
			
		||||
		return remainingMinutes+":"+(remSec/10)+remSec%10;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	@Override
 | 
			
		||||
	public void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
		super.onCreate(savedInstanceState);
 | 
			
		||||
		
 | 
			
		||||
		// copy assets
 | 
			
		||||
		copyAsset("certificates");
 | 
			
		||||
		copyAsset("i2pd.conf");
 | 
			
		||||
		copyAsset("subsciptions.txt");
 | 
			
		||||
		copyAsset("tunnels.conf");
 | 
			
		||||
		
 | 
			
		||||
		textView = new TextView(this);
 | 
			
		||||
		setContentView(textView);
 | 
			
		||||
		daemon.addStateChangeListener(daemonStateUpdatedListener);
 | 
			
		||||
		daemonStateUpdatedListener.daemonStateUpdate();
 | 
			
		||||
		
 | 
			
		||||
		// set the app be foreground
 | 
			
		||||
		doBindService();
 | 
			
		||||
		
 | 
			
		||||
		final Timer gracefulQuitTimer = getGracefulQuitTimer();
 | 
			
		||||
		if(gracefulQuitTimer!=null){
 | 
			
		||||
			long gracefulStopAtMillis;
 | 
			
		||||
			synchronized (graceStartedMillis_LOCK) {
 | 
			
		||||
				gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS;
 | 
			
		||||
			}
 | 
			
		||||
			rescheduleGraceStop(gracefulQuitTimer, gracefulStopAtMillis);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	@Override
 | 
			
		||||
	protected void onDestroy() {
 | 
			
		||||
		super.onDestroy();
 | 
			
		||||
        textView = null;
 | 
			
		||||
        daemon.removeStateChangeListener(daemonStateUpdatedListener);
 | 
			
		||||
        //cancelGracefulStop();
 | 
			
		||||
		textView = null;
 | 
			
		||||
		daemon.removeStateChangeListener(daemonStateUpdatedListener);
 | 
			
		||||
		//cancelGracefulStop();
 | 
			
		||||
		try{
 | 
			
		||||
            doUnbindService();
 | 
			
		||||
		}catch(Throwable tr){
 | 
			
		||||
			doUnbindService();
 | 
			
		||||
			}catch(Throwable tr){
 | 
			
		||||
			Log.e(TAG, "", tr);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    private static void cancelGracefulStop() {
 | 
			
		||||
        Timer gracefulQuitTimer = getGracefulQuitTimer();
 | 
			
		||||
        if(gracefulQuitTimer!=null) {
 | 
			
		||||
            gracefulQuitTimer.cancel();
 | 
			
		||||
            setGracefulQuitTimer(null);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private CharSequence throwableToString(Throwable tr) {
 | 
			
		||||
    	StringWriter sw = new StringWriter(8192);
 | 
			
		||||
    	PrintWriter pw = new PrintWriter(sw);
 | 
			
		||||
    	tr.printStackTrace(pw);
 | 
			
		||||
    	pw.close();
 | 
			
		||||
    	return sw.toString();
 | 
			
		||||
	
 | 
			
		||||
	private static void cancelGracefulStop() {
 | 
			
		||||
		Timer gracefulQuitTimer = getGracefulQuitTimer();
 | 
			
		||||
		if(gracefulQuitTimer!=null) {
 | 
			
		||||
			gracefulQuitTimer.cancel();
 | 
			
		||||
			setGracefulQuitTimer(null);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
//	private LocalService mBoundService;
 | 
			
		||||
 | 
			
		||||
    private ServiceConnection mConnection = new ServiceConnection() {
 | 
			
		||||
        public void onServiceConnected(ComponentName className, IBinder service) {
 | 
			
		||||
            // This is called when the connection with the service has been
 | 
			
		||||
            // established, giving us the service object we can use to
 | 
			
		||||
            // interact with the service.  Because we have bound to a explicit
 | 
			
		||||
            // service that we know is running in our own process, we can
 | 
			
		||||
            // cast its IBinder to a concrete class and directly access it.
 | 
			
		||||
//	        mBoundService = ((LocalService.LocalBinder)service).getService();
 | 
			
		||||
 | 
			
		||||
            // Tell the user about this for our demo.
 | 
			
		||||
//	        Toast.makeText(Binding.this, R.string.local_service_connected,
 | 
			
		||||
//	                Toast.LENGTH_SHORT).show();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void onServiceDisconnected(ComponentName className) {
 | 
			
		||||
            // This is called when the connection with the service has been
 | 
			
		||||
            // unexpectedly disconnected -- that is, its process crashed.
 | 
			
		||||
            // Because it is running in our same process, we should never
 | 
			
		||||
            // see this happen.
 | 
			
		||||
//	        mBoundService = null;
 | 
			
		||||
//	        Toast.makeText(Binding.this, R.string.local_service_disconnected,
 | 
			
		||||
//	                Toast.LENGTH_SHORT).show();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private static volatile boolean mIsBound;
 | 
			
		||||
 | 
			
		||||
    private void doBindService() {
 | 
			
		||||
        synchronized (I2PDActivity.class) {
 | 
			
		||||
            if (mIsBound) return;
 | 
			
		||||
            // Establish a connection with the service.  We use an explicit
 | 
			
		||||
            // class name because we want a specific service implementation that
 | 
			
		||||
            // we know will be running in our own process (and thus won't be
 | 
			
		||||
            // supporting component replacement by other applications).
 | 
			
		||||
            bindService(new Intent(this, ForegroundService.class), mConnection, Context.BIND_AUTO_CREATE);
 | 
			
		||||
            mIsBound = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void doUnbindService() {
 | 
			
		||||
        synchronized (I2PDActivity.class) {
 | 
			
		||||
            if (mIsBound) {
 | 
			
		||||
                // Detach our existing connection.
 | 
			
		||||
                unbindService(mConnection);
 | 
			
		||||
                mIsBound = false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	private CharSequence throwableToString(Throwable tr) {
 | 
			
		||||
		StringWriter sw = new StringWriter(8192);
 | 
			
		||||
		PrintWriter pw = new PrintWriter(sw);
 | 
			
		||||
		tr.printStackTrace(pw);
 | 
			
		||||
		pw.close();
 | 
			
		||||
		return sw.toString();
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// private LocalService mBoundService;
 | 
			
		||||
	
 | 
			
		||||
	private ServiceConnection mConnection = new ServiceConnection() {
 | 
			
		||||
		public void onServiceConnected(ComponentName className, IBinder service) {
 | 
			
		||||
			// This is called when the connection with the service has been
 | 
			
		||||
			// established, giving us the service object we can use to
 | 
			
		||||
			// interact with the service.  Because we have bound to a explicit
 | 
			
		||||
			// service that we know is running in our own process, we can
 | 
			
		||||
			// cast its IBinder to a concrete class and directly access it.
 | 
			
		||||
			//		mBoundService = ((LocalService.LocalBinder)service).getService();
 | 
			
		||||
			
 | 
			
		||||
			// Tell the user about this for our demo.
 | 
			
		||||
			//		Toast.makeText(Binding.this, R.string.local_service_connected,
 | 
			
		||||
			//		Toast.LENGTH_SHORT).show();
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		public void onServiceDisconnected(ComponentName className) {
 | 
			
		||||
			// This is called when the connection with the service has been
 | 
			
		||||
			// unexpectedly disconnected -- that is, its process crashed.
 | 
			
		||||
			// Because it is running in our same process, we should never
 | 
			
		||||
			// see this happen.
 | 
			
		||||
			//		mBoundService = null;
 | 
			
		||||
			//		Toast.makeText(Binding.this, R.string.local_service_disconnected,
 | 
			
		||||
			//		Toast.LENGTH_SHORT).show();
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	private static volatile boolean mIsBound;
 | 
			
		||||
	
 | 
			
		||||
	private void doBindService() {
 | 
			
		||||
		synchronized (I2PDActivity.class) {
 | 
			
		||||
			if (mIsBound) return;
 | 
			
		||||
			// Establish a connection with the service.  We use an explicit
 | 
			
		||||
			// class name because we want a specific service implementation that
 | 
			
		||||
			// we know will be running in our own process (and thus won't be
 | 
			
		||||
			// supporting component replacement by other applications).
 | 
			
		||||
			bindService(new Intent(this, ForegroundService.class), mConnection, Context.BIND_AUTO_CREATE);
 | 
			
		||||
			mIsBound = true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	private void doUnbindService() {
 | 
			
		||||
		synchronized (I2PDActivity.class) {
 | 
			
		||||
			if (mIsBound) {
 | 
			
		||||
				// Detach our existing connection.
 | 
			
		||||
				unbindService(mConnection);
 | 
			
		||||
				mIsBound = false;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	@Override
 | 
			
		||||
	public boolean onCreateOptionsMenu(Menu menu) {
 | 
			
		||||
		// Inflate the menu; this adds items to the action bar if it is present.
 | 
			
		||||
		getMenuInflater().inflate(R.menu.options_main, menu);
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	@Override
 | 
			
		||||
	public boolean onOptionsItemSelected(MenuItem item) {
 | 
			
		||||
		// Handle action bar item clicks here. The action bar will
 | 
			
		||||
		// automatically handle clicks on the Home/Up button, so long
 | 
			
		||||
		// as you specify a parent activity in AndroidManifest.xml.
 | 
			
		||||
		int id = item.getItemId();
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
		switch(id){
 | 
			
		||||
        case R.id.action_stop:
 | 
			
		||||
            i2pdStop();
 | 
			
		||||
            return true;
 | 
			
		||||
        case R.id.action_graceful_stop:
 | 
			
		||||
            i2pdGracefulStop();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
			case R.id.action_stop:
 | 
			
		||||
			i2pdStop();
 | 
			
		||||
			return true;
 | 
			
		||||
			case R.id.action_graceful_stop:
 | 
			
		||||
			i2pdGracefulStop();
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		return super.onOptionsItemSelected(item);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	private void i2pdStop() {
 | 
			
		||||
        cancelGracefulStop();
 | 
			
		||||
        new Thread(new Runnable(){
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public void run() {
 | 
			
		||||
                Log.d(TAG, "stopping");
 | 
			
		||||
                try{
 | 
			
		||||
                    daemon.stopDaemon();
 | 
			
		||||
                }catch (Throwable tr) {
 | 
			
		||||
                    Log.e(TAG, "", tr);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        },"stop").start();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static volatile Timer gracefulQuitTimer;
 | 
			
		||||
 | 
			
		||||
    private void i2pdGracefulStop() {
 | 
			
		||||
        if(daemon.getState()==DaemonSingleton.State.stopped){
 | 
			
		||||
            Toast.makeText(this, R.string.already_stopped,
 | 
			
		||||
                    Toast.LENGTH_SHORT).show();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    	if(getGracefulQuitTimer()!=null){
 | 
			
		||||
	        Toast.makeText(this, R.string.graceful_stop_is_already_in_progress,
 | 
			
		||||
	        		Toast.LENGTH_SHORT).show();
 | 
			
		||||
    		return;
 | 
			
		||||
    	}
 | 
			
		||||
        Toast.makeText(this, R.string.graceful_stop_is_in_progress,
 | 
			
		||||
        		Toast.LENGTH_SHORT).show();
 | 
			
		||||
        new Thread(new Runnable(){
 | 
			
		||||
 | 
			
		||||
		cancelGracefulStop();
 | 
			
		||||
		new Thread(new Runnable(){
 | 
			
		||||
			
 | 
			
		||||
			@Override
 | 
			
		||||
			public void run() {
 | 
			
		||||
				Log.d(TAG, "stopping");
 | 
			
		||||
				try{
 | 
			
		||||
					daemon.stopDaemon();
 | 
			
		||||
					}catch (Throwable tr) {
 | 
			
		||||
					Log.e(TAG, "", tr);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
		},"stop").start();
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	private static volatile Timer gracefulQuitTimer;
 | 
			
		||||
	
 | 
			
		||||
	private void i2pdGracefulStop() {
 | 
			
		||||
		if(daemon.getState()==DaemonSingleton.State.stopped){
 | 
			
		||||
			Toast.makeText(this, R.string.already_stopped,
 | 
			
		||||
			Toast.LENGTH_SHORT).show();
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		if(getGracefulQuitTimer()!=null){
 | 
			
		||||
			Toast.makeText(this, R.string.graceful_stop_is_already_in_progress,
 | 
			
		||||
			Toast.LENGTH_SHORT).show();
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		Toast.makeText(this, R.string.graceful_stop_is_in_progress,
 | 
			
		||||
		Toast.LENGTH_SHORT).show();
 | 
			
		||||
		new Thread(new Runnable(){
 | 
			
		||||
			
 | 
			
		||||
			@Override
 | 
			
		||||
			public void run() {
 | 
			
		||||
				try{
 | 
			
		||||
					Log.d(TAG, "grac stopping");
 | 
			
		||||
			        if(daemon.isStartedOkay()) {
 | 
			
		||||
			        	daemon.stopAcceptingTunnels();
 | 
			
		||||
                        long gracefulStopAtMillis;
 | 
			
		||||
                        synchronized (graceStartedMillis_LOCK) {
 | 
			
		||||
                            graceStartedMillis = System.currentTimeMillis();
 | 
			
		||||
                            gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS;
 | 
			
		||||
                        }
 | 
			
		||||
                        rescheduleGraceStop(null,gracefulStopAtMillis);
 | 
			
		||||
			        }else{
 | 
			
		||||
			        	i2pdStop();
 | 
			
		||||
			        }
 | 
			
		||||
				} catch(Throwable tr) {
 | 
			
		||||
					if(daemon.isStartedOkay()) {
 | 
			
		||||
						daemon.stopAcceptingTunnels();
 | 
			
		||||
						long gracefulStopAtMillis;
 | 
			
		||||
						synchronized (graceStartedMillis_LOCK) {
 | 
			
		||||
							graceStartedMillis = System.currentTimeMillis();
 | 
			
		||||
							gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS;
 | 
			
		||||
						}
 | 
			
		||||
						rescheduleGraceStop(null,gracefulStopAtMillis);
 | 
			
		||||
						}else{
 | 
			
		||||
						i2pdStop();
 | 
			
		||||
					}
 | 
			
		||||
					} catch(Throwable tr) {
 | 
			
		||||
					Log.e(TAG,"",tr);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
        },"gracInit").start();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void rescheduleGraceStop(Timer gracefulQuitTimerOld, long gracefulStopAtMillis) {
 | 
			
		||||
        if(gracefulQuitTimerOld!=null)gracefulQuitTimerOld.cancel();
 | 
			
		||||
        final Timer gracefulQuitTimer = new Timer(true);
 | 
			
		||||
        setGracefulQuitTimer(gracefulQuitTimer);
 | 
			
		||||
        gracefulQuitTimer.schedule(new TimerTask(){
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public void run() {
 | 
			
		||||
                i2pdStop();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }, Math.max(0,gracefulStopAtMillis-System.currentTimeMillis()));
 | 
			
		||||
        final TimerTask tickerTask = new TimerTask() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void run() {
 | 
			
		||||
                daemonStateUpdatedListener.daemonStateUpdate();
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        gracefulQuitTimer.scheduleAtFixedRate(tickerTask,0/*start delay*/,1000/*millis period*/);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static Timer getGracefulQuitTimer() {
 | 
			
		||||
        return gracefulQuitTimer;
 | 
			
		||||
			
 | 
			
		||||
		},"gracInit").start();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	private void rescheduleGraceStop(Timer gracefulQuitTimerOld, long gracefulStopAtMillis) {
 | 
			
		||||
		if(gracefulQuitTimerOld!=null)gracefulQuitTimerOld.cancel();
 | 
			
		||||
		final Timer gracefulQuitTimer = new Timer(true);
 | 
			
		||||
		setGracefulQuitTimer(gracefulQuitTimer);
 | 
			
		||||
		gracefulQuitTimer.schedule(new TimerTask(){
 | 
			
		||||
			
 | 
			
		||||
			@Override
 | 
			
		||||
			public void run() {
 | 
			
		||||
				i2pdStop();
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
		}, Math.max(0,gracefulStopAtMillis-System.currentTimeMillis()));
 | 
			
		||||
		final TimerTask tickerTask = new TimerTask() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public void run() {
 | 
			
		||||
				daemonStateUpdatedListener.daemonStateUpdate();
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
		gracefulQuitTimer.scheduleAtFixedRate(tickerTask,0/*start delay*/,1000/*millis period*/);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	private static Timer getGracefulQuitTimer() {
 | 
			
		||||
		return 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
 | 
			
		||||
		* asset is a directory, its contents are also copied.
 | 
			
		||||
		* 
 | 
			
		||||
		* @param path
 | 
			
		||||
		* Path to asset, relative to app's assets directory.
 | 
			
		||||
	*/
 | 
			
		||||
	private void copyAsset(String path) {
 | 
			
		||||
		AssetManager manager = getAssets();
 | 
			
		||||
		
 | 
			
		||||
		// If we have a directory, we make it and recurse. If a file, we copy its
 | 
			
		||||
		// contents.
 | 
			
		||||
		try {
 | 
			
		||||
			String[] contents = manager.list(path);
 | 
			
		||||
			
 | 
			
		||||
			// 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
 | 
			
		||||
			// to a file. That doesn't appear to be the case. If the returned array is
 | 
			
		||||
			// null or has 0 length, we assume the path is to a file. This means empty
 | 
			
		||||
			// directories will get turned into files.
 | 
			
		||||
			if (contents == null || contents.length == 0)
 | 
			
		||||
			throw new IOException();
 | 
			
		||||
			
 | 
			
		||||
			// Make the directory.
 | 
			
		||||
			File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd/", path);
 | 
			
		||||
			dir.mkdirs();
 | 
			
		||||
			
 | 
			
		||||
			// Recurse on the contents.
 | 
			
		||||
			for (String entry : contents) {
 | 
			
		||||
				copyAsset(path + "/" + entry);
 | 
			
		||||
			}
 | 
			
		||||
			} catch (IOException e) {
 | 
			
		||||
			copyFileAsset(path);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
		* Copy the asset file specified by path to app's data directory. Assumes
 | 
			
		||||
		* parent directories have already been created.
 | 
			
		||||
		* 
 | 
			
		||||
		* @param path
 | 
			
		||||
		* Path to asset, relative to app's assets directory.
 | 
			
		||||
	*/
 | 
			
		||||
	private void copyFileAsset(String path) {
 | 
			
		||||
		File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd/", path);
 | 
			
		||||
		try {
 | 
			
		||||
			InputStream in = getAssets().open(path);
 | 
			
		||||
			OutputStream out = new FileOutputStream(file);
 | 
			
		||||
			byte[] buffer = new byte[1024];
 | 
			
		||||
			int read = in.read(buffer);
 | 
			
		||||
			while (read != -1) {
 | 
			
		||||
				out.write(buffer, 0, read);
 | 
			
		||||
				read = in.read(buffer);
 | 
			
		||||
			}
 | 
			
		||||
			out.close();
 | 
			
		||||
			in.close();
 | 
			
		||||
			} catch (IOException e) {
 | 
			
		||||
			Log.e(TAG, "", e);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue