Browse Source

[android] update strings, menus, add reloading tunnels item

Signed-off-by: R4SAS <r4sas@i2pmail.org>
pull/1515/head
R4SAS 5 years ago
parent
commit
95fa835191
Signed by: r4sas
GPG Key ID: 66F6C87B98EBCFE2
  1. 2
      android/.gitignore
  2. 5
      android/AndroidManifest.xml
  3. 6
      android/jni/i2pd_android.cpp
  4. 3
      android/jni/org_purplei2p_i2pd_I2PD_JNI.h
  5. 7
      android/res/layout/activity_perms_asker.xml
  6. 6
      android/res/layout/activity_perms_explanation.xml
  7. 11
      android/res/layout/webview.xml
  8. 30
      android/res/menu/options_main.xml
  9. 18
      android/res/values-ru/strings.xml
  10. 20
      android/res/values/strings.xml
  11. 76
      android/src/org/purplei2p/i2pd/DaemonSingleton.java
  12. 380
      android/src/org/purplei2p/i2pd/I2PDActivity.java
  13. 2
      android/src/org/purplei2p/i2pd/I2PD_JNI.java

2
android/.gitignore vendored

@ -4,6 +4,7 @@ bin
libs libs
log* log*
obj obj
.cxx
.gradle .gradle
.idea .idea
.externalNativeBuild .externalNativeBuild
@ -14,3 +15,4 @@ android.iml
build build
*.iml *.iml
*.local *.local
*.jks

5
android/AndroidManifest.xml

@ -17,8 +17,9 @@
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@android:style/Theme.Holo.Light.DarkActionBar" android:theme="@android:style/Theme.Holo.Light.DarkActionBar"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
> >
<receiver android:name=".NetworkStateChangeReceiver"> <receiver android:name=".NetworkStateChangeReceiver">
<intent-filter> <intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
@ -30,10 +31,10 @@
android:label="@string/app_name"> 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" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".I2PDActivity" android:name=".I2PDActivity"
android:label="@string/app_name" /> android:label="@string/app_name" />

6
android/jni/i2pd_android.cpp

