diff --git a/android/app/build.gradle b/android/app/build.gradle index 7202b12494..301a07656c 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -133,7 +133,7 @@ dependencies { compile project(':react-native-camera') compile project(':react-native-orientation') // compile(name:'geth', ext:'aar') - compile(group: 'status-im', name: 'android-geth', version: '1.4.0-201604110816-a97a114', ext: 'aar') + compile(group: 'status-im', name: 'status-go', version: '0.1.0-test-callback', ext: 'aar') compile fileTree(dir: "node_modules/realm/android/libs", include: ["*.jar"]) } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 42b5ee2517..a7b7a31725 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -23,7 +23,7 @@ diff --git a/android/app/src/main/java/com/statusim/GethService.java b/android/app/src/main/java/com/statusim/GethService.java deleted file mode 100644 index 84ec5daaa2..0000000000 --- a/android/app/src/main/java/com/statusim/GethService.java +++ /dev/null @@ -1,140 +0,0 @@ -package com.statusim; - -import android.app.Service; -import android.content.Intent; -import android.os.Handler; -import android.os.AsyncTask; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.Messenger; -import android.support.annotation.Nullable; -import android.util.Log; -import android.os.Environment; - -import java.lang.ref.WeakReference; - -import com.github.ethereum.go_ethereum.cmd.Geth; - -import java.io.File; - -public class GethService extends Service { - - private static final String TAG = "GethService"; - - private static boolean isGethStarted = false; - private static boolean isGethInitialized = false; - private final Handler handler = new Handler(); - - static class IncomingHandler extends Handler { - - private final WeakReference service; - - IncomingHandler(GethService service) { - - this.service = new WeakReference(service); - } - - @Override - public void handleMessage(Message message) { - - GethService service = this.service.get(); - if (service != null) { - if (!service.handleMessage(message)) { - super.handleMessage(message); - } - } - } - } - - final Messenger serviceMessenger = new Messenger(new IncomingHandler(this)); - - protected class StartTask extends AsyncTask { - - public StartTask() { - } - - protected Void doInBackground(Void... args) { - startGeth(); - return null; - } - - protected void onPostExecute(Void results) { - onGethStarted(); - } - } - - protected void onGethStarted() { - Log.d(TAG, "Geth Service started"); - isGethStarted = true; - } - - protected void startGeth() { - Log.d(TAG, "Starting background Geth Service"); - - File extStore = Environment.getExternalStorageDirectory(); - - final String dataFolder = extStore.exists() ? - extStore.getAbsolutePath() : - getApplicationInfo().dataDir; - - final Runnable addPeer = new Runnable() { - public void run() { - Log.w("Geth", "adding peer"); - Geth.run("--exec admin.addPeer(\"enode://e2f28126720452aa82f7d3083e49e6b3945502cb94d9750a15e27ee310eed6991618199f878e5fbc7dfa0e20f0af9554b41f491dc8f1dbae8f0f2d37a3a613aa@139.162.13.89:55555\") attach http://localhost:8545"); - } - }; - - new Thread(new Runnable() { - public void run() { - Geth.run("--shh --ipcdisable --nodiscover --rpc --rpcapi db,eth,net,web3,shh,admin --fast --datadir=" + dataFolder); - } - }).start(); - - handler.postDelayed(addPeer, 5000); - } - - public void signalEvent(String jsonEvent) { - - } - - @Nullable - @Override - public IBinder onBind(Intent intent) { - return serviceMessenger.getBinder(); - } - - @Override - public void onCreate() { - super.onCreate(); - System.loadLibrary("gethraw"); - System.loadLibrary("geth"); - - if (!isGethInitialized) { - isGethInitialized = true; - new StartTask().execute(); - } - } - - @Override - public void onDestroy() { - super.onDestroy(); - //TODO: stop geth - isGethStarted = false; - isGethInitialized = false; - Log.d(TAG, "Geth Service stopped !"); - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - return Service.START_STICKY; - } - - protected boolean handleMessage(Message message) { - return false; - } - - public static boolean isRunning() { - return isGethInitialized; - } -} diff --git a/android/app/src/main/java/com/statusim/MainActivity.java b/android/app/src/main/java/com/statusim/MainActivity.java index ee5ff65fd1..f5c2afe69d 100644 --- a/android/app/src/main/java/com/statusim/MainActivity.java +++ b/android/app/src/main/java/com/statusim/MainActivity.java @@ -1,98 +1,36 @@ package com.statusim; import com.facebook.react.ReactActivity; +import com.statusim.geth.module.GethPackage; import io.realm.react.RealmReactPackage; import com.oblador.vectoricons.VectorIconsPackage; import com.facebook.react.ReactPackage; import com.facebook.react.shell.MainReactPackage; import com.rt2zz.reactnativecontacts.ReactNativeContacts; import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Message; -import android.os.Messenger; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.DialogInterface.OnCancelListener; -import android.content.ComponentName; -import android.content.ServiceConnection; import android.content.Intent; -import android.content.Context; import com.bitgo.randombytes.RandomBytesPackage; import com.BV.LinearGradient.LinearGradientPackage; import com.centaurwarchief.smslistener.SmsListener; import com.github.yamill.orientation.OrientationPackage; - -import android.util.Log; - import java.util.Arrays; import java.util.List; import java.util.Properties; -import java.io.File; import com.lwansbrough.RCTCamera.*; import com.i18n.reactnativei18n.ReactNativeI18n; -import io.realm.react.RealmReactPackage; -import android.content.Intent; import android.content.res.Configuration; public class MainActivity extends ReactActivity { private static final String TAG = "MainActivity"; - /** - * Incoming message handler. Calls to its binder are sequential! - */ - protected final IncomingHandler handler = new IncomingHandler(); - - /** Flag indicating if the service is bound. */ - protected boolean isBound; - - /** Sends messages to the service. */ - protected Messenger serviceMessenger = null; - - /** Receives messages from the service. */ - protected Messenger clientMessenger = new Messenger(handler); - - class IncomingHandler extends Handler { - - @Override - public void handleMessage(Message message) { - boolean isClaimed = false; - Log.d(TAG, "!!!!!!!!!!!!!! Received Service Message !!!!!!!!!!!!!!"); - super.handleMessage(message); - } - } - - protected ServiceConnection serviceConnection = new ServiceConnection() { - - public void onServiceConnected(ComponentName className, IBinder service) { - // This is called when the connection with the service has been - // established, giving us the object we can use to - // interact with the service. We are communicating with the - // service using a Messenger, so here we get a client-side - // representation of that from the raw IBinder object. - serviceMessenger = new Messenger(service); - isBound = true; - onConnected(); - } - - public void onServiceDisconnected(ComponentName className) { - // This is called when the connection with the service has been - // unexpectedly disconnected -- that is, its process crashed. - serviceMessenger = null; - isBound = false; - Log.d(TAG, "!!!!!!!!!!!!!! Geth Service Disconnected !!!!!!!!!!!!!!"); - } - }; - - protected void onConnected() { - Log.d(TAG, "!!!!!!!!!!!!!! Geth Service Connected !!!!!!!!!!!!!!"); - } - protected void startStatus() { // Required because of crazy APN settings redirecting localhost (found in GB) Properties properties = System.getProperties(); @@ -129,25 +67,11 @@ public class MainActivity extends ReactActivity { }).create(); dialog.show(); } - Intent intent = new Intent(this, GethService.class); - if (!GethService.isRunning()) { - startService(intent); - } - if (serviceConnection != null && GethService.isRunning()) { - bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); - } - } @Override protected void onDestroy() { super.onDestroy(); - try { - unbindService(serviceConnection); - } - catch (Throwable t) { - Log.e(TAG, "Failed to unbind from the geth service", t); - } } /** @@ -184,7 +108,8 @@ public class MainActivity extends ReactActivity { new LinearGradientPackage(), new RCTCameraPackage(), new SmsListener(this), - new OrientationPackage(this) + new OrientationPackage(this), + new GethPackage() ); } diff --git a/android/app/src/main/java/com/statusim/geth/module/GethModule.java b/android/app/src/main/java/com/statusim/geth/module/GethModule.java new file mode 100644 index 0000000000..a4bb3e12d0 --- /dev/null +++ b/android/app/src/main/java/com/statusim/geth/module/GethModule.java @@ -0,0 +1,209 @@ +package com.statusim.geth.module; + +import android.app.Activity; +import android.os.Bundle; +import android.os.Message; +import com.facebook.react.bridge.*; +import com.statusim.geth.service.ConnectorHandler; +import com.statusim.geth.service.GethConnector; +import com.statusim.geth.service.GethMessages; +import com.statusim.geth.service.GethService; + +import java.util.HashMap; +import java.util.UUID; + +public class GethModule extends ReactContextBaseJavaModule implements LifecycleEventListener, ConnectorHandler { + + protected GethConnector geth = null; + protected String handlerIdentifier = createIdentifier(); + + protected HashMap startNodeCallbacks = new HashMap<>(); + protected HashMap createAccountCallbacks = new HashMap<>(); + protected HashMap addAccountCallbacks = new HashMap<>(); + protected HashMap unlockAccountCallbacks = new HashMap<>(); + + + public GethModule(ReactApplicationContext reactContext) { + super(reactContext); + reactContext.addLifecycleEventListener(this); + } + + @Override + public String getName() { + return "Geth"; + } + + @Override + public void onHostResume() { // Actvity `onResume` + + Activity currentActivity = getCurrentActivity(); + + if (currentActivity == null) { + return; + } + if (geth == null) { + geth = new GethConnector(currentActivity, GethService.class); + geth.registerHandler(this); + } + geth.bindService(); + } + + @Override + public void onHostPause() { // Actvity `onPause` + + if (geth != null) { + geth.unbindService(); + } + } + + @Override + public void onHostDestroy() { // Actvity `onDestroy` + + if (geth != null) { + geth.stopNode(null); + } + } + + @Override + public String getID() { + + return handlerIdentifier; + } + + @Override + public void onConnectorConnected() { + } + + @Override + public void onConnectorDisconnected() { + } + + @Override + public boolean handleMessage(Message message) { + + boolean isClaimed = true; + Bundle data = message.getData(); + String callbackIdentifier = data.getString(GethConnector.CALLBACK_IDENTIFIER); + Callback callback = null; + switch (message.what) { + case GethMessages.MSG_NODE_STARTED: + callback = startNodeCallbacks.remove(callbackIdentifier); + if (callback != null) { + callback.invoke(null); + } + break; + case GethMessages.MSG_NODE_STOPPED: + break; + case GethMessages.MSG_ACCOUNT_CREATED: + callback = createAccountCallbacks.remove(callbackIdentifier); + if (callback != null) { + callback.invoke(null, "{ \"address\": \"" + data.getString("address") + "\"}"); + } + break; + case GethMessages.MSG_ACCOUNT_ADDED: + callback = addAccountCallbacks.remove(callbackIdentifier); + if (callback != null) { + callback.invoke(null, "{ \"address\": \"" + data.getString("address") + "\"}"); + } + break; + case GethMessages.MSG_ACCOUNT_UNLOCKED: + callback = unlockAccountCallbacks.remove(callbackIdentifier); + if (callback != null) { + callback.invoke(null, "{ \"result\": \"" + data.getString("result") + "\"}"); + } + break; + default: + isClaimed = false; + } + + return isClaimed; + } + + @ReactMethod + public void startNode(Callback callback) { + + Activity currentActivity = getCurrentActivity(); + + if (currentActivity == null) { + callback.invoke("Activity doesn't exist"); + return; + } + + if (geth == null) { + callback.invoke("Geth connector is null"); + return; + } + + String callbackIdentifier = createIdentifier(); + startNodeCallbacks.put(callbackIdentifier, callback); + + geth.startNode(callbackIdentifier); + } + + @ReactMethod + public void unlockAccount(String address, String password, Callback callback) { + + Activity currentActivity = getCurrentActivity(); + + if (currentActivity == null) { + callback.invoke("Activity doesn't exist"); + return; + } + + if (geth == null) { + callback.invoke("Geth connector is null"); + return; + } + + String callbackIdentifier = createIdentifier(); + unlockAccountCallbacks.put(callbackIdentifier, callback); + + geth.unlockAccount(callbackIdentifier, address, password); + } + + @ReactMethod + public void createAccount(Callback callback) { + + Activity currentActivity = getCurrentActivity(); + + if (currentActivity == null) { + callback.invoke("Activity doesn't exist"); + return; + } + + if (geth == null) { + callback.invoke("Geth connector is null"); + return; + } + + String callbackIdentifier = createIdentifier(); + createAccountCallbacks.put(callbackIdentifier, callback); + + geth.createAccount(callbackIdentifier); + } + + @ReactMethod + public void addAccount(String privateKey, Callback callback) { + + Activity currentActivity = getCurrentActivity(); + + if (currentActivity == null) { + callback.invoke("Activity doesn't exist"); + return; + } + + if (geth == null) { + callback.invoke("Geth connector is null"); + return; + } + + String callbackIdentifier = createIdentifier(); + addAccountCallbacks.put(callbackIdentifier, callback); + geth.addAccount(callbackIdentifier, privateKey); + } + + protected String createIdentifier() { + return UUID.randomUUID().toString(); + } + +} \ No newline at end of file diff --git a/android/app/src/main/java/com/statusim/geth/module/GethPackage.java b/android/app/src/main/java/com/statusim/geth/module/GethPackage.java new file mode 100644 index 0000000000..d7dc02df6f --- /dev/null +++ b/android/app/src/main/java/com/statusim/geth/module/GethPackage.java @@ -0,0 +1,33 @@ +package com.statusim.geth.module; + +import com.facebook.react.ReactPackage; +import com.facebook.react.bridge.JavaScriptModule; +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.uimanager.ViewManager; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class GethPackage implements ReactPackage { + + @Override + public List createNativeModules(ReactApplicationContext reactContext) { + List modules = new ArrayList<>(); + + modules.add(new GethModule(reactContext)); + + return modules; + } + + @Override + public List> createJSModules() { + return Collections.emptyList(); + } + + @Override + public List createViewManagers(ReactApplicationContext reactContext) { + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/statusim/geth/service/ConnectorHandler.java b/android/app/src/main/java/com/statusim/geth/service/ConnectorHandler.java new file mode 100644 index 0000000000..0df2737a97 --- /dev/null +++ b/android/app/src/main/java/com/statusim/geth/service/ConnectorHandler.java @@ -0,0 +1,12 @@ +package com.statusim.geth.service; + + +import android.os.Message; + +public interface ConnectorHandler { + + boolean handleMessage(Message message); + void onConnectorConnected(); + void onConnectorDisconnected(); + String getID(); +} \ No newline at end of file diff --git a/android/app/src/main/java/com/statusim/geth/service/GethConnector.java b/android/app/src/main/java/com/statusim/geth/service/GethConnector.java new file mode 100644 index 0000000000..73a1fd74d4 --- /dev/null +++ b/android/app/src/main/java/com/statusim/geth/service/GethConnector.java @@ -0,0 +1,107 @@ +package com.statusim.geth.service; + + +import android.content.Context; +import android.os.Bundle; +import android.os.Message; +import android.os.RemoteException; +import android.util.Log; + +public class GethConnector extends ServiceConnector { + + private static final String TAG = "GethConnector"; + + public static final String CALLBACK_IDENTIFIER = "callbackIdentifier"; + + public GethConnector(Context context, Class serviceClass) { + + super(context, serviceClass); + } + + public void startNode(String callbackIdentifier) { + + if (checkBound()) { + Message msg = createMessage(callbackIdentifier, GethMessages.MSG_START_NODE, null); + try { + serviceMessenger.send(msg); + } catch (RemoteException e) { + Log.e(TAG, "Exception sending message(startNode) to service: ", e); + } + } + } + + public void stopNode(String callbackIdentifier) { + + if (checkBound()) { + Message msg = createMessage(callbackIdentifier, GethMessages.MSG_STOP_NODE, null); + try { + serviceMessenger.send(msg); + } catch (RemoteException e) { + Log.e(TAG, "Exception sending message(stopNode) to service: ", e); + } + } + } + + public void unlockAccount(String callbackIdentifier, String address, String password) { + + if (checkBound()) { + Bundle data = new Bundle(); + data.putString("address", address); + data.putString("password", password); + Message msg = createMessage(callbackIdentifier, GethMessages.MSG_UNLOCK_ACCOUNT, data); + try { + serviceMessenger.send(msg); + } catch (RemoteException e) { + Log.e(TAG, "Exception sending message(unlockAccount) to service: ", e); + } + } + } + + public void createAccount(String callbackIdentifier) { + + if (checkBound()) { + Message msg = createMessage(callbackIdentifier, GethMessages.MSG_CREATE_ACCOUNT, null); + try { + serviceMessenger.send(msg); + } catch (RemoteException e) { + Log.e(TAG, "Exception sending message(createAccount) to service: ", e); + } + } + } + + public void addAccount(String callbackIdentifier, String privateKey) { + + if (checkBound()) { + Bundle data = new Bundle(); + data.putString("privateKey", privateKey); + Message msg = createMessage(callbackIdentifier, GethMessages.MSG_ADD_ACCOUNT, data); + try { + serviceMessenger.send(msg); + } catch (RemoteException e) { + Log.e(TAG, "Exception sending message(addAccount) to service: ", e); + } + } + } + + + protected boolean checkBound() { + + if (!isBound) { + Log.d(TAG, "GethConnector not bound!"); + return false; + } + return true; + } + + protected Message createMessage(String callbackIdentifier, int idMessage, Bundle data) { + + Message msg = Message.obtain(null, idMessage, 0, 0); + msg.replyTo = clientMessenger; + if (data == null) { + data = new Bundle(); + } + data.putString(CALLBACK_IDENTIFIER, callbackIdentifier); + msg.setData(data); + return msg; + } +} diff --git a/android/app/src/main/java/com/statusim/geth/service/GethMessages.java b/android/app/src/main/java/com/statusim/geth/service/GethMessages.java new file mode 100644 index 0000000000..080725bca2 --- /dev/null +++ b/android/app/src/main/java/com/statusim/geth/service/GethMessages.java @@ -0,0 +1,56 @@ +package com.statusim.geth.service; + + +public class GethMessages { + + /** + * Start the node + */ + public static final int MSG_START_NODE = 1; + + /** + * Node started event + */ + public static final int MSG_NODE_STARTED = 2; + + /** + * Stop the node + */ + public static final int MSG_STOP_NODE = 3; + + /** + * Node stopped event + */ + public static final int MSG_NODE_STOPPED = 4; + + /** + * Unlock an account + */ + public static final int MSG_UNLOCK_ACCOUNT = 5; + + /** + * Account unlocked event + */ + public static final int MSG_ACCOUNT_UNLOCKED = 6; + + /** + * Create an account + */ + public static final int MSG_CREATE_ACCOUNT = 7; + + /** + * Account created event + */ + public static final int MSG_ACCOUNT_CREATED = 8; + + /** + * Add an account + */ + public static final int MSG_ADD_ACCOUNT = 9; + + /** + * Account added event + */ + public static final int MSG_ACCOUNT_ADDED = 10; + +} diff --git a/android/app/src/main/java/com/statusim/geth/service/GethService.java b/android/app/src/main/java/com/statusim/geth/service/GethService.java new file mode 100644 index 0000000000..638c4c3657 --- /dev/null +++ b/android/app/src/main/java/com/statusim/geth/service/GethService.java @@ -0,0 +1,227 @@ +package com.statusim.geth.service; + +import android.app.Service; +import android.content.Intent; +import android.os.*; +import android.support.annotation.Nullable; +import android.util.Log; + +import java.lang.ref.WeakReference; + +import com.github.ethereum.go_ethereum.Statusgo; + +import java.io.File; + +public class GethService extends Service { + + private static final String TAG = "GethService"; + + private static boolean isGethStarted = false; + private static boolean isGethInitialized = false; + private final Handler handler = new Handler(); + + static class IncomingHandler extends Handler { + + private final WeakReference service; + + IncomingHandler(GethService service) { + + this.service = new WeakReference(service); + } + + @Override + public void handleMessage(Message message) { + + GethService service = this.service.get(); + if (service != null) { + if (!service.handleMessage(message)) { + super.handleMessage(message); + } + } + } + } + + final Messenger serviceMessenger = new Messenger(new IncomingHandler(this)); + + + public static void signalEvent(String jsonEvent) { + System.out.println("\n\n\nIT WOOOOOORKS1111!!!!!!\n\n\n"); + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return serviceMessenger.getBinder(); + } + + @Override + public void onCreate() { + super.onCreate(); + System.loadLibrary("statusgoraw"); + System.loadLibrary("statusgo"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + //TODO: stop geth + stopNode(null); + isGethStarted = false; + isGethInitialized = false; + Log.d(TAG, "Geth Service stopped !"); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + return Service.START_STICKY; + } + + protected boolean handleMessage(Message message) { + switch (message.what) { + + case GethMessages.MSG_START_NODE: + startNode(message); + break; + + case GethMessages.MSG_STOP_NODE: + stopNode(message); + break; + + case GethMessages.MSG_CREATE_ACCOUNT: + createAccount(message); + break; + + case GethMessages.MSG_ADD_ACCOUNT: + addAccount(message); + break; + + case GethMessages.MSG_UNLOCK_ACCOUNT: + unlockAccount(message); + break; + + default: + return false; + } + + return true; + } + + protected void startNode(Message message) { + if (!isGethInitialized) { + isGethInitialized = true; + new StartTask(message).execute(); + } + } + + protected class StartTask extends AsyncTask { + + protected Message message; + + public StartTask(Message message) { + this.message = message; + } + + protected Void doInBackground(Void... args) { + startGeth(); + return null; + } + + protected void onPostExecute(Void results) { + onGethStarted(message); + } + } + + protected void onGethStarted(Message message) { + Log.d(TAG, "Geth Service started"); + isGethStarted = true; + + sendReply(message, GethMessages.MSG_NODE_STARTED, null); + } + + protected void startGeth() { + Log.d(TAG, "Starting background Geth Service"); + + File extStore = Environment.getExternalStorageDirectory(); + + final String dataFolder = extStore.exists() ? + extStore.getAbsolutePath() : + getApplicationInfo().dataDir; + + new Thread(new Runnable() { + public void run() { + + Statusgo.doStartNode(dataFolder); + } + }).start(); + } + + protected void stopNode(Message message) { + // TODO: stop node + + sendReply(message, GethMessages.MSG_NODE_STOPPED, null); + } + + protected void createAccount(Message message) { + Bundle data = message.getData(); + String password = data.getString("password"); + // TODO: remove second argument + String address = Statusgo.doCreateAccount(password, ""); + Log.d(TAG, "Created account: " + address); + + Bundle replyData = new Bundle(); + replyData.putString("address", address); + sendReply(message, GethMessages.MSG_ACCOUNT_CREATED, replyData); + } + + protected void addAccount(Message message) { + Bundle data = message.getData(); + String privateKey = data.getString("privateKey"); + String password = data.getString("password"); + // TODO: add account + //String address = Statusgo.doAddAccount(privateKey, password); + String address = "added account address"; + Log.d(TAG, "Added account: " + address); + + Bundle replyData = new Bundle(); + replyData.putString("address", address); + sendReply(message, GethMessages.MSG_ACCOUNT_ADDED, replyData); + } + + protected void unlockAccount(Message message) { + Bundle data = message.getData(); + String address = data.getString("address"); + String password = data.getString("password"); + // TODO: remove third argument + String result = Statusgo.doUnlockAccount(address, password, 0); + Log.d(TAG, "Unlocked account: " + result); + + Bundle replyData = new Bundle(); + replyData.putString("result", result); + sendReply(message, GethMessages.MSG_ACCOUNT_UNLOCKED, replyData); + } + + public static boolean isRunning() { + return isGethInitialized; + } + + protected void sendReply(Message message, int replyIdMessage, Bundle replyData) { + + if (message == null) { + return; + } + Message replyMessage = Message.obtain(null, replyIdMessage, 0, 0, message.obj); + if (replyData == null) { + replyData = new Bundle(); + } + Bundle data = message.getData(); + String callbackIdentifier = data.getString("callbackIdentifier"); + replyData.putString("callbackIdentifier", callbackIdentifier); + replyMessage.setData(replyData); + + try { + message.replyTo.send(replyMessage); + } catch (RemoteException e) { + Log.e(TAG, "Exception sending message id: " + replyIdMessage, e); + } + } +} diff --git a/android/app/src/main/java/com/statusim/geth/service/ServiceConnector.java b/android/app/src/main/java/com/statusim/geth/service/ServiceConnector.java new file mode 100644 index 0000000000..1c70e92992 --- /dev/null +++ b/android/app/src/main/java/com/statusim/geth/service/ServiceConnector.java @@ -0,0 +1,149 @@ +package com.statusim.geth.service; + + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.*; + +import java.util.ArrayList; + +public class ServiceConnector { + + private static final String TAG = "ServiceConnector"; + + /** + * Incoming message handler. Calls to its binder are sequential! + */ + protected final IncomingHandler handler; + + /** + * Handler thread to avoid running on the main UI thread + */ + protected final HandlerThread handlerThread; + + /** Context of the activity from which this connector was launched */ + protected Context context; + + /** The class of the service we want to connect to */ + protected Class serviceClass; + + /** Flag indicating if the service is bound. */ + protected boolean isBound; + + /** Sends messages to the service. */ + protected Messenger serviceMessenger = null; + + /** Receives messages from the service. */ + protected Messenger clientMessenger = null; + + protected ArrayList handlers = new ArrayList<>(); + + /** Handles incoming messages from service. */ + class IncomingHandler extends Handler { + + public IncomingHandler(HandlerThread thread) { + + super(thread.getLooper()); + } + + @Override + public void handleMessage(Message message) { + + boolean isClaimed = false; + if (message.obj != null) { + String identifier = ((Bundle) message.obj).getString("identifier"); + if (identifier != null) { + + for (ConnectorHandler handler : handlers) { + if (identifier.equals(handler.getID())) { + isClaimed = handler.handleMessage(message); + } + } + } + } + if (!isClaimed) { + super.handleMessage(message); + } + } + } + + /** + * Class for interacting with the main interface of the service. + */ + protected ServiceConnection serviceConnection = new ServiceConnection() { + + public void onServiceConnected(ComponentName className, IBinder service) { + + // This is called when the connection with the service has been + // established, giving us the object we can use to + // interact with the service. We are communicating with the + // service using a Messenger, so here we get a client-side + // representation of that from the raw IBinder object. + serviceMessenger = new Messenger(service); + isBound = true; + for (ConnectorHandler handler: handlers) { + handler.onConnectorConnected(); + } + } + + public void onServiceDisconnected(ComponentName className) { + + // This is called when the connection with the service has been + // unexpectedly disconnected -- that is, its process crashed. + serviceMessenger = null; + isBound = false; + for (ConnectorHandler handler: handlers) { + handler.onConnectorDisconnected(); + } + } + }; + + public ServiceConnector(Context context, Class serviceClass) { + + this.context = context; + this.serviceClass = serviceClass; + handlerThread = new HandlerThread("HandlerThread"); + handlerThread.start(); + handler = new IncomingHandler(handlerThread); + clientMessenger = new Messenger(handler); + } + + /** Bind to the service */ + public boolean bindService() { + + if (serviceConnection != null) { + Intent intent = new Intent(context, serviceClass); + context.getApplicationContext().startService(intent); + return context.getApplicationContext().bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); + } else { + return false; + } + } + + /** Unbind from the service */ + public void unbindService() { + + if (isBound && serviceConnection != null) { + context.getApplicationContext().unbindService(serviceConnection); + isBound = false; +/* + Intent intent = new Intent(context, serviceClass); + context.getApplicationContext().stopService(intent); +*/ + } + } + + public void registerHandler(ConnectorHandler handler) { + + if (!handlers.contains(handler)) { + handlers.add(handler); + } + } + + public void removeHandler(ConnectorHandler handler) { + + handlers.remove(handler); + } +} diff --git a/src/status_im/components/react.cljs b/src/status_im/components/react.cljs index f931ce4e00..f180f463c2 100644 --- a/src/status_im/components/react.cljs +++ b/src/status_im/components/react.cljs @@ -4,6 +4,7 @@ [status-im.utils.utils :as u])) (def react (u/require "react-native")) +(def native-modules (.-NativeModules react)) (defn get-react-property [name] (aget react name)) @@ -78,3 +79,5 @@ (def dismiss-keyboard! (u/require "dismissKeyboard")) (def device-event-emitter (.-DeviceEventEmitter react)) (def orientation (u/require "react-native-orientation")) + +(def geth (.-Geth native-modules))