Browse Source

rework main ui, change theme, remove unused code

Signed-off-by: R4SAS <r4sas@i2pmail.org>
pull/44/head
R4SAS 3 years ago
parent
commit
75d444f374
Signed by: r4sas
GPG Key ID: 66F6C87B98EBCFE2
  1. 107
      app/jni/DaemonAndroid.cpp
  2. 52
      app/jni/DaemonAndroid.h
  3. 2
      app/jni/i2pd
  4. 46
      app/jni/i2pd_android.cpp
  5. 45
      app/jni/org_purplei2p_i2pd_I2PD_JNI.h
  6. 9
      app/src/main/AndroidManifest.xml
  7. 5
      app/src/main/java/org/purplei2p/i2pd/DaemonWrapper.java
  8. 59
      app/src/main/java/org/purplei2p/i2pd/I2PDActivity.java
  9. 30
      app/src/main/java/org/purplei2p/i2pd/I2PD_JNI.java
  10. 21
      app/src/main/java/org/purplei2p/i2pd/SettingsActivity.java
  11. 3
      app/src/main/java/org/purplei2p/i2pd/WebConsoleActivity.java
  12. 142
      app/src/main/res/layout/activity_main.xml
  13. 9
      app/src/main/res/layout/activity_settings.xml
  14. 2
      app/src/main/res/layout/activity_web_console.xml
  15. 1
      app/src/main/res/menu/options_main.xml
  16. 8
      app/src/main/res/values-ru/strings.xml
  17. 8
      app/src/main/res/values-tr/strings.xml
  18. 18
      app/src/main/res/values/strings.xml
  19. 2
      build.gradle
  20. 4
      gradle/wrapper/gradle-wrapper.properties

