Browse Source

i2pd 2.33.0-63, tabulation, gradle configuration

Signed-off-by: R4SAS <r4sas@i2pmail.org>
pull/18/head
R4SAS 4 years ago
parent
commit
d4110e64d9
Signed by: r4sas
GPG Key ID: 66F6C87B98EBCFE2
  1. 21
      app/build.gradle
  2. 2
      app/jni/i2pd
  3. 343
      app/src/main/java/org/purplei2p/i2pd/DaemonSingleton.java
  4. 260
      app/src/main/java/org/purplei2p/i2pd/ForegroundService.java
  5. 1187
      app/src/main/java/org/purplei2p/i2pd/I2PDActivity.java
  6. 305
      app/src/main/java/org/purplei2p/i2pd/I2PDPermsAskerActivity.java
  7. 48
      app/src/main/java/org/purplei2p/i2pd/I2PDPermsExplanationActivity.java
  8. 36
      app/src/main/java/org/purplei2p/i2pd/I2PD_JNI.java
  9. 33
      app/src/main/java/org/purplei2p/i2pd/NetworkStateChangeReceiver.java
  10. 2
      app/src/main/res/values/strings.xml
  11. 2
      build.gradle
  12. 12
      gradle/wrapper/gradle-wrapper.properties
  13. 1
      settings.gradle

21
app/build.gradle