@ -2,6 +2,7 @@
#include "org_purplei2p_i2pd_I2PD_JNI.h" #include "org_purplei2p_i2pd_I2PD_JNI.h"
#include "DaemonAndroid.h" #include "DaemonAndroid.h"
#include "RouterContext.h" #include "RouterContext.h"
#include "ClientContext.h"
#include "Transports.h" #include "Transports.h"
JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getABICompiledWith JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getABICompiledWith
@ -61,6 +62,11 @@ JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startAcceptingTunnels
i2p::context.SetAcceptsTunnels (true); i2p::context.SetAcceptsTunnels (true);
} }
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_reloadTunnelsConfigs
(JNIEnv *env, jclass clazz) {
i2p::client::context.ReloadConfig();
}
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_onNetworkStateChanged JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_onNetworkStateChanged
(JNIEnv *env, jclass clazz, jboolean isConnected) { (JNIEnv *env, jclass clazz, jboolean isConnected) {
bool isConnectedBool = (bool) isConnected; bool isConnectedBool = (bool) isConnected;

3
android/jni/org_purplei2p_i2pd_I2PD_JNI.h

@ -27,6 +27,9 @@ JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopAcceptingTunnels
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startAcceptingTunnels JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startAcceptingTunnels
(JNIEnv *, jclass); (JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_reloadTunnelsConfigs
(JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_onNetworkStateChanged JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_onNetworkStateChanged
(JNIEnv * env, jclass clazz, jboolean isConnected); (JNIEnv * env, jclass clazz, jboolean isConnected);

7
android/res/layout/activity_perms_asker.xml

@ -15,13 +15,12 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/horizontal_page_margin" android:layout_marginBottom="@dimen/horizontal_page_margin"
android:visibility="gone" android:visibility="gone" />
/>
<Button <Button
android:id="@+id/button_request_write_ext_storage_perms" android:id="@+id/button_request_write_ext_storage_perms"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Retry requesting the SD card write permissions" android:text="@string/retryPermRequest"
android:visibility="gone"/> android:visibility="gone" />
</LinearLayout> </LinearLayout>

6
android/res/layout/activity_perms_explanation.xml

@ -15,13 +15,11 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/horizontal_page_margin" android:layout_marginBottom="@dimen/horizontal_page_margin"
android:text="SD card write access is required to write the keys and other files to the I2PD folder on SD card." android:text="@string/permRequired" />
/>
<Button <Button
android:id="@+id/button_ok" android:id="@+id/button_ok"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="OK" android:text="@string/ok" />
/>
</LinearLayout> </LinearLayout>

11
android/res/layout/webview.xml

@ -5,9 +5,8 @@
android:layout_height="fill_parent" android:layout_height="fill_parent"
tools:context=".I2PDActivity"> tools:context=".I2PDActivity">
<WebView <WebView
android:id="@+id/webview1" android:id="@+id/webview1"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent"/> android:layout_height="fill_parent" />
</LinearLayout>
</LinearLayout>

30
android/res/menu/options_main.xml

@ -3,23 +3,29 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:context=".I2PDActivity"> tools:context=".I2PDActivity">
<group android:id="@+id/group_i2pd_control" >
<group android:id="@+id/group_various">
<item <item
android:id="@+id/action_stop" android:id="@+id/action_battery_otimizations"
android:orderInCategory="99" android:title="@string/menu_item_battery_optimizations_str" />
android:title="@string/action_stop" /> </group>
<group android:id="@+id/group_i2pd_control">
<item
android:id="@+id/action_start_webview"
android:orderInCategory="96"
android:title="@string/action_start_webview" />
<item
android:id="@+id/action_reload_tunnels_config"
android:orderInCategory="97"
android:title="@string/action_reload_tunnels_config" />
<item <item
android:id="@+id/action_graceful_stop" android:id="@+id/action_graceful_stop"
android:orderInCategory="98" android:orderInCategory="98"
android:title="@string/action_graceful_stop" /> android:title="@string/action_graceful_stop" />
<item <item
android:id="@+id/action_start_webview" android:id="@+id/action_stop"
android:orderInCategory="97" android:orderInCategory="99"
android:title="@string/action_start_webview" /> android:title="@string/action_stop" />
</group>
<group android:id="@+id/group_various" >
<item
android:id="@+id/action_battery_otimizations"
android:title="@string/menu_item_battery_optimizations_str" />
</group> </group>
</menu> </menu>

18
android/res/values-ru/strings.xml

@ -1,28 +1,40 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">i2pd</string> <string name="app_name">i2pd</string>
<string name="action_stop">Остановить</string> <string name="action_stop">Остановить</string>
<string name="action_graceful_stop">Корректная остановка</string> <string name="action_graceful_stop">Корректная остановка</string>
<string name="action_cancel_graceful_stop">Отменить корректную остановку</string> <string name="action_cancel_graceful_stop">Отменить корректную остановку</string>
<string name="action_reload_tunnels_config">Перезагрузить туннели</string>
<string name="action_start_webview">Открыть Веб Консоль</string>
<string name="graceful_stop_is_already_in_progress">Корректная остановка уже запущена</string> <string name="graceful_stop_is_already_in_progress">Корректная остановка уже запущена</string>
<string name="graceful_stop_is_in_progress">Корректная остановка запущена</string> <string name="graceful_stop_is_in_progress">Корректная остановка запущена</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>
<string name="gracefulShutdownInProgress">Корректная остановка запущена</string>
<string name="stopped">Приложение было остановлено</string> <string name="stopped">Приложение было остановлено</string>
<string name="remaining">осталось</string> <string name="remaining">осталось</string>
<string name="title_activity_i2_pdperms_asker_prompt">Запрос</string> <string name="title_activity_i2_pdperms_asker_prompt">Запрос</string>
<string name="permDenied">Права для записи на SD карту отклонены, вам необходимо предоставить их для продолжения</string> <string name="permDenied">Права для записи на SD карту отклонены, вам необходимо предоставить их для продолжения</string>
<string name="permRequired">Права на запись на SD карту необходимы для записи ключей и других файлов в папку I2PD на внутренней памяти.</string>
<string name="retryPermRequest">Повторить запрос прав на запись на SD карту</string>
<string name="menu_item_battery_optimizations_str">Оптимизации аккумулятора</string> <string name="menu_item_battery_optimizations_str">Оптимизации аккумулятора</string>
<string name="battery_optimizations_enabled">Оптимизации аккумулятора включены</string> <string name="battery_optimizations_enabled">Оптимизации аккумулятора включены</string>
<string name="device_does_not_support_disabling_battery_optimizations">Ваша версия Андроид не поддерживает отключение оптимизаций аккумулятора</string>
<string name="battery_optimizations_enabled_explained">Ваша операционная система осуществляет оптимизации расхода аккумулятора, которые могут приводить к выгрузке I2PD из памяти и прекращению его работы с целью сэкономить заряд аккумулятора.\nРекомендуется отключить эти оптимизации.</string> <string name="battery_optimizations_enabled_explained">Ваша операционная система осуществляет оптимизации расхода аккумулятора, которые могут приводить к выгрузке I2PD из памяти и прекращению его работы с целью сэкономить заряд аккумулятора.\nРекомендуется отключить эти оптимизации.</string>
<string name="battery_optimizations_enabled_dialog" >Ваша операционная система осуществляет оптимизации расхода аккумулятора, которые могут приводить к выгрузке I2PD из памяти и прекращению его работы с целью сэкономить заряд аккумулятора.\n\nВам сейчас будет предложено разрешить отключение этих оптимизаций.</string> <string name="battery_optimizations_enabled_dialog">Ваша операционная система осуществляет оптимизации расхода аккумулятора, которые могут приводить к выгрузке I2PD из памяти и прекращению его работы с целью сэкономить заряд аккумулятора.\n\nВам сейчас будет предложено разрешить отключение этих оптимизаций.</string>
<string name="continue_str">Продолжить</string> <string name="continue_str">Продолжить</string>
<string name="device_does_not_support_disabling_battery_optimizations">Ваша версия Андроид не поддерживает отключение оптимизаций аккумулятора</string>
<string name="os_version_does_not_support_battery_optimizations_show_os_dialog_api">Ваша версия Андроид не поддерживает показ диалога об оптимизациях аккумулятора для приложений.</string> <string name="os_version_does_not_support_battery_optimizations_show_os_dialog_api">Ваша версия Андроид не поддерживает показ диалога об оптимизациях аккумулятора для приложений.</string>
<string name="shutdown_canceled">Плановая остановка отменена</string> <string name="shutdown_canceled">Плановая остановка отменена</string>
<string name="tunnels_reloading">Перезагрузка конфигурации туннелей...</string>
</resources> </resources>

20
android/res/values/strings.xml

@ -1,29 +1,41 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_name">i2pd</string> <string name="app_name">i2pd</string>
<string name="action_stop">Stop</string> <string name="action_stop">Stop</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_start_webview">Open Web Console</string>
<string name="graceful_stop_is_already_in_progress">Graceful stop is already in progress</string> <string name="graceful_stop_is_already_in_progress">Graceful stop is already in progress</string>
<string name="graceful_stop_is_in_progress">Graceful stop is in progress</string> <string name="graceful_stop_is_in_progress">Graceful stop is 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="gracefulShutdownInProgress">Graceful shutdown in progress</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="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="retryPermRequest">Retry requesting the SD card write permissions</string>
<string name="menu_item_battery_optimizations_str">Battery Optimizations</string>
<string name="battery_optimizations_enabled">Battery optimizations enabled</string> <string name="battery_optimizations_enabled">Battery optimizations enabled</string>
<string name="battery_optimizations_enabled_explained">Your Android is doing some heavy battery optimizations on I2PD that might lead to daemon closing with no other reason.\nIt is recommended to allow disabling those battery optimizations.</string> <string name="battery_optimizations_enabled_explained">Your Android is doing some heavy battery optimizations on I2PD that might lead to daemon closing with no other reason.\nIt is recommended to allow disabling those battery optimizations.</string>
<string name="battery_optimizations_enabled_dialog" >Your Android is doing some heavy battery optimizations on I2PD that might lead to daemon closing with no other reason.\n\nYou will now be asked to allow to disable those.</string> <string name="battery_optimizations_enabled_dialog">Your Android is doing some heavy battery optimizations on I2PD that might lead to daemon closing with no other reason.\n\nYou will now be asked to allow to disable those.</string>
<string name="continue_str">Continue</string> <string name="continue_str">Continue</string>
<string name="device_does_not_support_disabling_battery_optimizations">Your Android version does not support opting out of battery optimizations</string> <string name="device_does_not_support_disabling_battery_optimizations">Your Android version does not support opting out of battery optimizations</string>
<string name="menu_item_battery_optimizations_str">Battery Optimizations</string>
<string name="os_version_does_not_support_battery_optimizations_show_os_dialog_api">Your Android OS version does not support showing the dialog for battery optimizations for applications.</string> <string name="os_version_does_not_support_battery_optimizations_show_os_dialog_api">Your Android OS version does not support showing the dialog for battery optimizations for applications.</string>
<string name="shutdown_canceled">Planned shutdown canceled</string> <string name="shutdown_canceled">Planned shutdown canceled</string>
<string name="action_start_webview">Start webview</string>
<string name="tunnels_reloading">Reloading tunnels config...</string>
</resources> </resources>

76
android/src/org/purplei2p/i2pd/DaemonSingleton.java

@ -10,36 +10,61 @@ 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 { void daemonStateUpdate(); }
public interface StateUpdateListener {
void daemonStateUpdate();
}
private final Set<StateUpdateListener> stateUpdateListeners = new HashSet<>(); private final Set<StateUpdateListener> stateUpdateListeners = new HashSet<>();
public static DaemonSingleton getInstance() { return instance; } public static DaemonSingleton getInstance() {
return instance;
}
public synchronized void addStateChangeListener(StateUpdateListener listener) {
stateUpdateListeners.add(listener);
}
public synchronized void addStateChangeListener(StateUpdateListener 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)throw new NullPointerException(); if (newState == null)
throw new NullPointerException();
State oldState = state; State oldState = state;
if(oldState==null)throw new NullPointerException();
if(oldState.equals(newState))return; if (oldState == null)
state=newState; throw new NullPointerException();
if (oldState.equals(newState))
return;
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() {
if (isStartedOkay()) {
I2PD_JNI.reloadTunnelsConfigs();
}
}
private volatile boolean startedOkay; private volatile boolean startedOkay;
public enum State { public enum State {
@ -64,11 +89,13 @@ public class DaemonSingleton {
private volatile State state = State.uninitialized; private volatile State state = State.uninitialized;
public State getState() { return state; } public State getState() {
return state;
}
{ {
setState(State.starting); setState(State.starting);
new Thread(new Runnable(){ new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -76,7 +103,7 @@ public class DaemonSingleton {
I2PD_JNI.loadLibraries(); I2PD_JNI.loadLibraries();
setState(State.jniLibraryLoaded); setState(State.jniLibraryLoaded);
} catch (Throwable tr) { } catch (Throwable tr) {
lastThrowable=tr; lastThrowable = tr;
setState(State.startFailed); setState(State.startFailed);
return; return;
} }
@ -84,25 +111,27 @@ public class DaemonSingleton {
synchronized (DaemonSingleton.this) { synchronized (DaemonSingleton.this) {
I2PD_JNI.setDataDir(Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd"); I2PD_JNI.setDataDir(Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd");
daemonStartResult = I2PD_JNI.startDaemon(); daemonStartResult = I2PD_JNI.startDaemon();
if("ok".equals(daemonStartResult)){ if ("ok".equals(daemonStartResult)) {
setState(State.startedOkay); setState(State.startedOkay);
setStartedOkay(true); setStartedOkay(true);
}else setState(State.startFailed); } else
setState(State.startFailed);
} }
} catch (Throwable tr) { } catch (Throwable tr) {
lastThrowable=tr; lastThrowable = tr;
setState(State.startFailed); setState(State.startFailed);
} }
} }
}, "i2pdDaemonStart").start(); }, "i2pdDaemonStart").start();
} }
private Throwable lastThrowable; private Throwable lastThrowable;
private String daemonStartResult="N/A"; private String daemonStartResult = "N/A";
private void fireStateUpdate1() { private void fireStateUpdate1() {
Log.i(TAG, "daemon state change: "+state); Log.i(TAG, "daemon state change: " + state);
for(StateUpdateListener listener : stateUpdateListeners) { for (StateUpdateListener listener : stateUpdateListeners) {
try { try {
listener.daemonStateUpdate(); listener.daemonStateUpdate();
} catch (Throwable tr) { } catch (Throwable tr) {
@ -134,8 +163,13 @@ public class DaemonSingleton {
} }
public synchronized void stopDaemon() { public synchronized void stopDaemon() {
if(isStartedOkay()){ if (isStartedOkay()) {
try {I2PD_JNI.stopDaemon();}catch(Throwable tr){Log.e(TAG, "", tr);} try {
I2PD_JNI.stopDaemon();
} catch(Throwable tr) {
Log.e(TAG, "", tr);
}
setStartedOkay(false); setStartedOkay(false);
setState(State.stopped); setState(State.stopped);
} }

380
android/src/org/purplei2p/i2pd/I2PDActivity.java

@ -68,42 +68,41 @@ public class I2PDActivity extends Activity {
private static final DaemonSingleton daemon = DaemonSingleton.getInstance(); private static final DaemonSingleton daemon = DaemonSingleton.getInstance();
private final DaemonSingleton.StateUpdateListener daemonStateUpdatedListener = private final DaemonSingleton.StateUpdateListener daemonStateUpdatedListener = new DaemonSingleton.StateUpdateListener() {
new DaemonSingleton.StateUpdateListener() {
@Override @Override
public void daemonStateUpdate() public void daemonStateUpdate() {
{
processAssets(); processAssets();
runOnUiThread(() -> { runOnUiThread(() -> {
try { try {
if(textView==null) return; if (textView==null)
Throwable tr = daemon.getLastThrowable(); return;
if(tr!=null) { Throwable tr = daemon.getLastThrowable();
textView.setText(throwableToString(tr)); if (tr!=null) {
return; textView.setText(throwableToString(tr));
} return;
DaemonSingleton.State state = daemon.getState(); }
String startResultStr = DaemonSingleton.State.startFailed.equals(state) ? String.format(": %s", daemon.getDaemonStartResult()) : ""; DaemonSingleton.State state = daemon.getState();
String graceStr = DaemonSingleton.State.gracefulShutdownInProgress.equals(state) ? String.format(": %s %s", formatGraceTimeRemaining(), getText(R.string.remaining)) : ""; String startResultStr = DaemonSingleton.State.startFailed.equals(state) ? String.format(": %s", daemon.getDaemonStartResult()) : "";
textView.setText(String.format("%s%s%s", getText(state.getStatusStringResourceId()), startResultStr, graceStr)); String graceStr = DaemonSingleton.State.gracefulShutdownInProgress.equals(state) ? String.format(": %s %s", formatGraceTimeRemaining(), getText(R.string.remaining)) : "";
} catch (Throwable tr) { textView.setText(String.format("%s%s%s", getText(state.getStatusStringResourceId()), startResultStr, graceStr));
Log.e(TAG,"error ignored",tr); } catch (Throwable tr) {
} Log.e(TAG,"error ignored",tr);
}); }
});
} }
}; };
private static volatile long graceStartedMillis; private static volatile long graceStartedMillis;
private static final Object graceStartedMillis_LOCK=new Object(); private static final Object graceStartedMillis_LOCK = new Object();
private Menu optionsMenu; private Menu optionsMenu;
private static String formatGraceTimeRemaining() { private static String formatGraceTimeRemaining() {
long remainingSeconds; long remainingSeconds;
synchronized (graceStartedMillis_LOCK){ synchronized (graceStartedMillis_LOCK) {
remainingSeconds=Math.round(Math.max(0,graceStartedMillis+GRACEFUL_DELAY_MILLIS-System.currentTimeMillis())/1000.0D); remainingSeconds = Math.round(Math.max(0, graceStartedMillis + GRACEFUL_DELAY_MILLIS - System.currentTimeMillis()) / 1000.0D);
} }
long remainingMinutes=(long)Math.floor(remainingSeconds/60.0D); long remainingMinutes = (long)Math.floor(remainingSeconds / 60.0D);
long remSec=remainingSeconds-remainingMinutes*60; long remSec = remainingSeconds - remainingMinutes * 60;
return remainingMinutes+":"+(remSec/10)+remSec%10; return remainingMinutes + ":" + (remSec / 10) + remSec % 10;
} }
@Override @Override
@ -117,10 +116,8 @@ public class I2PDActivity extends Activity {
daemonStateUpdatedListener.daemonStateUpdate(); daemonStateUpdatedListener.daemonStateUpdate();
// request permissions // request permissions
if (Build.VERSION.SDK_INT >= 23) if (Build.VERSION.SDK_INT >= 23) {
{ if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
{
ActivityCompat.requestPermissions(this, ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE); MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE);
@ -131,7 +128,7 @@ public class I2PDActivity extends Activity {
doBindService(); doBindService();
final Timer gracefulQuitTimer = getGracefulQuitTimer(); final Timer gracefulQuitTimer = getGracefulQuitTimer();
if(gracefulQuitTimer!=null){ if (gracefulQuitTimer!=null) {
long gracefulStopAtMillis; long gracefulStopAtMillis;
synchronized (graceStartedMillis_LOCK) { synchronized (graceStartedMillis_LOCK) {
gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS; gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS;
@ -148,9 +145,9 @@ public class I2PDActivity extends Activity {
textView = null; textView = null;
daemon.removeStateChangeListener(daemonStateUpdatedListener); daemon.removeStateChangeListener(daemonStateUpdatedListener);
//cancelGracefulStop0(); //cancelGracefulStop0();
try{ try {
doUnbindService(); doUnbindService();
}catch(Throwable tr){ } catch(Throwable tr) {
Log.e(TAG, "", tr); Log.e(TAG, "", tr);
} }
} }
@ -158,20 +155,20 @@ public class I2PDActivity extends Activity {
@Override @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
{ {
if (requestCode == MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE) { if (requestCode == MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
Log.e(TAG, "WR_EXT_STORAGE perm granted"); Log.e(TAG, "WR_EXT_STORAGE perm granted");
else { else {
Log.e(TAG, "WR_EXT_STORAGE perm declined, stopping i2pd"); Log.e(TAG, "WR_EXT_STORAGE perm declined, stopping i2pd");
i2pdStop(); i2pdStop();
//TODO must work w/o this perm, ask orignal //TODO must work w/o this perm, ask orignal
} }
} }
} }
private void cancelGracefulStop0() { private void cancelGracefulStop0() {
Timer gracefulQuitTimer = getGracefulQuitTimer(); Timer gracefulQuitTimer = getGracefulQuitTimer();
if(gracefulQuitTimer!=null) { if (gracefulQuitTimer != null) {
gracefulQuitTimer.cancel(); gracefulQuitTimer.cancel();
setGracefulQuitTimer(null); setGracefulQuitTimer(null);
} }
@ -216,7 +213,8 @@ public class I2PDActivity extends Activity {
private void doBindService() { private void doBindService() {
synchronized (I2PDActivity.class) { synchronized (I2PDActivity.class) {
if (mIsBound) return; if (mIsBound)
return;
// Establish a connection with the service. We use an explicit // Establish a connection with the service. We use an explicit
// class name because we want a specific service implementation that // class name because we want a specific service implementation that
// we know will be running in our own process (and thus won't be // we know will be running in our own process (and thus won't be
@ -256,10 +254,11 @@ public class I2PDActivity extends Activity {
// as you specify a parent activity in AndroidManifest.xml. // as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId(); int id = item.getItemId();
switch(id){ switch(id) {
case R.id.action_stop: case R.id.action_stop:
i2pdStop(); i2pdStop();
return true; return true;
case R.id.action_graceful_stop: case R.id.action_graceful_stop:
synchronized (graceStartedMillis_LOCK) { synchronized (graceStartedMillis_LOCK) {
if (getGracefulQuitTimer() != null) if (getGracefulQuitTimer() != null)
@ -268,9 +267,15 @@ public class I2PDActivity extends Activity {
i2pdGracefulStop(); i2pdGracefulStop();
} }
return true; return true;
case R.id.action_battery_otimizations: case R.id.action_battery_otimizations:
onActionBatteryOptimizations(); onActionBatteryOptimizations();
return true; return true;
case R.id.action_reload_tunnels_config:
onReloadTunnelsConfig();
return true;
case R.id.action_start_webview: case R.id.action_start_webview:
setContentView(R.layout.webview); setContentView(R.layout.webview);
this.webView = (WebView) findViewById(R.id.webview1); this.webView = (WebView) findViewById(R.id.webview1);
@ -291,100 +296,100 @@ public class I2PDActivity extends Activity {
try { try {
startActivity(new Intent(ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS)); startActivity(new Intent(ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS));
} catch (ActivityNotFoundException e) { } catch (ActivityNotFoundException e) {
Log.e(TAG,"BATT_OPTIM_DIALOG_ActvtNotFound", e); Log.e(TAG, "BATT_OPTIM_DIALOG_ActvtNotFound", e);
Toast.makeText(this, R.string.os_version_does_not_support_battery_optimizations_show_os_dialog_api, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.os_version_does_not_support_battery_optimizations_show_os_dialog_api, Toast.LENGTH_SHORT).show();
} }
} }
} }
private void onReloadTunnelsConfig() {
Log.d(TAG, "reloading tunnels");
daemon.reloadTunnelsConfigs();
Toast.makeText(this, R.string.tunnels_reloading, Toast.LENGTH_SHORT).show();
}
private void i2pdStop() { private void i2pdStop() {
cancelGracefulStop0(); cancelGracefulStop0();
new Thread(() -> { new Thread(() -> {
Log.d(TAG, "stopping"); Log.d(TAG, "stopping");
try { try {
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(); //TODO make menu items for starting i2pd. On my Android, I need to reboot the OS to restart i2pd.
},"stop").start(); }, "stop").start();
} }
private static volatile Timer gracefulQuitTimer; private static volatile Timer gracefulQuitTimer;
private void i2pdGracefulStop() { private void i2pdGracefulStop() {
if(daemon.getState()==DaemonSingleton.State.stopped){ if (daemon.getState() == DaemonSingleton.State.stopped) {
Toast.makeText(this, R.string.already_stopped, Toast.makeText(this, R.string.already_stopped, Toast.LENGTH_SHORT).show();
Toast.LENGTH_SHORT).show();
return; return;
} }
if(getGracefulQuitTimer()!=null){ if (getGracefulQuitTimer() != null) {
Toast.makeText(this, R.string.graceful_stop_is_already_in_progress, Toast.makeText(this, R.string.graceful_stop_is_already_in_progress, Toast.LENGTH_SHORT).show();
Toast.LENGTH_SHORT).show();
return; return;
} }
Toast.makeText(this, R.string.graceful_stop_is_in_progress, Toast.makeText(this, R.string.graceful_stop_is_in_progress, Toast.LENGTH_SHORT).show();
Toast.LENGTH_SHORT).show();
new Thread(() -> { new Thread(() -> {
try { try {
Log.d(TAG, "grac stopping"); Log.d(TAG, "graceful stopping");
if(daemon.isStartedOkay()) { if (daemon.isStartedOkay()) {
daemon.stopAcceptingTunnels(); daemon.stopAcceptingTunnels();
long gracefulStopAtMillis; long gracefulStopAtMillis;
synchronized (graceStartedMillis_LOCK) { synchronized (graceStartedMillis_LOCK) {
graceStartedMillis = System.currentTimeMillis(); graceStartedMillis = System.currentTimeMillis();
gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS; gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS;
} }
rescheduleGraceStop(null,gracefulStopAtMillis); rescheduleGraceStop(null, gracefulStopAtMillis);
} else { } else
i2pdStop(); i2pdStop();
} } catch(Throwable tr) {
} catch(Throwable tr) { Log.e(TAG, "", tr);
Log.e(TAG,"",tr); }
} }, "gracInit").start();
},"gracInit").start();
} }
private void cancelGracefulStop() private void cancelGracefulStop()
{ {
cancelGracefulStop0(); cancelGracefulStop0();
new Thread(() -> { new Thread(() -> {
try try {
{ Log.d(TAG, "canceling graceful stop");
Log.d(TAG, "canceling grac stop"); if (daemon.isStartedOkay()) {
if(daemon.isStartedOkay()) {
daemon.startAcceptingTunnels(); daemon.startAcceptingTunnels();
runOnUiThread(() -> Toast.makeText(this, R.string.shutdown_canceled, Toast.LENGTH_SHORT).show()); runOnUiThread(() -> Toast.makeText(this, R.string.shutdown_canceled, Toast.LENGTH_SHORT).show());
} } else
else i2pdStop();
i2pdStop(); } catch(Throwable tr) {
} Log.e(TAG, "", tr);
catch(Throwable tr) }
{ }, "gracCancel").start();
Log.e(TAG,"",tr);
}
},"gracCancel").start();
} }
private void rescheduleGraceStop(Timer gracefulQuitTimerOld, long gracefulStopAtMillis) { private void rescheduleGraceStop(Timer gracefulQuitTimerOld, long gracefulStopAtMillis) {
if(gracefulQuitTimerOld!=null)gracefulQuitTimerOld.cancel(); if (gracefulQuitTimerOld != null)
gracefulQuitTimerOld.cancel();
final Timer gracefulQuitTimer = new Timer(true); final Timer gracefulQuitTimer = new Timer(true);
setGracefulQuitTimer(gracefulQuitTimer); setGracefulQuitTimer(gracefulQuitTimer);
gracefulQuitTimer.schedule(new TimerTask(){ gracefulQuitTimer.schedule(new TimerTask() {
@Override @Override
public void run() { public void run() {
i2pdStop(); i2pdStop();
} }
}, Math.max(0,gracefulStopAtMillis-System.currentTimeMillis())); }, Math.max(0, gracefulStopAtMillis - System.currentTimeMillis()));
final TimerTask tickerTask = new TimerTask() { final TimerTask tickerTask = new TimerTask() {
@Override @Override
public void run() { public void run() {
daemonStateUpdatedListener.daemonStateUpdate(); daemonStateUpdatedListener.daemonStateUpdate();
} }
}; };
gracefulQuitTimer.scheduleAtFixedRate(tickerTask,0/*start delay*/,1000/*millis period*/); gracefulQuitTimer.scheduleAtFixedRate(tickerTask, 0/*start delay*/, 1000/*millis period*/);
} }
private static Timer getGracefulQuitTimer() { private static Timer getGracefulQuitTimer() {
@ -454,9 +459,9 @@ public class I2PDActivity extends Activity {
*/ */
private void copyFileAsset(String path) { private void copyFileAsset(String path) {
File file = new File(i2pdpath, path); File file = new File(i2pdpath, path);
if(!file.exists()) { if (!file.exists()) {
try { try {
try (InputStream in = getAssets().open(path) ) { try (InputStream in = getAssets().open(path)) {
try (OutputStream out = new FileOutputStream(file)) { try (OutputStream out = new FileOutputStream(file)) {
byte[] buffer = new byte[1024]; byte[] buffer = new byte[1024];
int read = in.read(buffer); int read = in.read(buffer);
@ -475,97 +480,102 @@ public class I2PDActivity extends Activity {
private void deleteRecursive(File fileOrDirectory) { private void deleteRecursive(File fileOrDirectory) {
if (fileOrDirectory.isDirectory()) { if (fileOrDirectory.isDirectory()) {
File[] files = fileOrDirectory.listFiles(); File[] files = fileOrDirectory.listFiles();
if(files!=null) { if (files!=null) {
for (File child : files) { for (File child : files) {
deleteRecursive(child); deleteRecursive(child);
} }
} }
} }
boolean deleteResult = fileOrDirectory.delete(); boolean deleteResult = fileOrDirectory.delete();
if(!deleteResult)Log.e(TAG, "fileOrDirectory.delete() returned "+deleteResult+", absolute path='"+fileOrDirectory.getAbsolutePath()+"'"); if (!deleteResult)
Log.e(TAG, "fileOrDirectory.delete() returned " + deleteResult + ", absolute path='" + fileOrDirectory.getAbsolutePath() + "'");
} }
private void processAssets() { private void processAssets() {
if (!assetsCopied) try { if (!assetsCopied) {
assetsCopied = true; // prevent from running on every state update try {
assetsCopied = true; // prevent from running on every state update
File holderFile = new File(i2pdpath, "assets.ready");
String versionName = BuildConfig.VERSION_NAME; // here will be app version, like 2.XX.XX File holderFile = new File(i2pdpath, "assets.ready");
StringBuilder text = new StringBuilder(); String versionName = BuildConfig.VERSION_NAME; // here will be app version, like 2.XX.XX
StringBuilder text = new StringBuilder();
if (holderFile.exists()) {
try { // if holder file exists, read assets version string if (holderFile.exists()) {
FileReader fileReader = new FileReader(holderFile); try { // if holder file exists, read assets version string
FileReader fileReader = new FileReader(holderFile);
try {
BufferedReader br = new BufferedReader(fileReader); try {
BufferedReader br = new BufferedReader(fileReader);
try {
String line; try {
String line;
while ((line = br.readLine()) != null) {
text.append(line); while ((line = br.readLine()) != null) {
} text.append(line);
}finally { }
try{ }finally {
br.close(); try {
} catch (IOException e) { br.close();
Log.e(TAG, "", e); } catch (IOException e) {
} Log.e(TAG, "", e);
} }
} finally { }
try{ } finally {
fileReader.close(); try {
} catch (IOException e) { fileReader.close();
Log.e(TAG, "", e); } catch (IOException e) {
} Log.e(TAG, "", e);
} }
} catch (IOException e) { }
Log.e(TAG, "", e); } catch (IOException e) {
} Log.e(TAG, "", e);
} }
}
// if version differs from current app version or null, try to delete certificates folder
if (!text.toString().contains(versionName)) try {
boolean deleteResult = holderFile.delete();
if(!deleteResult)Log.e(TAG, "holderFile.delete() returned "+deleteResult+", absolute path='"+holderFile.getAbsolutePath()+"'");
File certPath = new File(i2pdpath, "certificates");
deleteRecursive(certPath);
}
catch (Throwable tr) {
Log.e(TAG, "", tr);
}
// copy assets. If processed file exists, it won't be overwritten // if version differs from current app version or null, try to delete certificates folder
copyAsset("addressbook"); if (!text.toString().contains(versionName))
copyAsset("certificates"); try {
copyAsset("tunnels.d"); boolean deleteResult = holderFile.delete();
copyAsset("i2pd.conf"); if (!deleteResult)
copyAsset("subscriptions.txt"); Log.e(TAG, "holderFile.delete() returned " + deleteResult + ", absolute path='" + holderFile.getAbsolutePath() + "'");
copyAsset("tunnels.conf"); File certPath = new File(i2pdpath, "certificates");
deleteRecursive(certPath);
}
catch (Throwable tr) {
Log.e(TAG, "", tr);
}
// update holder file about successful copying // copy assets. If processed file exists, it won't be overwritten
FileWriter writer = new FileWriter(holderFile); copyAsset("addressbook");
try { copyAsset("certificates");
writer.append(versionName); copyAsset("tunnels.d");
} finally { copyAsset("i2pd.conf");
try{ copyAsset("subscriptions.txt");
writer.close(); copyAsset("tunnels.conf");
}catch (IOException e){
Log.e(TAG,"on writer close", e); // update holder file about successful copying
} FileWriter writer = new FileWriter(holderFile);
} try {
} writer.append(versionName);
catch (Throwable tr) } finally {
{ try {
Log.e(TAG,"on assets copying", tr); writer.close();
} catch (IOException e) {
Log.e(TAG,"on writer close", e);
}
}
}
catch (Throwable tr)
{
Log.e(TAG,"on assets copying", tr);
}
} }
} }
@SuppressLint("BatteryLife") @SuppressLint("BatteryLife")
private void openBatteryOptimizationDialogIfNeeded() { private void openBatteryOptimizationDialogIfNeeded() {
boolean questionEnabled = getPreferences().getBoolean(getBatteryOptimizationPreferenceKey(), true); boolean questionEnabled = getPreferences().getBoolean(getBatteryOptimizationPreferenceKey(), true);
Log.i(TAG,"BATT_OPTIM_questionEnabled=="+questionEnabled); Log.i(TAG, "BATT_OPTIM_questionEnabled==" + questionEnabled);
if (!isKnownIgnoringBatteryOptimizations() if (!isKnownIgnoringBatteryOptimizations()
&& android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M
&& questionEnabled) { && questionEnabled) {
@ -576,7 +586,7 @@ public class I2PDActivity extends Activity {
try { try {
startActivity(new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, Uri.parse(PACKAGE_URI_SCHEME + getPackageName()))); startActivity(new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, Uri.parse(PACKAGE_URI_SCHEME + getPackageName())));
} catch (ActivityNotFoundException e) { } catch (ActivityNotFoundException e) {
Log.e(TAG,"BATT_OPTIM_ActvtNotFound", e); Log.e(TAG, "BATT_OPTIM_ActvtNotFound", e);
Toast.makeText(this, R.string.device_does_not_support_disabling_battery_optimizations, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.device_does_not_support_disabling_battery_optimizations, Toast.LENGTH_SHORT).show();
} }
}); });
@ -595,14 +605,14 @@ public class I2PDActivity extends Activity {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
final PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE); final PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
if (pm == null) { if (pm == null) {
Log.i(TAG, "BATT_OPTIM: POWER_SERVICE==null"); Log.i(TAG, "BATT_OPTIM: POWER_SERVICE==null");
return false; return false;
} }
boolean ignoring = pm.isIgnoringBatteryOptimizations(getPackageName()); boolean ignoring = pm.isIgnoringBatteryOptimizations(getPackageName());
Log.i(TAG, "BATT_OPTIM: ignoring==" + ignoring); Log.i(TAG, "BATT_OPTIM: ignoring==" + ignoring);
return ignoring; return ignoring;
} else { } else {
Log.i(TAG, "BATT_OPTIM: old sdk version=="+Build.VERSION.SDK_INT); Log.i(TAG, "BATT_OPTIM: old sdk version==" + Build.VERSION.SDK_INT);
return false; return false;
} }
} }
@ -626,12 +636,12 @@ public class I2PDActivity extends Activity {
//moveTaskToBack(true); //moveTaskToBack(true);
finish(); finish();
} }
}catch (Throwable tr) { } catch (Throwable tr) {
Log.e(TAG, "", tr); Log.e(TAG, "", tr);
} }
try{ try {
daemon.stopDaemon(); daemon.stopDaemon();
}catch (Throwable tr) { } catch (Throwable tr) {
Log.e(TAG, "", tr); Log.e(TAG, "", tr);
} }
System.exit(0); System.exit(0);

2
android/src/org/purplei2p/i2pd/I2PD_JNI.java

@ -16,6 +16,8 @@ public class I2PD_JNI {
public static native void startAcceptingTunnels(); public static native void startAcceptingTunnels();
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);

Loading…
Cancel
Save