107
app/jni/DaemonAndroid.cpp

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -22,80 +22,19 @@ namespace i2p
{ {
namespace android namespace android
{ {
/* Worker::Worker (DaemonAndroidImpl& daemon):
m_Daemon (daemon)
{
}
void Worker::startDaemon()
{
Log.d(TAG"Performing daemon start...");
m_Daemon.start();
Log.d(TAG"Daemon started.");
emit resultReady();
}
void Worker::restartDaemon()
{
Log.d(TAG"Performing daemon restart...");
m_Daemon.restart();
Log.d(TAG"Daemon restarted.");
emit resultReady();
}
void Worker::stopDaemon() {
Log.d(TAG"Performing daemon stop...");
m_Daemon.stop();
Log.d(TAG"Daemon stopped.");
emit resultReady();
}
Controller::Controller(DaemonAndroidImpl& daemon):
m_Daemon (daemon)
{
Worker *worker = new Worker (m_Daemon);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::startDaemon, worker, &Worker::startDaemon);
connect(this, &Controller::stopDaemon, worker, &Worker::stopDaemon);
connect(this, &Controller::restartDaemon, worker, &Worker::restartDaemon);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
Controller::~Controller()
{
Log.d(TAG"Closing and waiting for daemon worker thread...");
workerThread.quit();
workerThread.wait();
Log.d(TAG"Waiting for daemon worker thread finished.");
if(m_Daemon.isRunning())
{
Log.d(TAG"Stopping the daemon...");
m_Daemon.stop();
Log.d(TAG"Stopped the daemon.");
}
}
*/
std::string dataDir = ""; std::string dataDir = "";
std::string language = ""; std::string language = "";
DaemonAndroidImpl::DaemonAndroidImpl () DaemonAndroidImpl::DaemonAndroidImpl ()
//:
/*mutex(nullptr), */
//m_IsRunning(false),
//m_RunningChangedCallback(nullptr)
{ {
} }
DaemonAndroidImpl::~DaemonAndroidImpl () DaemonAndroidImpl::~DaemonAndroidImpl ()
{ {
//delete mutex;
} }
bool DaemonAndroidImpl::init(int argc, char* argv[]) bool DaemonAndroidImpl::init(int argc, char* argv[])
{ {
//mutex=new QMutex(QMutex::Recursive);
//setRunningCallback(0);
//m_IsRunning=false;
// make sure assets are ready before proceed // make sure assets are ready before proceed
i2p::fs::DetectDataDir(dataDir, false); i2p::fs::DetectDataDir(dataDir, false);
int numAttempts = 0; int numAttempts = 0;
@ -129,28 +68,7 @@ namespace android
stop(); stop();
start(); start();
} }
/*
void DaemonAndroidImpl::setRunningCallback(runningChangedCallback cb)
{
m_RunningChangedCallback = cb;
}
bool DaemonAndroidImpl::isRunning()
{
return m_IsRunning;
}
void DaemonAndroidImpl::setRunning(bool newValue)
{
bool oldValue = m_IsRunning;
if(oldValue!=newValue)
{
m_IsRunning = newValue;
if(m_RunningChangedCallback)
m_RunningChangedCallback();
}
}
*/
static DaemonAndroidImpl daemon; static DaemonAndroidImpl daemon;
static char* argv[1]={strdup("tmp")}; static char* argv[1]={strdup("tmp")};
/** /**
@ -161,40 +79,19 @@ namespace android
{ {
try try
{ {
//int result;
{ {
//Log.d(TAG"Initialising the daemon..."); //Log.d(TAG"Initialising the daemon...");
bool daemonInitSuccess = daemon.init(1, argv); bool daemonInitSuccess = daemon.init(1, argv);
if(!daemonInitSuccess) if(!daemonInitSuccess)
{ {
//QMessageBox::critical(0, "Error", "Daemon init failed");
return "Daemon init failed"; return "Daemon init failed";
} }
// Set webconsole language from application // Set webconsole language from application
i2p::i18n::SetLanguage(language); i2p::i18n::SetLanguage(language);
//Log.d(TAG"Initialised, creating the main window..."); daemon.start();
//MainWindow w;
//Log.d(TAG"Before main window.show()...");
//w.show ();
{
//i2p::qt::Controller daemonQtController(daemon);
//Log.d(TAG"Starting the daemon...");
//emit daemonQtController.startDaemon();
//daemon.start ();
//Log.d(TAG"Starting GUI event loop...");
//result = app.exec();
//daemon.stop ();
daemon.start();
}
} }
//QMessageBox::information(&w, "Debug", "demon stopped");
//Log.d(TAG"Exiting the application");
//return result;
} }
catch (boost::exception& ex) catch (boost::exception& ex)
{ {

52
app/jni/DaemonAndroid.h

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -22,8 +22,6 @@ namespace android
DaemonAndroidImpl (); DaemonAndroidImpl ();
~DaemonAndroidImpl (); ~DaemonAndroidImpl ();
//typedef void (*runningChangedCallback)();
/** /**
* @return success * @return success
*/ */
@ -31,14 +29,6 @@ namespace android
void start(); void start();
void stop(); void stop();
void restart(); void restart();
//void setRunningCallback(runningChangedCallback cb);
//bool isRunning();
private:
//void setRunning(bool running);
private:
//QMutex* mutex;
//bool m_IsRunning;
//runningChangedCallback m_RunningChangedCallback;
}; };
/** /**
@ -47,7 +37,6 @@ namespace android
*/ */
std::string start(); std::string start();
// stops the daemon
void stop(); void stop();
// set datadir received from jni // set datadir received from jni
@ -56,45 +45,6 @@ namespace android
std::string GetDataDir(void); std::string GetDataDir(void);
// set webconsole language // set webconsole language
void SetLanguage(std::string jlanguage); void SetLanguage(std::string jlanguage);
/*
class Worker : public QObject
{
Q_OBJECT
public:
Worker (DaemonAndroidImpl& daemon);
private:
DaemonAndroidImpl& m_Daemon;
public slots:
void startDaemon();
void restartDaemon();
void stopDaemon();
signals:
void resultReady();
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller(DaemonAndroidImpl& daemon);
~Controller();
private:
DaemonAndroidImpl& m_Daemon;
public slots:
void handleResults(){}
signals:
void startDaemon();
void stopDaemon();
void restartDaemon();
};
*/
} }
} }

2
app/jni/i2pd

@ -1 +1 @@
Subproject commit b7e20b9b86165a0eb2ba5bcf9a580f3824a38462 Subproject commit 064b8042a5bd37bf44b4e712d8af16bca6c2acf7

46
app/jni/i2pd_android.cpp