@ -3,7 +3,7 @@ plugins {
} }
dependencies { dependencies {
implementation 'androidx.core:core:1.0.2' implementation 'androidx.core:core:1.3.0'
} }
android { android {
@ -14,8 +14,8 @@ android {
applicationId "org.purplei2p.i2pd" applicationId "org.purplei2p.i2pd"
targetSdkVersion 29 targetSdkVersion 29
minSdkVersion 14 minSdkVersion 14
versionCode 23210 versionCode 23300
versionName "2.32.1" versionName "2.33.0-63-g2648f1ba"
setProperty("archivesBaseName", archivesBaseName + "-" + versionName) setProperty("archivesBaseName", archivesBaseName + "-" + versionName)
ndk { ndk {
@ -39,14 +39,13 @@ android {
enable true enable true
reset() reset()
include "armeabi-v7a", "arm64-v8a", "x86", "x86_64" include "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
//include "armeabi-v7a", "x86"
universalApk true universalApk true
} }
} }
signingConfigs { signingConfigs {
orignal { release {
storeFile file("i2pdapk.jks") storeFile file('i2pdapk.jks')
storePassword "android" storePassword "android"
keyAlias "i2pdapk" keyAlias "i2pdapk"
keyPassword "android" keyPassword "android"
@ -55,10 +54,12 @@ android {
buildTypes { buildTypes {
release { release {
minifyEnabled false signingConfig signingConfigs.release
signingConfig signingConfigs.orignal
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
} }
debug {
jniDebuggable = true
}
} }
externalNativeBuild { externalNativeBuild {
@ -68,8 +69,8 @@ android {
} }
compileOptions { compileOptions {
sourceCompatibility = '1.8' sourceCompatibility = "1.8"
targetCompatibility = '1.8' targetCompatibility = "1.8"
} }
} }

2
app/jni/i2pd

@ -1 +1 @@
Subproject commit 6735b2686b6c13a36546dd794ee49b4d583565e0 Subproject commit 2648f1ba89d5032262a72ca8b2d2d8a70e441b9a

343
app/src/main/java/org/purplei2p/i2pd/DaemonSingleton.java

@ -2,180 +2,183 @@ package org.purplei2p.i2pd;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import android.os.Environment; import android.os.Environment;
import android.util.Log; import android.util.Log;
import org.purplei2p.i2pd.R; import org.purplei2p.i2pd.R;
public class DaemonSingleton { public class DaemonSingleton {
private static final String TAG = "i2pd"; private static final String TAG = "i2pd";
private static final DaemonSingleton instance = new DaemonSingleton(); private static final DaemonSingleton instance = new DaemonSingleton();
public interface StateUpdateListener { public interface StateUpdateListener {
void daemonStateUpdate(); void daemonStateUpdate();
} }
private final Set<StateUpdateListener> stateUpdateListeners = new HashSet<>(); private final Set<StateUpdateListener> stateUpdateListeners = new HashSet<>();
public static DaemonSingleton getInstance() { public static DaemonSingleton getInstance() {
return instance; return instance;
} }
public synchronized void addStateChangeListener(StateUpdateListener listener) { public synchronized void addStateChangeListener(StateUpdateListener listener) {
stateUpdateListeners.add(listener); stateUpdateListeners.add(listener);
} }
public synchronized void removeStateChangeListener(StateUpdateListener listener) { public synchronized void removeStateChangeListener(StateUpdateListener listener) {
stateUpdateListeners.remove(listener); stateUpdateListeners.remove(listener);
} }
private synchronized void setState(State newState) { private synchronized void setState(State newState) {
if (newState == null) if (newState == null)
throw new NullPointerException(); throw new NullPointerException();
State oldState = state; State oldState = state;
if (oldState == null) if (oldState == null)
throw new NullPointerException(); throw new NullPointerException();
if (oldState.equals(newState)) if (oldState.equals(newState))
return; return;
state = newState; state = newState;
fireStateUpdate1(); fireStateUpdate1();
} }
public synchronized void stopAcceptingTunnels() { public synchronized void stopAcceptingTunnels() {
if (isStartedOkay()) { if (isStartedOkay()) {
setState(State.gracefulShutdownInProgress); setState(State.gracefulShutdownInProgress);
I2PD_JNI.stopAcceptingTunnels(); I2PD_JNI.stopAcceptingTunnels();
} }
} }
public synchronized void startAcceptingTunnels() { public synchronized void startAcceptingTunnels() {
if (isStartedOkay()) { if (isStartedOkay()) {
setState(State.startedOkay); setState(State.startedOkay);
I2PD_JNI.startAcceptingTunnels(); I2PD_JNI.startAcceptingTunnels();
} }
} }
public synchronized void reloadTunnelsConfigs() { public synchronized void reloadTunnelsConfigs() {
if (isStartedOkay()) { if (isStartedOkay()) {
I2PD_JNI.reloadTunnelsConfigs(); I2PD_JNI.reloadTunnelsConfigs();
} }
} }
public synchronized int GetTransitTunnelsCount() { public synchronized int GetTransitTunnelsCount() {
return I2PD_JNI.GetTransitTunnelsCount(); return I2PD_JNI.GetTransitTunnelsCount();
} }
private volatile boolean startedOkay; private volatile boolean startedOkay;
public enum State { public enum State {
uninitialized(R.string.uninitialized), uninitialized(R.string.uninitialized),
starting(R.string.starting), starting(R.string.starting),
jniLibraryLoaded(R.string.jniLibraryLoaded), jniLibraryLoaded(R.string.jniLibraryLoaded),
startedOkay(R.string.startedOkay), startedOkay(R.string.startedOkay),
startFailed(R.string.startFailed), startFailed(R.string.startFailed),
gracefulShutdownInProgress(R.string.gracefulShutdownInProgress), gracefulShutdownInProgress(R.string.gracefulShutdownInProgress),
stopped(R.string.stopped); stopped(R.string.stopped);
State(int statusStringResourceId) { State(int statusStringResourceId) {
this.statusStringResourceId = statusStringResourceId; this.statusStringResourceId = statusStringResourceId;
} }
private final int statusStringResourceId; private final int statusStringResourceId;
public int getStatusStringResourceId() { public int getStatusStringResourceId() {
return statusStringResourceId; return statusStringResourceId;
} }
}; }
private volatile State state = State.uninitialized; ;
public State getState() { private volatile State state = State.uninitialized;
return state;
} public State getState() {
return state;
{ }
setState(State.starting);
new Thread(new Runnable() { {
setState(State.starting);
@Override new Thread(new Runnable() {
public void run() {
try { @Override
I2PD_JNI.loadLibraries(); public void run() {
setState(State.jniLibraryLoaded); try {
} catch (Throwable tr) { I2PD_JNI.loadLibraries();
lastThrowable = tr; setState(State.jniLibraryLoaded);
setState(State.startFailed); } catch (Throwable tr) {
return; lastThrowable = tr;
} setState(State.startFailed);
try { return;
synchronized (DaemonSingleton.this) { }
I2PD_JNI.setDataDir(Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd"); try {
daemonStartResult = I2PD_JNI.startDaemon(); synchronized (DaemonSingleton.this) {
if ("ok".equals(daemonStartResult)) { I2PD_JNI.setDataDir(Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd");
setState(State.startedOkay); daemonStartResult = I2PD_JNI.startDaemon();
setStartedOkay(true); if ("ok".equals(daemonStartResult)) {
} else setState(State.startedOkay);
setState(State.startFailed); setStartedOkay(true);
} } else
} catch (Throwable tr) { setState(State.startFailed);
lastThrowable = tr; }
setState(State.startFailed); } catch (Throwable tr) {
} lastThrowable = tr;
} setState(State.startFailed);
}
}, "i2pdDaemonStart").start(); }
}
}, "i2pdDaemonStart").start();
private Throwable lastThrowable; }
private String daemonStartResult = "N/A";
private Throwable lastThrowable;
private void fireStateUpdate1() { private String daemonStartResult = "N/A";
Log.i(TAG, "daemon state change: " + state);
for (StateUpdateListener listener : stateUpdateListeners) { private void fireStateUpdate1() {
try { Log.i(TAG, "daemon state change: " + state);
listener.daemonStateUpdate(); for (StateUpdateListener listener : stateUpdateListeners) {
} catch (Throwable tr) { try {
Log.e(TAG, "exception in listener ignored", tr); listener.daemonStateUpdate();
} } catch (Throwable tr) {
} Log.e(TAG, "exception in listener ignored", tr);
} }
}
public Throwable getLastThrowable() { }
return lastThrowable;
} public Throwable getLastThrowable() {
return lastThrowable;
public String getDaemonStartResult() { }
return daemonStartResult;
} public String getDaemonStartResult() {
return daemonStartResult;
private final Object startedOkayLock = new Object(); }
public boolean isStartedOkay() { private final Object startedOkayLock = new Object();
synchronized (startedOkayLock) {
return startedOkay; public boolean isStartedOkay() {
} synchronized (startedOkayLock) {
} return startedOkay;
}
private void setStartedOkay(boolean startedOkay) { }
synchronized (startedOkayLock) {
this.startedOkay = startedOkay; private void setStartedOkay(boolean startedOkay) {
} synchronized (startedOkayLock) {
} this.startedOkay = startedOkay;
}
public synchronized void stopDaemon() { }
if (isStartedOkay()) {
try { public synchronized void stopDaemon() {
I2PD_JNI.stopDaemon(); if (isStartedOkay()) {
} catch(Throwable tr) { try {
Log.e(TAG, "", tr); I2PD_JNI.stopDaemon();
} } catch (Throwable tr) {
Log.e(TAG, "", tr);
setStartedOkay(false); }
setState(State.stopped);
} setStartedOkay(false);
} setState(State.stopped);
}
}
} }

260
app/src/main/java/org/purplei2p/i2pd/ForegroundService.java

@ -10,140 +10,144 @@ import android.content.Intent;
import android.os.Binder; import android.os.Binder;
import android.os.Build; import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import android.util.Log; import android.util.Log;
public class ForegroundService extends Service { public class ForegroundService extends Service {
private static final String TAG="FgService"; private static final String TAG = "FgService";
private volatile boolean shown; private volatile boolean shown;
private final DaemonSingleton.StateUpdateListener daemonStateUpdatedListener = private final DaemonSingleton.StateUpdateListener daemonStateUpdatedListener =
new DaemonSingleton.StateUpdateListener() { new DaemonSingleton.StateUpdateListener() {
@Override @Override
public void daemonStateUpdate() { public void daemonStateUpdate() {
try { try {
synchronized (ForegroundService.this) { synchronized (ForegroundService.this) {
if (shown) cancelNotification(); if (shown) cancelNotification();
showNotification(); showNotification();
} }
} catch (Throwable tr) { } catch (Throwable tr) {
Log.e(TAG,"error ignored",tr); Log.e(TAG, "error ignored", tr);
} }
} }
}; };
private NotificationManager notificationManager; private NotificationManager notificationManager;
// Unique Identification Number for the Notification. // Unique Identification Number for the Notification.
// We use it on Notification start, and to cancel it. // We use it on Notification start, and to cancel it.
private int NOTIFICATION = 1; private int NOTIFICATION = 1;
/** /**
* Class for clients to access. Because we know this service always * Class for clients to access. Because we know this service always
* runs in the same process as its clients, we don't need to deal with * runs in the same process as its clients, we don't need to deal with
* IPC. * IPC.
*/ */
public class LocalBinder extends Binder { public class LocalBinder extends Binder {
ForegroundService getService() { ForegroundService getService() {
return ForegroundService.this; return ForegroundService.this;
} }
} }
@Override @Override
public void onCreate() { public void onCreate() {
notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
synchronized (this) { synchronized (this) {
DaemonSingleton.getInstance().addStateChangeListener(daemonStateUpdatedListener); DaemonSingleton.getInstance().addStateChangeListener(daemonStateUpdatedListener);
if (!shown) daemonStateUpdatedListener.daemonStateUpdate(); if (!shown) daemonStateUpdatedListener.daemonStateUpdate();
} }
// Tell the user we started. // Tell the user we started.
// Toast.makeText(this, R.string.i2pd_service_started, Toast.LENGTH_SHORT).show(); // Toast.makeText(this, R.string.i2pd_service_started, Toast.LENGTH_SHORT).show();
} }
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("ForegroundService", "Received start id " + startId + ": " + intent); Log.i("ForegroundService", "Received start id " + startId + ": " + intent);
return START_STICKY; return START_STICKY;
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
DaemonSingleton.getInstance().removeStateChangeListener(daemonStateUpdatedListener); DaemonSingleton.getInstance().removeStateChangeListener(daemonStateUpdatedListener);
cancelNotification(); cancelNotification();
} }
private synchronized void cancelNotification() { private synchronized void cancelNotification() {
// Cancel the persistent notification. // Cancel the persistent notification.
notificationManager.cancel(NOTIFICATION); notificationManager.cancel(NOTIFICATION);
stopForeground(true); stopForeground(true);
// Tell the user we stopped. // Tell the user we stopped.
//Toast.makeText(this, R.string.i2pd_service_stopped, Toast.LENGTH_SHORT).show(); //Toast.makeText(this, R.string.i2pd_service_stopped, Toast.LENGTH_SHORT).show();
shown=false; shown = false;
} }
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
return mBinder; return mBinder;
} }
// This is the object that receives interactions from clients. See // This is the object that receives interactions from clients. See
// RemoteService for a more complete example. // RemoteService for a more complete example.
private final IBinder mBinder = new LocalBinder(); private final IBinder mBinder = new LocalBinder();
/** /**
* Show a notification while this service is running. * Show a notification while this service is running.
*/ */
private synchronized void showNotification() { private synchronized void showNotification() {
// In this sample, we'll use the same text for the ticker and the expanded notification // In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getText(DaemonSingleton.getInstance().getState().getStatusStringResourceId()); CharSequence text = getText(DaemonSingleton.getInstance().getState().getStatusStringResourceId());
// The PendingIntent to launch our activity if the user selects this notification // The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, I2PDActivity.class), 0); new Intent(this, I2PDActivity.class), 0);
// If earlier version channel ID is not used // If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context) // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
String channelId = Build.VERSION.SDK_INT >= 26 ? createNotificationChannel() : ""; String channelId = Build.VERSION.SDK_INT >= 26 ? createNotificationChannel() : "";
// Set the info for the views that show in the notification panel. // Set the info for the views that show in the notification panel.
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId) NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId)
.setOngoing(true) .setOngoing(true)
.setSmallIcon(R.drawable.itoopie_notification_icon); // the status icon .setSmallIcon(R.drawable.itoopie_notification_icon); // the status icon
if(Build.VERSION.SDK_INT >= 16) builder = builder.setPriority(Notification.PRIORITY_DEFAULT); if (Build.VERSION.SDK_INT >= 16)
if(Build.VERSION.SDK_INT >= 21) builder = builder.setCategory(Notification.CATEGORY_SERVICE); builder = builder.setPriority(Notification.PRIORITY_DEFAULT);
Notification notification = builder if (Build.VERSION.SDK_INT >= 21)
.setTicker(text) // the status text builder = builder.setCategory(Notification.CATEGORY_SERVICE);
.setWhen(System.currentTimeMillis()) // the time stamp Notification notification = builder
.setContentTitle(getText(R.string.app_name)) // the label of the entry .setTicker(text) // the status text
.setContentText(text) // the contents of the entry .setWhen(System.currentTimeMillis()) // the time stamp
.setContentIntent(contentIntent) // The intent to send when the entry is clicked .setContentTitle(getText(R.string.app_name)) // the label of the entry
.build(); .setContentText(text) // the contents of the entry
.setContentIntent(contentIntent) // The intent to send when the entry is clicked
// Send the notification. .build();
//mNM.notify(NOTIFICATION, notification);
startForeground(NOTIFICATION, notification); // Send the notification.
shown = true; //mNM.notify(NOTIFICATION, notification);
} startForeground(NOTIFICATION, notification);
shown = true;
@RequiresApi(Build.VERSION_CODES.O) }
private synchronized String createNotificationChannel() {
String channelId = getString(R.string.app_name); @RequiresApi(Build.VERSION_CODES.O)
CharSequence channelName = "I2Pd service"; private synchronized String createNotificationChannel() {
NotificationChannel chan = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_LOW); String channelId = getString(R.string.app_name);
//chan.setLightColor(Color.PURPLE); CharSequence channelName = "I2Pd service";
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); NotificationChannel chan = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_LOW);
NotificationManager service = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); //chan.setLightColor(Color.PURPLE);
if(service!=null)service.createNotificationChannel(chan); chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
else Log.e(TAG, "error: NOTIFICATION_SERVICE is null"); NotificationManager service = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
return channelId; if (service != null) service.createNotificationChannel(chan);
} else Log.e(TAG, "error: NOTIFICATION_SERVICE is null");
return channelId;
}
private static final DaemonSingleton daemon = DaemonSingleton.getInstance(); private static final DaemonSingleton daemon = DaemonSingleton.getInstance();
} }

