diff --git a/.gitignore b/.gitignore
index e79c41e..fd1f9dc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,4 @@ build
*.iml
*.local
*.jks
+*.tmp
diff --git a/README.md b/README.md
index fdec169..5abc3ab 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,8 @@ This repository contains Android application sources of i2pd
### Install g++, OpenJDK 11+, gradle 5.1+
+ * Note: openjdk 17 has also been tested okay.
+
```bash
sudo apt-get install g++ openjdk-11-jdk gradle
```
@@ -58,7 +60,7 @@ pushd binary/jni
./build.sh -d
popd
-gradle clean assembleDebug
+./gradlew clean assembleDebug
```
You will find APKs in `app/build/outputs/apk`
diff --git a/app/build.gradle b/app/build.gradle
index 0178977..53505f3 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -17,16 +17,12 @@ dependencies {
}
android {
- lintOptions {
- // Not so a good way
- disable 'DuplicatePlatformClasses'
- }
-
- compileSdkVersion 33
+ // do not remove, it is deprecated & nevertheless required
+ compileSdkVersion 35
defaultConfig {
applicationId "org.purplei2p.i2pd"
- targetSdkVersion 33
+ targetSdkVersion 35
// TODO: 24?
minSdkVersion 16
versionCode 2550000
@@ -89,6 +85,12 @@ android {
targetCompatibility = JavaVersion.VERSION_1_8
}
namespace 'org.purplei2p.i2pd'
+ lint {
+ disable 'DuplicatePlatformClasses'
+ }
+ buildFeatures {
+ buildConfig = true
+ }
}
ext.abiCodes = ['armeabi-v7a': 1, 'x86': 2, 'arm64-v8a': 3, 'x86_64': 4]
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e7c2d76..23bec2e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -24,7 +24,7 @@
android:usesCleartextTraffic="true">
@@ -62,7 +62,7 @@
@@ -80,12 +80,12 @@
android:enabled="true" />
+ android:parentActivityName=".I2PDPermissionsAskerActivity">
+ android:value="org.purplei2p.i2pd.I2PDPermissionsAskerActivity" />
updateStatusText();
private void updateStatusText() {
runOnUiThread(() -> {
@@ -67,15 +54,15 @@ private void updateStatusText() {
if (textView == null)
return;
- Throwable tr = daemon.getLastThrowable();
+ Throwable tr = getDaemon().getLastThrowable();
if (tr != null) {
textView.setText(throwableToString(tr));
return;
}
- DaemonWrapper.State state = daemon.getState();
+ DaemonWrapper.State state = getDaemon().getState();
- if (daemon.isStartedOkay()) {
+ if (getDaemon().isStartedOkay()) {
HTTPProxyState.setChecked(I2PD_JNI.getHTTPProxyState());
SOCKSProxyState.setChecked(I2PD_JNI.getSOCKSProxyState());
BOBState.setChecked(I2PD_JNI.getBOBState());
@@ -83,8 +70,8 @@ private void updateStatusText() {
I2CPState.setChecked(I2PD_JNI.getI2CPState());
}
- String startResultStr = DaemonWrapper.State.startFailed.equals(state) ? String.format(": %s", daemon.getDaemonStartResult()) : "";
- String graceStr = DaemonWrapper.State.gracefulShutdownInProgress.equals(state) ? String.format(": %s %s", formatGraceTimeRemaining(), getText(R.string.remaining)) : "";
+ String startResultStr = DaemonWrapper.State.startFailed.equals(state) ? String.format(": %s", getDaemon().getDaemonStartResult()) : "";
+ String graceStr = DaemonWrapper.State.gracefulShutdownInProgress.equals(state) ? String.format(": %s %s", I2PDApplication.formatGraceTimeRemaining(), getText(R.string.remaining)) : "";
textView.setText(String.format("%s%s%s", getText(state.getStatusStringResourceId()), startResultStr, graceStr));
} catch (Throwable tr) {
Log.e(TAG,"error ignored",tr);
@@ -92,41 +79,25 @@ private void updateStatusText() {
});
}
- private static volatile long graceStartedMillis;
- private static final Object graceStartedMillis_LOCK = new Object();
- private Menu optionsMenu;
-
- 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;
+ private DaemonWrapper getDaemon() {
+ return I2PDApplication.getDaemonWrapper();
}
+ private Menu optionsMenu;
+
@Override
public void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
- startService(new Intent(this, ForegroundService.class));
- textView = (TextView) findViewById(R.id.appStatusText);
- HTTPProxyState = (CheckBox) findViewById(R.id.service_httpproxy_box);
- SOCKSProxyState = (CheckBox) findViewById(R.id.service_socksproxy_box);
- BOBState = (CheckBox) findViewById(R.id.service_bob_box);
- SAMState = (CheckBox) findViewById(R.id.service_sam_box);
- I2CPState = (CheckBox) findViewById(R.id.service_i2cp_box);
-
- if (daemon == null) {
- ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
- daemon = new DaemonWrapper(getAssets(), connectivityManager);
- }
- ForegroundService.init(daemon);
+ textView = findViewById(R.id.appStatusText);
+ HTTPProxyState = findViewById(R.id.service_httpproxy_box);
+ SOCKSProxyState = findViewById(R.id.service_socksproxy_box);
+ BOBState = findViewById(R.id.service_bob_box);
+ SAMState = findViewById(R.id.service_sam_box);
+ I2CPState = findViewById(R.id.service_i2cp_box);
- daemon.addStateChangeListener(daemonStateUpdatedListener);
- daemonStateUpdatedListener.daemonStateUpdate(DaemonWrapper.State.uninitialized, daemon.getState());
+ getDaemon().addStateChangeListener(daemonStateUpdatedListener);
// request permissions
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
@@ -142,13 +113,11 @@ public void onCreate(Bundle savedInstanceState) {
}
}
- doBindService();
-
- final Timer gracefulQuitTimer = getGracefulQuitTimer();
+ final Timer gracefulQuitTimer = I2PDApplication.getGracefulQuitTimer();
if (gracefulQuitTimer != null) {
long gracefulStopAtMillis;
- synchronized (graceStartedMillis_LOCK) {
- gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS;
+ synchronized (I2PDApplication.graceStartedMillis_LOCK) {
+ gracefulStopAtMillis = I2PDApplication.graceStartedMillis + I2PDApplication.GRACEFUL_DELAY_MILLIS;
}
rescheduleGraceStop(gracefulQuitTimer, gracefulStopAtMillis);
}
@@ -160,19 +129,7 @@ public void onCreate(Bundle savedInstanceState) {
protected void onDestroy() {
super.onDestroy();
textView = null;
- ForegroundService.deinit();
- daemon.removeStateChangeListener(daemonStateUpdatedListener);
- //cancelGracefulStop0();
- try {
- doUnbindService();
- } catch (IllegalArgumentException ex) {
- Log.e(TAG, "throwable caught and ignored", ex);
- if (ex.getMessage().startsWith("Service not registered: " + org.purplei2p.i2pd.I2PDActivity.class.getName())) {
- Log.i(TAG, "Service not registered exception seems to be normal, not a bug it seems.");
- }
- } catch (Throwable tr) {
- Log.e(TAG, "throwable caught and ignored", tr);
- }
+ getDaemon().removeStateChangeListener(daemonStateUpdatedListener);
}
@Override
@@ -189,7 +146,7 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis
}
private void cancelGracefulStop0() {
- Timer gracefulQuitTimer = getGracefulQuitTimer();
+ Timer gracefulQuitTimer = I2PDApplication.getGracefulQuitTimer();
if (gracefulQuitTimer != null) {
gracefulQuitTimer.cancel();
setGracefulQuitTimer(null);
@@ -204,58 +161,6 @@ private CharSequence throwableToString(Throwable tr) {
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.
@@ -282,8 +187,8 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
return true;
case R.id.action_graceful_stop:
- synchronized (graceStartedMillis_LOCK) {
- if (getGracefulQuitTimer() != null)
+ synchronized (I2PDApplication.graceStartedMillis_LOCK) {
+ if (I2PDApplication.getGracefulQuitTimer() != null)
cancelGracefulStop();
else
i2pdGracefulStop();
@@ -299,7 +204,7 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
return true;
case R.id.action_start_webview:
- if(daemon.isStartedOkay())
+ if(getDaemon().isStartedOkay())
startActivity(new Intent(getApplicationContext(), WebConsoleActivity.class));
else
Toast.makeText(this,"I2Pd not was started!", Toast.LENGTH_SHORT).show();
@@ -325,7 +230,7 @@ private void onActionBatteryOptimizations() {
private void onReloadTunnelsConfig() {
Log.i(TAG, "reloading tunnels");
- daemon.reloadTunnelsConfigs();
+ getDaemon().reloadTunnelsConfigs();
Toast.makeText(this, R.string.tunnels_reloading, Toast.LENGTH_SHORT).show();
}
@@ -335,7 +240,7 @@ private void i2pdStop() {
textView.setText(getText(R.string.stopping));
new Thread(() -> {
try {
- daemon.stopDaemon();
+ getDaemon().stopDaemon();
} catch (Throwable tr) {
Log.e(TAG, "", tr);
}
@@ -343,14 +248,12 @@ private void i2pdStop() {
}, "stop").start();
}
- private static volatile Timer gracefulQuitTimer;
-
private void i2pdGracefulStop() {
- if (daemon.getState() == DaemonWrapper.State.stopped) {
+ if (getDaemon().getState() == DaemonWrapper.State.stopped) {
Toast.makeText(this, R.string.already_stopped, Toast.LENGTH_SHORT).show();
return;
}
- if (getGracefulQuitTimer() != null) {
+ if (I2PDApplication.getGracefulQuitTimer() != null) {
Toast.makeText(this, R.string.graceful_stop_is_already_in_progress, Toast.LENGTH_SHORT).show();
return;
}
@@ -358,12 +261,12 @@ private void i2pdGracefulStop() {
Toast.makeText(this, R.string.graceful_stop_is_in_progress, Toast.LENGTH_SHORT).show();
new Thread(() -> {
try {
- if (daemon.isStartedOkay()) {
- daemon.stopAcceptingTunnels();
+ if (getDaemon().isStartedOkay()) {
+ getDaemon().stopAcceptingTunnels();
long gracefulStopAtMillis;
- synchronized (graceStartedMillis_LOCK) {
- graceStartedMillis = System.currentTimeMillis();
- gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS;
+ synchronized (I2PDApplication.graceStartedMillis_LOCK) {
+ I2PDApplication.graceStartedMillis = System.currentTimeMillis();
+ gracefulStopAtMillis = I2PDApplication.graceStartedMillis + I2PDApplication.GRACEFUL_DELAY_MILLIS;
}
rescheduleGraceStop(null, gracefulStopAtMillis);
} else
@@ -380,8 +283,8 @@ private void cancelGracefulStop()
Log.i(TAG, "canceling graceful stop");
new Thread(() -> {
try {
- if (daemon.isStartedOkay()) {
- daemon.startAcceptingTunnels();
+ if (getDaemon().isStartedOkay()) {
+ getDaemon().startAcceptingTunnels();
runOnUiThread(() -> Toast.makeText(this, R.string.shutdown_canceled, Toast.LENGTH_SHORT).show());
} else
i2pdStop();
@@ -395,7 +298,7 @@ private void rescheduleGraceStop(Timer gracefulQuitTimerOld, long gracefulStopAt
if (gracefulQuitTimerOld != null)
gracefulQuitTimerOld.cancel();
- if (daemon.getTransitTunnelsCount() <= 0) { // no tunnels left
+ if (getDaemon().getTransitTunnelsCount() <= 0) { // no tunnels left
Log.i(TAG, "no transit tunnels left, stopping");
i2pdStop();
return;
@@ -420,19 +323,15 @@ public void run() {
gracefulQuitTimer.scheduleAtFixedRate(tickerTask, 0/*start delay*/, 1000/*millis period*/);
}
- private static Timer getGracefulQuitTimer() {
- return gracefulQuitTimer;
- }
-
private void setGracefulQuitTimer(Timer gracefulQuitTimer) {
- I2PDActivity.gracefulQuitTimer = gracefulQuitTimer;
+ I2PDApplication.gracefulQuitTimer = gracefulQuitTimer;
runOnUiThread(() -> {
Menu menu = optionsMenu;
if (menu != null) {
MenuItem item = menu.findItem(R.id.action_graceful_stop);
if (item != null) {
- synchronized (graceStartedMillis_LOCK) {
- item.setTitle(getGracefulQuitTimer() != null ? R.string.action_cancel_graceful_stop : R.string.action_graceful_stop);
+ synchronized (I2PDApplication.graceStartedMillis_LOCK) {
+ item.setTitle(I2PDApplication.getGracefulQuitTimer() != null ? R.string.action_cancel_graceful_stop : R.string.action_graceful_stop);
}
}
}
@@ -453,7 +352,7 @@ private void openBatteryOptimizationDialogIfNeeded() {
try {
startActivity(new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, Uri.parse(PACKAGE_URI_SCHEME + getPackageName())));
} catch (ActivityNotFoundException e) {
- Log.e(TAG, "BATT_OPTIM_ActvtNotFound", e);
+ Log.e(TAG, "BATT_OPTIM_ActivityNotFound", e);
Toast.makeText(this, R.string.device_does_not_support_disabling_battery_optimizations, Toast.LENGTH_SHORT).show();
}
});
@@ -506,11 +405,6 @@ private void quit() {
} catch (Throwable tr) {
Log.e(TAG, "", tr);
}
- try {
- daemon.stopDaemon();
- } catch (Throwable tr) {
- Log.e(TAG, "", tr);
- }
- System.exit(0);
+ I2PDApplication.quit();
}
}
diff --git a/app/src/main/java/org/purplei2p/i2pd/I2PDApplication.java b/app/src/main/java/org/purplei2p/i2pd/I2PDApplication.java
new file mode 100644
index 0000000..5ca66ac
--- /dev/null
+++ b/app/src/main/java/org/purplei2p/i2pd/I2PDApplication.java
@@ -0,0 +1,234 @@
+package org.purplei2p.i2pd;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.os.Build;
+import android.os.Environment;
+import android.os.IBinder;
+import android.util.Log;
+
+import androidx.core.content.ContextCompat;
+
+import java.lang.reflect.Method;
+import java.util.Timer;
+
+public class I2PDApplication extends android.app.Application {
+ public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000;
+ private static final String TAG = "App";
+ public static final Object graceStartedMillis_LOCK = new Object();
+ private static I2PDApplication instance = null;
+
+ private static volatile boolean startDaemon = false;
+ public static volatile long graceStartedMillis;
+ public static volatile Timer gracefulQuitTimer;
+
+ public I2PDApplication() {
+ I2PDApplication.instance = this;
+ }
+
+
+ public static synchronized boolean isStartDaemon() {
+ return startDaemon;
+ }
+
+ public static synchronized void setStartDaemon(boolean startDaemon) {
+ I2PDApplication.startDaemon = startDaemon;
+ }
+
+//private static final I2PD_JNI jniHolder = new I2PD_JNI();
+
+ private static volatile DaemonWrapper daemonWrapper;
+ private String versionName;
+
+ private static volatile boolean mIsBound;
+
+
+ public synchronized static DaemonWrapper getDaemonWrapper() {
+ return daemonWrapper;
+ }
+
+ public static void startForegroundService(Context context) {
+ Intent serviceIntent = new Intent(context, ForegroundService.class);
+ serviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ ContextCompat.startForegroundService(context, serviceIntent);
+ } else {
+ context.startService(serviceIntent);
+ }
+ }
+
+ public static void maybeAutostartForegroundServiceOnBoot(Context context) {
+ boolean autostartOnBoot = isAutostartOnBoot(context);
+ if(autostartOnBoot) {
+ startForegroundService(context);
+ }
+ }
+
+ public static boolean isAutostartOnBoot(Context context) {
+ SharedPreferences sharedPref = context.getSharedPreferences(
+ org.purplei2p.i2pd.BootUpReceiver.SHARED_PREF_FILE_KEY, MODE_PRIVATE);
+ return sharedPref.getBoolean(org.purplei2p.i2pd.BootUpReceiver.AUTOSTART_ON_BOOT, true);
+ }
+
+ public static I2PDApplication getInstance() {
+ return instance;
+ }
+
+ public 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;
+ }
+
+ public static Timer getGracefulQuitTimer() {
+ return gracefulQuitTimer;
+ }
+
+ @Override
+ public void onCreate() {
+ Log.d(TAG, "App.onCreate");
+ super.onCreate();
+ if(I2PDApplication.isAutostartOnBoot(getApplicationContext())){
+ Log.d(TAG, "calling App.setStartDaemon(true)");
+ I2PDApplication.setStartDaemon(true);
+ }
+ if(I2PDApplication.isStartDaemon()){
+ Log.d(TAG, "calling App.doBindService()");
+ doBindService();
+ }
+ Log.d(TAG, "calling App.maybeAutostartForegroundServiceOnBoot()");
+ maybeAutostartForegroundServiceOnBoot(getApplicationContext());
+// synchronized (this) {
+// if (getDaemonWrapper() == null) {
+// createDaemonWrapper();
+// }
+ versionName = BuildConfig.VERSION_NAME;
+// startService(new Intent(this, ForegroundService.class));
+// }
+ startService(new Intent(this, ForegroundService.class));
+ createDaemonWrapper();
+
+ //daemonWrapper.addStateChangeListener(daemonStateUpdatedListener);
+ //daemonStateUpdatedListener.daemonStateUpdate(DaemonWrapper.State.uninitialized, daemon.getState());
+ Log.d(TAG, "App.onCreate() leave");
+ }
+
+ private void createDaemonWrapper() {
+ ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ daemonWrapper = new DaemonWrapper(getAssets(), connectivityManager);
+ ForegroundService.init(daemonWrapper);
+ }
+
+ private synchronized void doBindService() {
+ 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 synchronized void doUnbindService() {
+ if (mIsBound) {
+ // Detach our existing connection.
+ unbindService(mConnection);
+ mIsBound = false;
+ }
+ }
+
+ @Override
+ public void onTerminate() {
+ quit();
+ super.onTerminate();
+ }
+
+ private final 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();
+ }
+ };
+
+ public static synchronized void quit() {
+ try {
+ if (daemonWrapper != null) daemonWrapper.stopDaemon();
+ } catch (Throwable tr) {
+ Log.e(TAG, "", tr);
+ }
+
+ try {
+ if(instance!=null)instance.doUnbindService();
+ } catch (IllegalArgumentException ex) {
+ Log.e(TAG, "throwable caught and ignored", ex);
+ final String message = ex.getMessage();
+ if (message!=null && message.startsWith("Service not registered: " + I2PDActivity.class.getName())) {
+ Log.i(TAG, "Service not registered exception seems to be normal, not a bug it seems.");
+ }
+ } catch (Throwable tr) {
+ Log.e(TAG, "throwable caught and ignored", tr);
+ }
+ try {
+ Log.d(TAG, "calling fgservice.stop");
+ ForegroundService.deinit();
+ } catch (Throwable tr) {
+ Log.e(TAG, "", tr);
+ }
+ System.exit(0);
+ }
+
+ public boolean isPermittedToWriteToExternalStorage() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ return Environment.isExternalStorageManager();
+ } else {
+ Method methodCheckPermission;
+
+ try {
+ methodCheckPermission = getClass().getMethod(
+ "checkSelfPermission", String.class);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ }
+
+ Integer resultObj;
+
+ try {
+ resultObj = (Integer) methodCheckPermission.invoke(
+ this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+
+ return resultObj != null && resultObj == PackageManager.PERMISSION_GRANTED;
+ }
+ }
+}
diff --git a/app/src/main/java/org/purplei2p/i2pd/I2PDPermsAskerActivity.java b/app/src/main/java/org/purplei2p/i2pd/I2PDPermissionsAskerActivity.java
similarity index 97%
rename from app/src/main/java/org/purplei2p/i2pd/I2PDPermsAskerActivity.java
rename to app/src/main/java/org/purplei2p/i2pd/I2PDPermissionsAskerActivity.java
index 2df2780..55c3bbc 100644
--- a/app/src/main/java/org/purplei2p/i2pd/I2PDPermsAskerActivity.java
+++ b/app/src/main/java/org/purplei2p/i2pd/I2PDPermissionsAskerActivity.java
@@ -17,11 +17,9 @@
//dangerous perms, per https://developer.android.com/guide/topics/permissions/normal-permissions.html :
//android.permission.WRITE_EXTERNAL_STORAGE
-public class I2PDPermsAskerActivity extends Activity {
+public class I2PDPermissionsAskerActivity extends Activity {
private static final int PERMISSION_WRITE_EXTERNAL_STORAGE = 0;
- private static final int PERMISSION_MANAGE_EXTERNAL_STORAGE = 0;
-
private Button button_request_write_ext_storage_perms;
private TextView textview_retry;
@@ -153,7 +151,7 @@ private void startMainActivity() {
private static final int APP_STORAGE_ACCESS_REQUEST_CODE = 2;
private void showExplanation() {
- Intent intent = new Intent(this, I2PDPermsExplanationActivity.class);
+ Intent intent = new Intent(this, I2PDPermissionsExplanationActivity.class);
startActivityForResult(intent, SHOW_EXPLANATION_REQUEST);
}
diff --git a/app/src/main/java/org/purplei2p/i2pd/I2PDPermsExplanationActivity.java b/app/src/main/java/org/purplei2p/i2pd/I2PDPermissionsExplanationActivity.java
similarity index 51%
rename from app/src/main/java/org/purplei2p/i2pd/I2PDPermsExplanationActivity.java
rename to app/src/main/java/org/purplei2p/i2pd/I2PDPermissionsExplanationActivity.java
index 33f3a28..469d168 100644
--- a/app/src/main/java/org/purplei2p/i2pd/I2PDPermsExplanationActivity.java
+++ b/app/src/main/java/org/purplei2p/i2pd/I2PDPermissionsExplanationActivity.java
@@ -4,33 +4,27 @@
import android.content.Intent;
import android.os.Bundle;
import android.app.Activity;
-import android.view.View;
import android.widget.Button;
-public class I2PDPermsExplanationActivity extends Activity {
+public class I2PDPermissionsExplanationActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_perms_explanation);
+ setContentView(R.layout.activity_permissions_explanation);
ActionBar actionBar = getActionBar();
if (actionBar != null) actionBar.setHomeButtonEnabled(false);
- Button button_ok = (Button) findViewById(R.id.button_ok);
- button_ok.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- returnFromActivity();
- }
- });
+ Button button_ok = findViewById(R.id.button_ok);
+ button_ok.setOnClickListener(view -> returnFromActivity());
}
private void returnFromActivity() {
- Intent data = new Intent();
+ Intent intent = new Intent();
Activity parent = getParent();
if (parent == null) {
- setResult(Activity.RESULT_OK, data);
+ setResult(Activity.RESULT_OK, intent);
} else {
- parent.setResult(Activity.RESULT_OK, data);
+ parent.setResult(Activity.RESULT_OK, intent);
}
finish();
}
diff --git a/app/src/main/java/org/purplei2p/i2pd/I2PdQSTileService.java b/app/src/main/java/org/purplei2p/i2pd/I2PDQuickSettingsTileService.java
similarity index 92%
rename from app/src/main/java/org/purplei2p/i2pd/I2PdQSTileService.java
rename to app/src/main/java/org/purplei2p/i2pd/I2PDQuickSettingsTileService.java
index 3a06165..3646ff3 100644
--- a/app/src/main/java/org/purplei2p/i2pd/I2PdQSTileService.java
+++ b/app/src/main/java/org/purplei2p/i2pd/I2PDQuickSettingsTileService.java
@@ -1,7 +1,6 @@
package org.purplei2p.i2pd;
import android.content.Intent;
-import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
import android.util.Log;
@@ -9,7 +8,7 @@
import android.os.Build;
@TargetApi(Build.VERSION_CODES.N)
-public class I2PdQSTileService extends TileService {
+public class I2PDQuickSettingsTileService extends TileService {
private static final String TAG = "MyQSTileService";
@Override
diff --git a/app/src/main/java/org/purplei2p/i2pd/receivers/BootUpReceiver.java b/app/src/main/java/org/purplei2p/i2pd/receivers/BootUpReceiver.java
deleted file mode 100644
index 2a4078f..0000000
--- a/app/src/main/java/org/purplei2p/i2pd/receivers/BootUpReceiver.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package org.purplei2p.i2pd.receivers;
-
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-//import org.purplei2p.i2pd.ForegroundService;
-//ToDo:* fix^^^ change to service, not on window on start.
-import org.purplei2p.i2pd.I2PDPermsAskerActivity;
-
-import java.io.File;
-
-import static org.purplei2p.i2pd.SettingsActivity.onBootFileName;
-
-public class BootUpReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- /* todo: disable the autostart? */
- File onBoot =
- new File(
- context.getApplicationContext().getCacheDir().getAbsolutePath()
- + onBootFileName);
- if(onBoot.exists()) {
- Intent i = new Intent(context, I2PDPermsAskerActivity.class);
- i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(i);
- }
-
- }
-}
-
diff --git a/app/src/main/res/layout/activity_perms_explanation.xml b/app/src/main/res/layout/activity_permissions_explanation.xml
similarity index 94%
rename from app/src/main/res/layout/activity_perms_explanation.xml
rename to app/src/main/res/layout/activity_permissions_explanation.xml
index 9129a43..428dbd4 100644
--- a/app/src/main/res/layout/activity_perms_explanation.xml
+++ b/app/src/main/res/layout/activity_permissions_explanation.xml
@@ -8,7 +8,7 @@
android:paddingLeft="@dimen/horizontal_page_margin"
android:paddingRight="@dimen/horizontal_page_margin"
android:paddingTop="@dimen/vertical_page_margin"
- tools:context=".I2PDPermsAskerActivity">
+ tools:context=".I2PDPermissionsAskerActivity">
+ tools:context=".I2PDPermissionsAskerActivity">