@ -90,31 +90,12 @@ JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_onNetworkStateChanged
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_setDataDir JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_setDataDir
(JNIEnv *env, jclass clazz, jstring jdataDir) { (JNIEnv *env, jclass clazz, jstring jdataDir) {
/*
// Method 1: convert UTF-16 jstring to std::string (https://stackoverflow.com/a/41820336)
const jclass stringClass = env->GetObjectClass(jdataDir);
const jmethodID getBytes = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B");
const jbyteArray stringJbytes = (jbyteArray) env->CallObjectMethod(jdataDir, getBytes, env->NewStringUTF("UTF-8"));
size_t length = (size_t) env->GetArrayLength(stringJbytes);
jbyte* pBytes = env->GetByteArrayElements(stringJbytes, NULL);
std::string dataDir = std::string((char *)pBytes, length);
env->ReleaseByteArrayElements(stringJbytes, pBytes, JNI_ABORT);
env->DeleteLocalRef(stringJbytes);
env->DeleteLocalRef(stringClass); */
// Method 2: get string chars and make char array.
auto dataDir = env->GetStringUTFChars(jdataDir, NULL); auto dataDir = env->GetStringUTFChars(jdataDir, NULL);
env->ReleaseStringUTFChars(jdataDir, dataDir); env->ReleaseStringUTFChars(jdataDir, dataDir);
// Set DataDir
i2p::android::SetDataDir(dataDir); i2p::android::SetDataDir(dataDir);
} }
JNIEXPORT jint JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_GetTransitTunnelsCount JNIEXPORT jint JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getTransitTunnelsCount
(JNIEnv *env, jclass clazz) { (JNIEnv *env, jclass clazz) {
return i2p::tunnel::tunnels.CountTransitTunnels(); return i2p::tunnel::tunnels.CountTransitTunnels();
} }
@ -133,3 +114,28 @@ JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_setLanguage
env->ReleaseStringUTFChars(jlanguage, language); env->ReleaseStringUTFChars(jlanguage, language);
i2p::android::SetLanguage(language); i2p::android::SetLanguage(language);
} }
JNIEXPORT jboolean JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getHTTPProxyState
(JNIEnv *, jclass) {
return i2p::client::context.GetHttpProxy () ? true : false;
}
JNIEXPORT jboolean JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getSOCKSProxyState
(JNIEnv *, jclass) {
return i2p::client::context.GetSocksProxy() ? true : false;
}
JNIEXPORT jboolean JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getBOBState
(JNIEnv *, jclass) {
return i2p::client::context.GetBOBCommandChannel() ? true : false;
}
JNIEXPORT jboolean JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getSAMState
(JNIEnv *, jclass) {
return i2p::client::context.GetSAMBridge() ? true : false;
}
JNIEXPORT jboolean JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getI2CPState
(JNIEnv *, jclass) {
return i2p::client::context.GetI2CPServer() ? true : false;
}