1187
app/src/main/java/org/purplei2p/i2pd/I2PDActivity.java

File diff suppressed because it is too large Load Diff

305
app/src/main/java/org/purplei2p/i2pd/I2PDPermsAskerActivity.java

@ -15,156 +15,157 @@ import java.lang.reflect.Method;
//android.permission.WRITE_EXTERNAL_STORAGE //android.permission.WRITE_EXTERNAL_STORAGE
public class I2PDPermsAskerActivity extends Activity { public class I2PDPermsAskerActivity extends Activity {
private static final int PERMISSION_WRITE_EXTERNAL_STORAGE = 0; private static final int PERMISSION_WRITE_EXTERNAL_STORAGE = 0;
private Button button_request_write_ext_storage_perms; private Button button_request_write_ext_storage_perms;
private TextView textview_retry; private TextView textview_retry;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
//if less than Android 6, no runtime perms req system present //if less than Android 6, no runtime perms req system present
if (android.os.Build.VERSION.SDK_INT < 23) { if (android.os.Build.VERSION.SDK_INT < 23) {
startMainActivity(); startMainActivity();
return; return;
} }
setContentView(R.layout.activity_perms_asker); setContentView(R.layout.activity_perms_asker);
button_request_write_ext_storage_perms = (Button) findViewById(R.id.button_request_write_ext_storage_perms); button_request_write_ext_storage_perms = (Button) findViewById(R.id.button_request_write_ext_storage_perms);
textview_retry = (TextView) findViewById(R.id.textview_retry); textview_retry = (TextView) findViewById(R.id.textview_retry);
button_request_write_ext_storage_perms.setOnClickListener(new View.OnClickListener() { button_request_write_ext_storage_perms.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
request_write_ext_storage_perms(); request_write_ext_storage_perms();
} }
}); });
request_write_ext_storage_perms(); request_write_ext_storage_perms();
} }
private void request_write_ext_storage_perms() { private void request_write_ext_storage_perms() {
textview_retry.setVisibility(TextView.GONE); textview_retry.setVisibility(TextView.GONE);
button_request_write_ext_storage_perms.setVisibility(Button.GONE); button_request_write_ext_storage_perms.setVisibility(Button.GONE);
Method methodCheckPermission; Method methodCheckPermission;
Method method_shouldShowRequestPermissionRationale; Method method_shouldShowRequestPermissionRationale;
Method method_requestPermissions; Method method_requestPermissions;
try { try {
methodCheckPermission = getClass().getMethod("checkSelfPermission", String.class); methodCheckPermission = getClass().getMethod("checkSelfPermission", String.class);
method_shouldShowRequestPermissionRationale = method_shouldShowRequestPermissionRationale =
getClass().getMethod("shouldShowRequestPermissionRationale", String.class); getClass().getMethod("shouldShowRequestPermissionRationale", String.class);
method_requestPermissions = method_requestPermissions =
getClass().getMethod("requestPermissions", String[].class, int.class); getClass().getMethod("requestPermissions", String[].class, int.class);
} catch (NoSuchMethodException e) { } catch (NoSuchMethodException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
Integer resultObj; Integer resultObj;
try { try {
resultObj = (Integer) methodCheckPermission.invoke( resultObj = (Integer) methodCheckPermission.invoke(
this, Manifest.permission.WRITE_EXTERNAL_STORAGE); this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
} catch (Throwable e) { } catch (Throwable e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
if (resultObj != PackageManager.PERMISSION_GRANTED) { if (resultObj != PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation? // Should we show an explanation?
Boolean aBoolean; Boolean aBoolean;
try { try {
aBoolean = (Boolean) method_shouldShowRequestPermissionRationale.invoke(this, aBoolean = (Boolean) method_shouldShowRequestPermissionRationale.invoke(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE); Manifest.permission.WRITE_EXTERNAL_STORAGE);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
if (aBoolean) { if (aBoolean) {
// Show an explanation to the user *asynchronously* -- don't block // Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user // this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission. // sees the explanation, try again to request the permission.
showExplanation(); showExplanation();
} else { } else {
// No explanation needed, we can request the permission. // No explanation needed, we can request the permission.
try { try {
method_requestPermissions.invoke(this, method_requestPermissions.invoke(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
PERMISSION_WRITE_EXTERNAL_STORAGE); PERMISSION_WRITE_EXTERNAL_STORAGE);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
} else startMainActivity(); } else startMainActivity();
} }
@Override @Override
public void onRequestPermissionsResult(int requestCode, public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) { String permissions[], int[] grantResults) {
switch (requestCode) { switch (requestCode) {
case PERMISSION_WRITE_EXTERNAL_STORAGE: { case PERMISSION_WRITE_EXTERNAL_STORAGE: {
// If request is cancelled, the result arrays are empty. // If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) { && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the // permission was granted, yay! Do the
// contacts-related task you need to do. // contacts-related task you need to do.
startMainActivity(); startMainActivity();
} else { } else {
// permission denied, boo! Disable the // permission denied, boo! Disable the
// functionality that depends on this permission. // functionality that depends on this permission.
textview_retry.setText(R.string.permDenied); textview_retry.setText(R.string.permDenied);
textview_retry.setVisibility(TextView.VISIBLE); textview_retry.setVisibility(TextView.VISIBLE);
button_request_write_ext_storage_perms.setVisibility(Button.VISIBLE); button_request_write_ext_storage_perms.setVisibility(Button.VISIBLE);
} }
} }
// other 'case' lines to check for other // other 'case' lines to check for other
// permissions this app might request. // permissions this app might request.
} }
} }
private void startMainActivity() { private void startMainActivity() {
startActivity(new Intent(this, I2PDActivity.class)); startActivity(new Intent(this, I2PDActivity.class));
finish(); finish();
} }
private static final int SHOW_EXPLANATION_REQUEST = 1; // The request code private static final int SHOW_EXPLANATION_REQUEST = 1; // The request code
private void showExplanation() {
Intent intent = new Intent(this, I2PDPermsExplanationActivity.class); private void showExplanation() {
startActivityForResult(intent, SHOW_EXPLANATION_REQUEST); Intent intent = new Intent(this, I2PDPermsExplanationActivity.class);
} startActivityForResult(intent, SHOW_EXPLANATION_REQUEST);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { @Override
// Check which request we're responding to protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == SHOW_EXPLANATION_REQUEST) { // Check which request we're responding to
// Make sure the request was successful if (requestCode == SHOW_EXPLANATION_REQUEST) {
if (resultCode == RESULT_OK) { // Make sure the request was successful
// Request the permission if (resultCode == RESULT_OK) {
Method method_requestPermissions; // Request the permission
try { Method method_requestPermissions;
method_requestPermissions = try {
getClass().getMethod("requestPermissions", String[].class, int.class); method_requestPermissions =
} catch (NoSuchMethodException e) { getClass().getMethod("requestPermissions", String[].class, int.class);
throw new RuntimeException(e); } catch (NoSuchMethodException e) {
} throw new RuntimeException(e);
try { }
method_requestPermissions.invoke(this, try {
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, method_requestPermissions.invoke(this,
PERMISSION_WRITE_EXTERNAL_STORAGE); new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
} catch (Exception e) { PERMISSION_WRITE_EXTERNAL_STORAGE);
throw new RuntimeException(e); } catch (Exception e) {
} throw new RuntimeException(e);
} else { }
finish(); //close the app } else {
} finish(); //close the app
} }
} }
}
} }

48
app/src/main/java/org/purplei2p/i2pd/I2PDPermsExplanationActivity.java

@ -9,30 +9,30 @@ import android.widget.Button;
public class I2PDPermsExplanationActivity extends Activity { public class I2PDPermsExplanationActivity extends Activity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_perms_explanation); setContentView(R.layout.activity_perms_explanation);
ActionBar actionBar = getActionBar(); ActionBar actionBar = getActionBar();
if(actionBar!=null)actionBar.setHomeButtonEnabled(false); if (actionBar != null) actionBar.setHomeButtonEnabled(false);
Button button_ok = (Button) findViewById(R.id.button_ok); Button button_ok = (Button) findViewById(R.id.button_ok);
button_ok.setOnClickListener(new View.OnClickListener() { button_ok.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
returnFromActivity(); returnFromActivity();
} }
}); });
} }
private void returnFromActivity() { private void returnFromActivity() {
Intent data = new Intent(); Intent data = new Intent();
Activity parent = getParent(); Activity parent = getParent();
if (parent == null) { if (parent == null) {
setResult(Activity.RESULT_OK, data); setResult(Activity.RESULT_OK, data);
} else { } else {
parent.setResult(Activity.RESULT_OK, data); parent.setResult(Activity.RESULT_OK, data);
} }
finish(); finish();
} }
} }

