Browse Source

[android] build libi2pd staticly, add update strings, fix messages

pull/1288/head
R4SAS 6 years ago
parent
commit
d7609f119c
  1. 15
      android/build.gradle
  2. 7
      android/jni/Application.mk
  3. 36
      android/jni/DaemonAndroid.cpp
  4. 96
      android/jni/DaemonAndroid.h
  5. 75
      android/jni/i2pd_android.cpp
  6. 12
      android/jni/org_purplei2p_i2pd_I2PD_JNI.h
  7. 19
      android/res/values-ru/strings.xml
  8. 18
      android/res/values/strings.xml
  9. 38
      android/src/org/purplei2p/i2pd/DaemonSingleton.java
  10. 259
      android/src/org/purplei2p/i2pd/ForegroundService.java
  11. 32
      android/src/org/purplei2p/i2pd/I2PDActivity.java
  12. 306
      android/src/org/purplei2p/i2pd/I2PDPermsAskerActivity.java
  13. 48
      android/src/org/purplei2p/i2pd/I2PDPermsExplanationActivity.java
  14. 18
      android/src/org/purplei2p/i2pd/I2PD_JNI.java
  15. 10
      android/src/org/purplei2p/i2pd/NetworkStateChangeReceiver.java

15
android/build.gradle

@ -28,15 +28,17 @@ android {
applicationId "org.purplei2p.i2pd" applicationId "org.purplei2p.i2pd"
targetSdkVersion 28 targetSdkVersion 28
minSdkVersion 14 minSdkVersion 14
versionCode 1 versionCode 2220
versionName "2.22.0" versionName "2.22.0"
ndk { ndk {
abiFilters 'armeabi-v7a' abiFilters 'armeabi-v7a'
abiFilters 'x86' abiFilters 'x86'
//abiFilters 'arm64-v8a'
//abiFilters 'x86_64'
} }
externalNativeBuild { externalNativeBuild {
ndkBuild { ndkBuild {
arguments "-j4" arguments "-j3"
} }
} }
} }
@ -49,6 +51,15 @@ android {
assets.srcDirs = ['assets'] assets.srcDirs = ['assets']
} }
} }
splits {
abi {
// change that to true if you need splitted apk
enable false
reset()
include "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
universalApk true
}
}
signingConfigs { signingConfigs {
orignal { orignal {
storeFile file("i2pdapk.jks") storeFile file("i2pdapk.jks")

7
android/jni/Application.mk

@ -1,12 +1,13 @@
APP_ABI := armeabi-v7a x86 #APP_ABI := armeabi-v7a x86
APP_PLATFORM := android-14 #APP_PLATFORM := android-14
# ABI arm64-v8a and x86_64 supported only from platform-21 # ABI arm64-v8a and x86_64 supported only from platform-21
#APP_ABI := arm64-v8a x86_64 #APP_ABI := arm64-v8a x86_64
#APP_PLATFORM := android-21 #APP_PLATFORM := android-21
NDK_TOOLCHAIN_VERSION := clang NDK_TOOLCHAIN_VERSION := clang
APP_STL := c++_shared #APP_STL := c++_shared
APP_STL := c++_static
# Enable c++11 extensions in source code # Enable c++11 extensions in source code
APP_CPPFLAGS += -std=c++11 -fexceptions -frtti APP_CPPFLAGS += -std=c++11 -fexceptions -frtti

36
android/jni/DaemonAndroid.cpp

@ -39,7 +39,7 @@ namespace android
emit resultReady(); emit resultReady();
} }
Controller::Controller(DaemonAndroidImpl& daemon): Controller::Controller(DaemonAndroidImpl& daemon):
m_Daemon (daemon) m_Daemon (daemon)
{ {
Worker *worker = new Worker (m_Daemon); Worker *worker = new Worker (m_Daemon);
@ -57,19 +57,19 @@ namespace android
workerThread.quit(); workerThread.quit();
workerThread.wait(); workerThread.wait();
Log.d(TAG"Waiting for daemon worker thread finished."); Log.d(TAG"Waiting for daemon worker thread finished.");
if(m_Daemon.isRunning()) if(m_Daemon.isRunning())
{ {
Log.d(TAG"Stopping the daemon..."); Log.d(TAG"Stopping the daemon...");
m_Daemon.stop(); m_Daemon.stop();
Log.d(TAG"Stopped the daemon."); Log.d(TAG"Stopped the daemon.");
} }
} }
*/ */
DaemonAndroidImpl::DaemonAndroidImpl () DaemonAndroidImpl::DaemonAndroidImpl ()
//: //:
/*mutex(nullptr), */ /*mutex(nullptr), */
//m_IsRunning(false), //m_IsRunning(false),
//m_RunningChangedCallback(nullptr) //m_RunningChangedCallback(nullptr)
{ {
} }
@ -82,7 +82,7 @@ namespace android
{ {
//mutex=new QMutex(QMutex::Recursive); //mutex=new QMutex(QMutex::Recursive);
//setRunningCallback(0); //setRunningCallback(0);
//m_IsRunning=false; //m_IsRunning=false;
// make sure assets are ready before proceed // make sure assets are ready before proceed
i2p::fs::DetectDataDir("", false); i2p::fs::DetectDataDir("", false);
@ -125,16 +125,16 @@ namespace android
bool DaemonAndroidImpl::isRunning() bool DaemonAndroidImpl::isRunning()
{ {
return m_IsRunning; return m_IsRunning;
} }
void DaemonAndroidImpl::setRunning(bool newValue) void DaemonAndroidImpl::setRunning(bool newValue)
{ {
bool oldValue = m_IsRunning; bool oldValue = m_IsRunning;
if(oldValue!=newValue) if(oldValue!=newValue)
{ {
m_IsRunning = newValue; m_IsRunning = newValue;
if(m_RunningChangedCallback) if(m_RunningChangedCallback)
m_RunningChangedCallback(); m_RunningChangedCallback();
} }
} }
@ -183,14 +183,14 @@ namespace android
catch (boost::exception& ex) catch (boost::exception& ex)
{ {
std::stringstream ss; std::stringstream ss;
ss << boost::diagnostic_information(ex); ss << boost::diagnostic_information(ex);
return ss.str(); return ss.str();
} }
catch (std::exception& ex) catch (std::exception& ex)
{ {
std::stringstream ss; std::stringstream ss;
ss << ex.what(); ss << ex.what();
return ss.str(); return ss.str();
} }
catch(...) catch(...)
{ {

96
android/jni/DaemonAndroid.h

@ -7,45 +7,45 @@ namespace i2p
{ {
namespace android namespace android
{ {
class DaemonAndroidImpl class DaemonAndroidImpl
{ {
public: public:
DaemonAndroidImpl (); DaemonAndroidImpl ();
~DaemonAndroidImpl (); ~DaemonAndroidImpl ();
//typedef void (*runningChangedCallback)(); //typedef void (*runningChangedCallback)();
/** /**
* @return success * @return success
*/ */
bool init(int argc, char* argv[]); bool init(int argc, char* argv[]);
void start(); void start();
void stop(); void stop();
void restart(); void restart();
//void setRunningCallback(runningChangedCallback cb); //void setRunningCallback(runningChangedCallback cb);
//bool isRunning(); //bool isRunning();
private: private:
//void setRunning(bool running); //void setRunning(bool running);
private: private:
//QMutex* mutex; //QMutex* mutex;
//bool m_IsRunning; //bool m_IsRunning;
//runningChangedCallback m_RunningChangedCallback; //runningChangedCallback m_RunningChangedCallback;
}; };
/** /**
* returns "ok" if daemon init failed * returns "ok" if daemon init failed
* returns errinfo if daemon initialized and started okay * returns errinfo if daemon initialized and started okay
*/ */
std::string start(); std::string start();
// stops the daemon // stops the daemon
void stop(); void stop();
/* /*
class Worker : public QObject class Worker : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
Worker (DaemonAndroidImpl& daemon); Worker (DaemonAndroidImpl& daemon);
@ -54,33 +54,33 @@ namespace android
DaemonAndroidImpl& m_Daemon; DaemonAndroidImpl& m_Daemon;
public slots: public slots:
void startDaemon(); void startDaemon();
void restartDaemon(); void restartDaemon();
void stopDaemon(); void stopDaemon();
signals: signals:
void resultReady(); void resultReady();
}; };
class Controller : public QObject class Controller : public QObject
{ {
Q_OBJECT Q_OBJECT
QThread workerThread; QThread workerThread;
public: public:
Controller(DaemonAndroidImpl& daemon); Controller(DaemonAndroidImpl& daemon);
~Controller(); ~Controller();
private: private:
DaemonAndroidImpl& m_Daemon; DaemonAndroidImpl& m_Daemon;
public slots: public slots:
void handleResults(){} void handleResults(){}
signals: signals:
void startDaemon(); void startDaemon();
void stopDaemon(); void stopDaemon();
void restartDaemon(); void restartDaemon();
}; };
*/ */
} }
} }

75
android/jni/i2pd_android.cpp

@ -1,5 +1,3 @@
//#include <string.h>
#include <jni.h> #include <jni.h>
#include "org_purplei2p_i2pd_I2PD_JNI.h" #include "org_purplei2p_i2pd_I2PD_JNI.h"
#include "DaemonAndroid.h" #include "DaemonAndroid.h"
@ -7,65 +5,64 @@
#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
(JNIEnv * env, jclass clazz) { (JNIEnv * env, jclass clazz) {
#if defined(__arm__) #if defined(__arm__)
#if defined(__ARM_ARCH_7A__) #if defined(__ARM_ARCH_7A__)
#if defined(__ARM_NEON__) #if defined(__ARM_NEON__)
#if defined(__ARM_PCS_VFP) #if defined(__ARM_PCS_VFP)
#define ABI "armeabi-v7a/NEON (hard-float)" #define ABI "armeabi-v7a/NEON (hard-float)"
#else #else
#define ABI "armeabi-v7a/NEON" #define ABI "armeabi-v7a/NEON"
#endif #endif
#else #else
#if defined(__ARM_PCS_VFP) #if defined(__ARM_PCS_VFP)
#define ABI "armeabi-v7a (hard-float)" #define ABI "armeabi-v7a (hard-float)"
#else #else
#define ABI "armeabi-v7a" #define ABI "armeabi-v7a"
#endif #endif
#endif #endif
#else #else
#define ABI "armeabi" #define ABI "armeabi"
#endif #endif
#elif defined(__i386__) #elif defined(__i386__)
#define ABI "x86" #define ABI "x86"
#elif defined(__x86_64__) #elif defined(__x86_64__)
#define ABI "x86_64" #define ABI "x86_64"
#elif defined(__mips64) /* mips64el-* toolchain defines __mips__ too */ #elif defined(__mips64) /* mips64el-* toolchain defines __mips__ too */
#define ABI "mips64" #define ABI "mips64"
#elif defined(__mips__) #elif defined(__mips__)
#define ABI "mips" #define ABI "mips"
#elif defined(__aarch64__) #elif defined(__aarch64__)
#define ABI "arm64-v8a" #define ABI "arm64-v8a"
#else #else
#define ABI "unknown" #define ABI "unknown"
#endif #endif
return env->NewStringUTF(ABI); return env->NewStringUTF(ABI);
} }
JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startDaemon JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startDaemon
(JNIEnv * env, jclass clazz) { (JNIEnv * env, jclass clazz) {
return env->NewStringUTF(i2p::android::start().c_str()); return env->NewStringUTF(i2p::android::start().c_str());
} }
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopDaemon JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopDaemon
(JNIEnv * env, jclass clazz) { (JNIEnv * env, jclass clazz) {
i2p::android::stop(); i2p::android::stop();
} }
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopAcceptingTunnels JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopAcceptingTunnels
(JNIEnv * env, jclass clazz) { (JNIEnv * env, jclass clazz) {
i2p::context.SetAcceptsTunnels (false); i2p::context.SetAcceptsTunnels (false);
} }
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startAcceptingTunnels JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startAcceptingTunnels
(JNIEnv * env, jclass clazz) { (JNIEnv * env, jclass clazz) {
i2p::context.SetAcceptsTunnels (true); i2p::context.SetAcceptsTunnels (true);
} }
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;
i2p::transport::transports.SetOnline (isConnectedBool); i2p::transport::transports.SetOnline (isConnectedBool);
} }

12
android/jni/org_purplei2p_i2pd_I2PD_JNI.h

@ -13,22 +13,22 @@ extern "C" {
* Signature: ()Ljava/lang/String; * 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 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_stopAcceptingTunnels
(JNIEnv *, jclass); (JNIEnv *, jclass);
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_onNetworkStateChanged JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_onNetworkStateChanged
(JNIEnv * env, jclass clazz, jboolean isConnected); (JNIEnv * env, jclass clazz, jboolean isConnected);
#ifdef __cplusplus #ifdef __cplusplus
} }

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

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">i2pd</string>
<string name="action_stop">Остановить</string>
<string name="action_graceful_stop">Корректная остановка</string>
<string name="action_cancel_graceful_stop">Отменить мягкую остановку</string>
<string name="graceful_stop_is_already_in_progress">Мягкая остановка уже запущена</string>
<string name="graceful_stop_is_in_progress">Мягкая остановка запущена</string>
<string name="already_stopped">Уже остановлено</string>
<string name="uninitialized">Приложение инициализируется</string>
<string name="starting">Приложение запускается</string>
<string name="jniLibraryLoaded">Загружены JNI библиотеки</string>
<string name="startedOkay">Приложение запущено</string>
<string name="startFailed">Запуск не удался</string>
<string name="gracefulShutdownInProgress">Мягкая остановка запущена</string>
<string name="stopped">Приложение было остановлено</string>
<string name="remaining">осталось</string>
<string name="title_activity_i2_pdperms_asker_prompt">Запрос</string>
</resources>

18
android/res/values/strings.xml

@ -1,19 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <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="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="already_stopped">Already stopped</string> <string name="already_stopped">Already stopped</string>
<string name="uninitialized">i2pd initializing</string> <string name="uninitialized">Application initializing</string>
<string name="starting">i2pd is starting</string> <string name="starting">Application starting</string>
<string name="jniLibraryLoaded">i2pd: loaded JNI libraries</string> <string name="jniLibraryLoaded">Loaded JNI libraries</string>
<string name="startedOkay">i2pd started</string> <string name="startedOkay">Application Started</string>
<string name="startFailed">i2pd start failed</string> <string name="startFailed">Start failed</string>
<string name="gracefulShutdownInProgress">i2pd: graceful shutdown in progress</string> <string name="gracefulShutdownInProgress">Graceful shutdown in progress</string>
<string name="stopped">i2pd has stopped</string> <string name="stopped">Application stopped</string>
<string name="remaining">remaining</string> <string name="remaining">remaining</string>
<string name="title_activity_i2_pdperms_asker_prompt">Prompt</string> <string name="title_activity_i2_pdperms_asker_prompt">Prompt</string>
</resources> </resources>

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

@ -2,8 +2,8 @@ package org.purplei2p.i2pd;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import android.util.Log; import android.util.Log;
import org.purplei2p.i2pd.R;
public class DaemonSingleton { public class DaemonSingleton {
private static final String TAG="i2pd"; private static final String TAG="i2pd";
@ -43,24 +43,24 @@ public class DaemonSingleton {
private volatile boolean startedOkay; private volatile boolean startedOkay;
public enum State { public enum State {
uninitialized(R.string.uninitialized), uninitialized(R.string.uninitialized),
starting(R.string.starting), starting(R.string.starting),
jniLibraryLoaded(R.string.jniLibraryLoaded), jniLibraryLoaded(R.string.jniLibraryLoaded),
startedOkay(R.string.startedOkay), startedOkay(R.string.startedOkay),
startFailed(R.string.startFailed), startFailed(R.string.startFailed),
gracefulShutdownInProgress(R.string.gracefulShutdownInProgress), gracefulShutdownInProgress(R.string.gracefulShutdownInProgress),
stopped(R.string.stopped); stopped(R.string.stopped);
State(int statusStringResourceId) { State(int statusStringResourceId) {
this.statusStringResourceId = statusStringResourceId; this.statusStringResourceId = statusStringResourceId;
} }
private final int statusStringResourceId; private final int statusStringResourceId;
public int getStatusStringResourceId() { public int getStatusStringResourceId() {
return statusStringResourceId; return statusStringResourceId;
} }
}; };
private volatile State state = State.uninitialized; private volatile State state = State.uninitialized;

259
android/src/org/purplei2p/i2pd/ForegroundService.java

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

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

@ -66,9 +66,9 @@ public class I2PDActivity extends Activity {
} }
DaemonSingleton.State state = daemon.getState(); DaemonSingleton.State state = daemon.getState();
textView.setText( textView.setText(
String.valueOf(state)+ String.valueOf(getText(state.getStatusStringResourceId()))+
(DaemonSingleton.State.startFailed.equals(state)?": "+daemon.getDaemonStartResult():"")+ (DaemonSingleton.State.startFailed.equals(state) ? ": "+daemon.getDaemonStartResult() : "")+
(DaemonSingleton.State.gracefulShutdownInProgress.equals(state)?": "+formatGraceTimeRemaining()+" "+getText(R.string.remaining):"") (DaemonSingleton.State.gracefulShutdownInProgress.equals(state) ? ": "+formatGraceTimeRemaining()+" "+getText(R.string.remaining) : "")
); );
} catch (Throwable tr) { } catch (Throwable tr) {
Log.e(TAG,"error ignored",tr); Log.e(TAG,"error ignored",tr);
@ -100,14 +100,14 @@ 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);
} }
} }
// set the app be foreground // set the app be foreground
@ -139,19 +139,19 @@ public class I2PDActivity extends Activity {
@Override @Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults)
{ {
switch (requestCode) switch (requestCode)
{ {
case MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE: case 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, "Memory permission granted"); Log.e(TAG, "Memory permission granted");
else else
Log.e(TAG, "Memory permission declined"); Log.e(TAG, "Memory permission declined");
// TODO: terminate // TODO: terminate
return; return;
} }
default: ; default: ;
} }
} }
private static void cancelGracefulStop() { private static void cancelGracefulStop() {

306
android/src/org/purplei2p/i2pd/I2PDPermsAskerActivity.java

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

48
android/src/org/purplei2p/i2pd/I2PDPermsExplanationActivity.java

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

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

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

10
android/src/org/purplei2p/i2pd/NetworkStateChangeReceiver.java

@ -9,12 +9,12 @@ import android.net.NetworkInfo;
public class NetworkStateChangeReceiver extends BroadcastReceiver { public class NetworkStateChangeReceiver extends BroadcastReceiver {
private static final String TAG = "i2pd"; private static final String TAG = "i2pd";
//api level 1 //api level 1
@Override @Override
public void onReceive(final Context context, final Intent intent) { public void onReceive(final Context context, final Intent intent) {
Log.d(TAG,"Network state change"); Log.d(TAG,"Network state change");
try { try {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = cm.getActiveNetworkInfo(); NetworkInfo activeNetworkInfo = cm.getActiveNetworkInfo();
@ -26,5 +26,5 @@ public class NetworkStateChangeReceiver extends BroadcastReceiver {
} catch (Throwable tr) { } catch (Throwable tr) {
Log.d(TAG,"",tr); Log.d(TAG,"",tr);
} }
} }
} }

Loading…
Cancel
Save