45
app/jni/org_purplei2p_i2pd_I2PD_JNI.h

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -15,46 +15,57 @@
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
/*
* Class: org_purplei2p_i2pd_I2PD_JNI
* Method: stringFromJNI
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getABICompiledWith JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getABICompiledWith
(JNIEnv *, jclass); (JNIEnv *, jclass);
JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startDaemon JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startDaemon
(JNIEnv *, jclass); (JNIEnv *, jclass);
JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getDataDir
(JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopDaemon JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopDaemon
(JNIEnv *, jclass); (JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopAcceptingTunnels JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startAcceptingTunnels
(JNIEnv *, jclass); (JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startAcceptingTunnels JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopAcceptingTunnels
(JNIEnv *, jclass); (JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_reloadTunnelsConfigs JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_reloadTunnelsConfigs
(JNIEnv *, jclass); (JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_onNetworkStateChanged
(JNIEnv * env, jclass clazz, jboolean isConnected);
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_setDataDir JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_setDataDir
(JNIEnv *env, jclass clazz, jstring jdataDir); (JNIEnv *env, jclass clazz, jstring jdataDir);
JNIEXPORT jint JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_GetTransitTunnelsCount JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_setLanguage
(JNIEnv *env, jclass clazz, jstring jlanguage);
JNIEXPORT jint JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getTransitTunnelsCount
(JNIEnv *, jclass); (JNIEnv *, jclass);
JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getWebConsAddr JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getWebConsAddr
(JNIEnv *, jclass); (JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_setLanguage JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getDataDir
(JNIEnv *env, jclass clazz, jstring jlanguage); (JNIEnv *, jclass);
JNIEXPORT jboolean JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getHTTPProxyState
(JNIEnv *, jclass);
JNIEXPORT jboolean JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getSOCKSProxyState
(JNIEnv *, jclass);
JNIEXPORT jboolean JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getBOBState
(JNIEnv *, jclass);
JNIEXPORT jboolean JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getSAMState
(JNIEnv *, jclass) ;
JNIEXPORT jboolean JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getI2CPState
(JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_onNetworkStateChanged
(JNIEnv * env, jclass clazz, jboolean isConnected);
#ifdef __cplusplus #ifdef __cplusplus
} }

9
app/src/main/AndroidManifest.xml

@ -12,13 +12,12 @@
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application <application
android:allowBackup="true" android:allowBackup="true"
android:icon="@mipmap/logo" android:icon="@mipmap/logo"
android:label="@string/app_name" android:label="@string/app_name"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:theme="@android:style/Theme.Holo.Light.DarkActionBar" android:theme="@android:style/Theme.DeviceDefault"
android:usesCleartextTraffic="true"> android:usesCleartextTraffic="true">
<receiver <receiver
@ -47,8 +46,7 @@
</receiver> </receiver>
<activity <activity
android:name=".I2PDPermsAskerActivity" android:name=".I2PDPermsAskerActivity">
android:label="@string/app_name">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
@ -56,8 +54,7 @@
</activity> </activity>
<activity <activity
android:name=".I2PDActivity" android:name=".I2PDActivity" />
android:label="@string/app_name" />
<service <service
android:name=".ForegroundService" android:name=".ForegroundService"

5
app/src/main/java/org/purplei2p/i2pd/DaemonWrapper.java

@ -85,13 +85,12 @@ public class DaemonWrapper {
} }
public int getTransitTunnelsCount() { public int getTransitTunnelsCount() {
return I2PD_JNI.GetTransitTunnelsCount(); return I2PD_JNI.getTransitTunnelsCount();
} }
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),
startedOkay(R.string.startedOkay), startedOkay(R.string.startedOkay),
startFailed(R.string.startFailed), startFailed(R.string.startFailed),
gracefulShutdownInProgress(R.string.gracefulShutdownInProgress), gracefulShutdownInProgress(R.string.gracefulShutdownInProgress),
@ -168,7 +167,6 @@ public class DaemonWrapper {
} catch (Throwable tr) { } catch (Throwable tr) {
Log.e(TAG, "", tr); Log.e(TAG, "", tr);
} }
setState(State.stopped); setState(State.stopped);
} }
} }
@ -178,7 +176,6 @@ public class DaemonWrapper {
try { try {
processAssets(); processAssets();
I2PD_JNI.loadLibraries(); I2PD_JNI.loadLibraries();
setState(State.jniLibraryLoaded);
//registerNetworkCallback(); //registerNetworkCallback();
} catch (Throwable tr) { } catch (Throwable tr) {
lastThrowable = tr; lastThrowable = tr;

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

@ -27,8 +27,7 @@ import android.provider.Settings;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.widget.CheckBox;
import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
@ -37,19 +36,20 @@ import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import static android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS; import static android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS;
import static org.purplei2p.i2pd.DaemonWrapper.State.startedOkay;
import static org.purplei2p.i2pd.DaemonWrapper.State.starting;
import static org.purplei2p.i2pd.DaemonWrapper.State.stopped;
public class I2PDActivity extends Activity { public class I2PDActivity extends Activity {
private static final String TAG = "i2pdActvt"; private static final String TAG = "i2pdActvt";
private static final int MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 1; private static final int MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000; public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000;
public static final String PACKAGE_URI_SCHEME = "package:"; public static final String PACKAGE_URI_SCHEME = "package:";
private Button enableButton;
private Button disableButton;
private TextView textView; private TextView textView;
private CheckBox HTTPProxyState;
private CheckBox SOCKSProxyState;
private CheckBox BOBState;
private CheckBox SAMState;
private CheckBox I2CPState;
private static volatile DaemonWrapper daemon; private static volatile DaemonWrapper daemon;
@ -65,15 +65,23 @@ public class I2PDActivity extends Activity {
try { try {
if (textView == null) if (textView == null)
return; return;
Throwable tr = daemon.getLastThrowable(); Throwable tr = daemon.getLastThrowable();
if (tr != null) { if (tr != null) {
textView.setText(throwableToString(tr)); textView.setText(throwableToString(tr));
return; return;
} }
DaemonWrapper.State state = daemon.getState(); DaemonWrapper.State state = daemon.getState();
if(state == startedOkay){
disableButton.setVisibility(View.VISIBLE); if (daemon.isStartedOkay()) {
HTTPProxyState.setChecked(I2PD_JNI.getHTTPProxyState());
SOCKSProxyState.setChecked(I2PD_JNI.getSOCKSProxyState());
BOBState.setChecked(I2PD_JNI.getBOBState());
SAMState.setChecked(I2PD_JNI.getSAMState());
I2CPState.setChecked(I2PD_JNI.getI2CPState());
} }
String startResultStr = DaemonWrapper.State.startFailed.equals(state) ? String.format(": %s", daemon.getDaemonStartResult()) : ""; 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 graceStr = DaemonWrapper.State.gracefulShutdownInProgress.equals(state) ? String.format(": %s %s", formatGraceTimeRemaining(), getText(R.string.remaining)) : "";
textView.setText(String.format("%s%s%s", getText(state.getStatusStringResourceId()), startResultStr, graceStr)); textView.setText(String.format("%s%s%s", getText(state.getStatusStringResourceId()), startResultStr, graceStr));
@ -102,9 +110,14 @@ public class I2PDActivity extends Activity {
Log.i(TAG, "onCreate"); Log.i(TAG, "onCreate");
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
enableButton = findViewById(R.id.enableButton);
disableButton = findViewById(R.id.disableButton);
textView = (TextView) findViewById(R.id.appStatusText); 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) { if (daemon == null) {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
daemon = new DaemonWrapper(getAssets(), connectivityManager); daemon = new DaemonWrapper(getAssets(), connectivityManager);
@ -113,25 +126,6 @@ public class I2PDActivity extends Activity {
daemon.addStateChangeListener(daemonStateUpdatedListener); daemon.addStateChangeListener(daemonStateUpdatedListener);
daemonStateUpdatedListener.daemonStateUpdate(DaemonWrapper.State.uninitialized, daemon.getState()); daemonStateUpdatedListener.daemonStateUpdate(DaemonWrapper.State.uninitialized, daemon.getState());
enableButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if( daemon.getState() != startedOkay && daemon.getState() != starting ){
daemon.stopDaemon();
daemon.startDaemon();
disableButton.setVisibility(View.VISIBLE);
}
}
});
disableButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if( daemon.getState() != stopped ){
daemon.stopDaemon();
disableButton.setVisibility(View.INVISIBLE);
}
}
});
// request permissions // request permissions
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
@ -299,7 +293,7 @@ public class I2PDActivity extends Activity {
return true; return true;
case R.id.action_start_webview: case R.id.action_start_webview:
if( daemon.getState() == startedOkay) if(daemon.isStartedOkay())
startActivity(new Intent(getApplicationContext(), WebConsoleActivity.class)); startActivity(new Intent(getApplicationContext(), WebConsoleActivity.class));
else else
Toast.makeText(this,"I2Pd not was started!", Toast.LENGTH_SHORT).show(); Toast.makeText(this,"I2Pd not was started!", Toast.LENGTH_SHORT).show();
@ -334,11 +328,12 @@ public class I2PDActivity extends Activity {
new Thread(() -> { new Thread(() -> {
Log.d(TAG, "stopping"); Log.d(TAG, "stopping");
try { try {
textView.setText(getText(R.string.stopping));
daemon.stopDaemon(); daemon.stopDaemon();
} catch (Throwable tr) { } catch (Throwable tr) {
Log.e(TAG, "", tr); Log.e(TAG, "", tr);
} }
quit(); //TODO make menu items for starting i2pd. On my Android, I need to reboot the OS to restart i2pd. quit();
}, "stop").start(); }, "stop").start();
} }

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

@ -3,35 +3,33 @@ package org.purplei2p.i2pd;
public class I2PD_JNI { public class I2PD_JNI {
public static native String getABICompiledWith(); public static native String getABICompiledWith();
public static void loadLibraries() {
System.loadLibrary("i2pd");
}
/** /**
* 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
public static native void stopDaemon(); public static native void stopDaemon();
public static native void stopAcceptingTunnels();
public static native void startAcceptingTunnels(); public static native void startAcceptingTunnels();
public static native void stopAcceptingTunnels();
public static native void reloadTunnelsConfigs(); public static native void reloadTunnelsConfigs();
public static native void onNetworkStateChanged(boolean isConnected);
public static native void setDataDir(String jdataDir); public static native void setDataDir(String jdataDir);
public static native void setLanguage(String jlanguage);
public static native int GetTransitTunnelsCount(); public static native int getTransitTunnelsCount();
public static native String getWebConsAddr(); public static native String getWebConsAddr();
public static native String getDataDir();
public static native void setLanguage(String jlanguage); public static native boolean getHTTPProxyState();
public static native boolean getSOCKSProxyState();
public static void loadLibraries() { public static native boolean getBOBState();
//System.loadLibrary("c++_shared"); public static native boolean getSAMState();
System.loadLibrary("i2pd"); public static native boolean getI2CPState();
}
public static native String getDataDir(); public static native void onNetworkStateChanged(boolean isConnected);
} }

21
app/src/main/java/org/purplei2p/i2pd/SettingsActivity.java

@ -10,14 +10,16 @@ import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings; import android.provider.Settings;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.MenuItem;
import android.widget.CompoundButton; import android.widget.CompoundButton;
import android.widget.Switch; import android.widget.Switch;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
import java.util.Objects;
import org.purplei2p.i2pd.iniedotr.IniEditor; import org.purplei2p.i2pd.iniedotr.IniEditor;
public class SettingsActivity extends Activity { public class SettingsActivity extends Activity {
protected IniEditor iniedit = new IniEditor(); protected IniEditor iniedit = new IniEditor();
private String dataDir = DaemonWrapper.getDataDir();//for inieditor private String dataDir = DaemonWrapper.getDataDir();//for inieditor
@ -62,6 +64,7 @@ public class SettingsActivity extends Activity {
} }
} }
//@Override //@Override
private void requestPermission() { private void requestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
@ -76,11 +79,15 @@ public class SettingsActivity extends Activity {
} }
} }
} }
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate"); Log.i(TAG, "onCreate");
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
cacheDir = getApplicationContext().getCacheDir();
setContentView(R.layout.activity_settings); setContentView(R.layout.activity_settings);
Objects.requireNonNull(getActionBar()).setDisplayHomeAsUpEnabled(true);
cacheDir = getApplicationContext().getCacheDir();
Switch autostart_switch = findViewById(R.id.autostart_enable); Switch autostart_switch = findViewById(R.id.autostart_enable);
File onBoot = new File(cacheDir.getAbsolutePath() + onBootFileName); File onBoot = new File(cacheDir.getAbsolutePath() + onBootFileName);
autostart_switch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { autostart_switch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@ -107,4 +114,14 @@ public class SettingsActivity extends Activity {
if(onBoot.exists()) if(onBoot.exists())
autostart_switch.setChecked(true); autostart_switch.setChecked(true);
} }
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
finish();
return true;
}
return false;
}
} }

3
app/src/main/java/org/purplei2p/i2pd/WebConsoleActivity.java

@ -8,7 +8,6 @@ import android.webkit.WebSettings;
import android.webkit.WebView; import android.webkit.WebView;
import android.webkit.WebViewClient; import android.webkit.WebViewClient;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import java.util.Objects; import java.util.Objects;
public class WebConsoleActivity extends Activity { public class WebConsoleActivity extends Activity {
@ -22,7 +21,7 @@ public class WebConsoleActivity extends Activity {
Objects.requireNonNull(getActionBar()).setDisplayHomeAsUpEnabled(true); Objects.requireNonNull(getActionBar()).setDisplayHomeAsUpEnabled(true);
webView = (WebView) findViewById(R.id.webview1); webView = (WebView) findViewById(R.id.webconsole);
webView.setWebViewClient(new WebViewClient()); webView.setWebViewClient(new WebViewClient());
final WebSettings webSettings = webView.getSettings(); final WebSettings webSettings = webView.getSettings();

142
app/src/main/res/layout/activity_main.xml

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main" android:id="@+id/activity_main"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -10,14 +11,17 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical"> android:orientation="vertical">
<ImageView <ImageView
android:id="@+id/mainLogo" android:id="@+id/mainLogo"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="140dp" android:layout_height="140dp"
android:src="@drawable/ic_logo" /> android:contentDescription="@string/app_name"
android:src="@drawable/ic_logo"
tools:ignore="ImageContrastCheck" />
<TextView <TextView
style="@android:style/Widget.Holo.Light.TextView.SpinnerItem" style="@android:style/Widget.Holo.Light.TextView.SpinnerItem"
@ -43,50 +47,108 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/margin_medium" /> android:layout_height="@dimen/margin_medium" />
<LinearLayout <TextView
android:id="@+id/appStatusText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textAppearance="@android:style/TextAppearance.Material.Large"
android:textColor="#DFDFDF" />
<Space
android:layout_width="match_parent"
android:layout_height="@dimen/margin_medium" />
<TextView
android:id="@+id/textView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:gravity="center_horizontal"
android:text="@string/services"
android:textAppearance="@android:style/TextAppearance.Material.Medium"
android:textColor="#DFDFDF" />
<RelativeLayout <TableLayout
android:layout_height="wrap_content" android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal">
<TableRow
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_gravity="center"> android:layout_height="match_parent"
android:gravity="center_horizontal">
<Button
android:id="@+id/enableButton" <CheckBox
style="@android:style/Widget.Button.Inset" android:id="@+id/service_httpproxy_box"
android:layout_width="@dimen/margin_huge" android:layout_width="wrap_content"
android:layout_height="@dimen/margin_huge" android:layout_height="wrap_content"
android:layout_centerInParent="true" android:clickable="false"
android:background="@android:drawable/presence_offline" android:text="@string/services_http_proxy"
android:longClickable="true" /> android:textAppearance="@android:style/TextAppearance.Material.Small"
android:textColor="#DFDFDF" />
<Button
android:id="@+id/disableButton" </TableRow>
style="@android:style/Widget.Button.Inset"
android:layout_width="@dimen/margin_huge" <TableRow
android:layout_height="@dimen/margin_huge" android:layout_width="match_parent"
android:layout_centerInParent="true" android:layout_height="match_parent"
android:background="@android:drawable/presence_online" android:gravity="center_horizontal">
android:longClickable="true"
android:visibility="invisible" /> <CheckBox
android:id="@+id/service_socksproxy_box"
</RelativeLayout> android:layout_width="wrap_content"
android:layout_height="wrap_content"
<Space android:clickable="false"
android:layout_width="wrap_content" android:text="@string/services_socks_proxy"
android:layout_height="@dimen/margin_medium" /> android:textAppearance="@android:style/TextAppearance.Material.Small"
android:textColor="#DFDFDF" />
<TextView </TableRow>
android:id="@+id/appStatusText"
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal">
<CheckBox
android:id="@+id/service_bob_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:text="@string/services_bob"
android:textAppearance="@android:style/TextAppearance.Material.Small"
android:textColor="#DFDFDF" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal">
<CheckBox
android:id="@+id/service_sam_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:text="@string/services_sam"
android:textAppearance="@android:style/TextAppearance.Material.Small"
android:textColor="#DFDFDF" />
</TableRow>
<TableRow
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:gravity="center_horizontal" android:gravity="center_horizontal">
android:textColor="#DFDFDF"
android:textSize="18sp" />
</LinearLayout> <CheckBox
android:id="@+id/service_i2cp_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:text="@string/services_i2cp"
android:textAppearance="@android:style/TextAppearance.Material.Small"
android:textColor="#DFDFDF" />
</TableRow>
</TableLayout>
</LinearLayout> </LinearLayout>

9
app/src/main/res/layout/activity_settings.xml

@ -3,8 +3,9 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:padding="@dimen/margin_medium" android:background="@android:color/widget_edittext_dark"
android:orientation="vertical" android:orientation="vertical"
android:padding="@dimen/margin_medium"
tools:context=".SettingsActivity"> tools:context=".SettingsActivity">
<TextView <TextView
@ -21,15 +22,15 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="left" android:gravity="left"
android:text="@string/autostart_enabled"
android:textOff="@string/disabled" android:textOff="@string/disabled"
android:textOn="@string/enabled" android:textOn="@string/enabled" />
android:text="@string/autostart_enabled" />
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
android:background="?android:attr/listDivider" /> android:background="?android:attr/listDivider" />
<!-- <!--
<TextView <TextView
android:id="@+id/settings_section2" android:id="@+id/settings_section2"
android:layout_width="match_parent" android:layout_width="match_parent"

2
app/src/main/res/layout/activity_web_console.xml

@ -11,7 +11,7 @@
android:id="@+id/swipe"> android:id="@+id/swipe">
<WebView <WebView
android:id="@+id/webview1" android:id="@+id/webconsole"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" /> android:layout_height="fill_parent" />

1
app/src/main/res/menu/options_main.xml

@ -25,6 +25,7 @@
android:title="@string/action_graceful_stop" /> android:title="@string/action_graceful_stop" />
<item <item
android:id="@+id/action_settings" android:id="@+id/action_settings"
android:orderInCategory="95"
android:title="@string/action_settings" /> android:title="@string/action_settings" />
<item <item
android:id="@+id/action_stop" android:id="@+id/action_stop"

8
app/src/main/res/values-ru/strings.xml

@ -17,8 +17,8 @@
<string name="gracefulShutdownInProgress">Корректная остановка запущена</string> <string name="gracefulShutdownInProgress">Корректная остановка запущена</string>
<string name="already_stopped">Уже остановлено</string> <string name="already_stopped">Уже остановлено</string>
<string name="uninitialized">Приложение инициализируется</string> <string name="uninitialized">Приложение инициализируется...</string>
<string name="starting">Приложение запускается</string> <string name="starting">Приложение запускается...</string>
<string name="jniLibraryLoaded">Загружены JNI библиотеки</string> <string name="jniLibraryLoaded">Загружены JNI библиотеки</string>
<string name="startedOkay">Приложение запущено</string> <string name="startedOkay">Приложение запущено</string>
<string name="startFailed">Запуск не удался</string> <string name="startFailed">Запуск не удался</string>
@ -53,4 +53,8 @@
<string name="del_tunnel_button">Удалить</string> <string name="del_tunnel_button">Удалить</string>
<string name="enabled">Вкл</string> <string name="enabled">Вкл</string>
<string name="disabled">Выкл</string> <string name="disabled">Выкл</string>
<string name="services">Внутренние сервисы</string>
<string name="services_http_proxy">HTTP Прокси</string>
<string name="services_socks_proxy">SOCKS5 Прокси</string>
<string name="stopping">Остановка приложения</string>
</resources> </resources>

8
app/src/main/res/values-tr/strings.xml

@ -6,8 +6,8 @@
<string name="action_start">Başlat</string> <string name="action_start">Başlat</string>
<string name="action_stop">Durdur</string> <string name="action_stop">Durdur</string>
<string name="action_exit">Çık</string> <string name="action_exit">Çık</string>
<string name="action_graceful_stop">Düzgün Durdur</string> <string name="action_graceful_stop">Düzgün durdur</string>
<string name="action_cancel_graceful_stop">Düzgün Durdurmayı İptal Et</string> <string name="action_cancel_graceful_stop">Düzgün durdurmayı İptal Et</string>
<string name="action_reload_tunnels_config">Tünelleri yeniden yükle</string> <string name="action_reload_tunnels_config">Tünelleri yeniden yükle</string>
<string name="action_start_webview">Web Konsolunu Aç</string> <string name="action_start_webview">Web Konsolunu Aç</string>
<string name="action_settings">Ayarlar</string> <string name="action_settings">Ayarlar</string>
@ -17,8 +17,8 @@
<string name="gracefulShutdownInProgress">Düzgün kapatma devam ediyor</string> <string name="gracefulShutdownInProgress">Düzgün kapatma devam ediyor</string>
<string name="already_stopped">Zaten durduruldu</string> <string name="already_stopped">Zaten durduruldu</string>
<string name="uninitialized">Uygulama ilk ayarları yapılıyor</string> <string name="uninitialized">Uygulama ilk ayarları yapılıyor...</string>
<string name="starting">Uygulama başlatılıyor</string> <string name="starting">Uygulama başlatılıyor...</string>
<string name="jniLibraryLoaded">JNI kütüphaneleri yüklendi</string> <string name="jniLibraryLoaded">JNI kütüphaneleri yüklendi</string>
<string name="startedOkay">Uygulama başlatıldı</string> <string name="startedOkay">Uygulama başlatıldı</string>
<string name="startFailed">Başlatılamadı</string> <string name="startFailed">Başlatılamadı</string>

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

@ -7,8 +7,8 @@
<string name="action_start">Start</string> <string name="action_start">Start</string>
<string name="action_stop">Stop</string> <string name="action_stop">Stop</string>
<string name="action_exit">Exit</string> <string name="action_exit">Exit</string>
<string name="action_graceful_stop">Graceful Stop</string> <string name="action_graceful_stop">Graceful stop</string>
<string name="action_cancel_graceful_stop">Cancel Graceful Stop</string> <string name="action_cancel_graceful_stop">Cancel Graceful stop</string>
<string name="action_reload_tunnels_config">Reload tunnels</string> <string name="action_reload_tunnels_config">Reload tunnels</string>
<string name="action_start_webview">Open Web Console</string> <string name="action_start_webview">Open Web Console</string>
<string name="action_settings">Settings</string> <string name="action_settings">Settings</string>
@ -18,15 +18,23 @@
<string name="gracefulShutdownInProgress">Graceful shutdown in progress</string> <string name="gracefulShutdownInProgress">Graceful shutdown in progress</string>
<string name="already_stopped">Already stopped</string> <string name="already_stopped">Already stopped</string>
<string name="uninitialized">Application initializing</string> <string name="uninitialized">Application initializing...</string>
<string name="starting">Application starting</string> <string name="starting">Application starting...</string>
<string name="jniLibraryLoaded">Loaded JNI libraries</string> <string name="jniLibraryLoaded">Loaded JNI libraries</string>
<string name="startedOkay">Application Started</string> <string name="startedOkay">Application started</string>
<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="stopping">Application stopping</string>
<string name="remaining">remaining</string> <string name="remaining">remaining</string>
<string name="ok" translatable="false">OK</string> <string name="ok" translatable="false">OK</string>
<string name="services">Internal services</string>
<string name="services_http_proxy">HTTP Proxy</string>
<string name="services_socks_proxy">SOCKS5 Proxy</string>
<string name="services_bob" translatable="false">BOB</string>
<string name="services_sam" translatable="false">SAM</string>
<string name="services_i2cp" translatable="false">I2CP</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>
<string name="permRequired">SD card write access is required to write the keys and other files to the I2PD folder on SD card.</string> <string name="permRequired">SD card write access is required to write the keys and other files to the I2PD folder on SD card.</string>

2
build.gradle

@ -4,7 +4,7 @@ buildscript {
google() google()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:4.1.0' classpath 'com.android.tools.build:gradle:7.1.2'
} }
} }

4
gradle/wrapper/gradle-wrapper.properties vendored

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionSha256Sum=8b356fd8702d5ffa2e066ed0be45a023a779bba4dd1a68fd11bc2a6bdc981e8f distributionSha256Sum=f581709a9c35e9cb92e16f585d2c4bc99b2b1a5f85d2badbd3dc6bff59e1e6dd
distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

Loading…
Cancel
Save