36
app/src/main/java/org/purplei2p/i2pd/I2PD_JNI.java

@ -1,31 +1,31 @@
package org.purplei2p.i2pd; package org.purplei2p.i2pd;
public class I2PD_JNI { public class I2PD_JNI {
public static native String getABICompiledWith(); public static native String getABICompiledWith();
/** /**
* returns error info if failed * returns error info if failed
* returns "ok" if daemon initialized and started okay * returns "ok" if daemon initialized and started okay
*/ */
public static native String startDaemon(); public static native String startDaemon();
//should only be called after startDaemon() success //should only be called after startDaemon() success
public static native void stopDaemon(); public static native void stopDaemon();
public static native void stopAcceptingTunnels(); public static native void stopAcceptingTunnels();
public static native void startAcceptingTunnels(); public static native void startAcceptingTunnels();
public static native void reloadTunnelsConfigs(); public static native void reloadTunnelsConfigs();
public static native void onNetworkStateChanged(boolean isConnected); public static native void onNetworkStateChanged(boolean isConnected);
public static native void setDataDir(String jdataDir); public static native void setDataDir(String jdataDir);
public static native int GetTransitTunnelsCount(); public static native int GetTransitTunnelsCount();
public static void loadLibraries() { public static void loadLibraries() {
//System.loadLibrary("c++_shared"); //System.loadLibrary("c++_shared");
System.loadLibrary("i2pd"); System.loadLibrary("i2pd");
} }
} }

33
app/src/main/java/org/purplei2p/i2pd/NetworkStateChangeReceiver.java

@ -9,22 +9,23 @@ import android.net.NetworkInfo;
public class NetworkStateChangeReceiver extends BroadcastReceiver { public class NetworkStateChangeReceiver extends BroadcastReceiver {
private static final String TAG = "i2pd"; private static final String TAG = "i2pd";
//api level 1 //api level 1
@Override @Override
public void onReceive(final Context context, final Intent intent) { public void onReceive(final Context context, final Intent intent) {
Log.d(TAG,"Network state change"); Log.d(TAG, "Network state change");
try { try {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = cm.getActiveNetworkInfo(); assert cm != null;
boolean isConnected = activeNetworkInfo!=null && activeNetworkInfo.isConnected(); NetworkInfo activeNetworkInfo = cm.getActiveNetworkInfo();
// https://developer.android.com/training/monitoring-device-state/connectivity-monitoring.html?hl=ru boolean isConnected = activeNetworkInfo != null && activeNetworkInfo.isConnected();
// boolean isWiFi = activeNetworkInfo!=null && (activeNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI); // https://developer.android.com/training/monitoring-device-state/connectivity-monitoring.html?hl=ru
// boolean isWiFi = activeNetworkInfo!=null && (activeNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI);
I2PD_JNI.onNetworkStateChanged(isConnected); I2PD_JNI.onNetworkStateChanged(isConnected);
} catch (Throwable tr) { } catch (Throwable tr) {
Log.d(TAG,"",tr); Log.d(TAG, "", tr);
} }
} }
} }

2
app/src/main/res/values/strings.xml

@ -20,7 +20,7 @@
<string name="startFailed">Start failed</string> <string name="startFailed">Start failed</string>
<string name="stopped">Application stopped</string> <string name="stopped">Application stopped</string>
<string name="remaining">remaining</string> <string name="remaining">remaining</string>
<string name="ok">OK</string> <string name="ok" translatable="false">OK</string>
<string name="title_activity_i2_pdperms_asker_prompt">Prompt</string> <string name="title_activity_i2_pdperms_asker_prompt">Prompt</string>
<string name="permDenied">SD card write permission denied, you need to allow this to continue</string> <string name="permDenied">SD card write permission denied, you need to allow this to continue</string>

2
build.gradle

@ -5,7 +5,7 @@ buildscript {
google() google()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.4.2' classpath 'com.android.tools.build:gradle:3.6.1'
} }
} }

12
gradle/wrapper/gradle-wrapper.properties vendored

@ -1,6 +1,6 @@
#Tue Aug 20 14:39:08 MSK 2019 #Fri Jun 12 07:37:19 MSK 2020
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip

1
settings.gradle

@ -1 +1,2 @@
include ':app' include ':app'
project(":app").name = "i2pd"
Loading…
Cancel
Save