diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java b/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java
index 32e6918..f51a448 100644
--- a/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java
+++ b/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java
@@ -1,13 +1,7 @@
package cy.agorise.graphenej.api.android;
-import android.app.Service;
-import android.content.Intent;
-import android.os.Binder;
-import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Looper;
-import android.util.Log;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@@ -62,10 +56,8 @@ import cy.agorise.graphenej.network.NodeProvider;
import cy.agorise.graphenej.operations.CustomOperation;
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
import cy.agorise.graphenej.operations.TransferOperation;
-import cy.agorise.graphenej.stats.ExponentialMovingAverage;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.annotations.Nullable;
import io.reactivex.disposables.Disposable;
import io.reactivex.subjects.PublishSubject;
import okhttp3.OkHttpClient;
@@ -75,12 +67,10 @@ import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
/**
- * Service in charge of maintaining a connection to the full node.
+ * Class in charge of maintaining a connection to the full node.
*/
-public class NetworkService extends Service {
- private final String TAG = this.getClass().getName();
-
+public class NetworkService {
public static final int NORMAL_CLOSURE_STATUS = 1000;
private static final int GOING_AWAY_STATUS = 1001;
@@ -92,72 +82,6 @@ public class NetworkService extends Service {
// which we can choose from.
private final int DEFAULT_INITIAL_DELAY = 500;
- /**
- * Constant to be used as a key in order to pass the user name information, in case the
- * provided API nodes might require this information.
- */
- public static final String KEY_USERNAME = "key_username";
-
- /**
- * Constant to be used as a key in order to pass the password information, in case the
- * provided API nodes might require this information.
- *
- * This information should be passed as an intent extra when calling the bindService
- * or startService methods.
- */
- public static final String KEY_PASSWORD = "key_password";
-
- /**
- * Constant used as a key in order to specify which APIs the application will be requiring.
- *
- * This information should be passed as an intent extra when calling the bindService
- * or startService methods.
- */
- public static final String KEY_REQUESTED_APIS = "key_requested_apis";
-
- /**
- * Constant used as a key in order to let the NetworkService know whether or not it should
- * start a recurring node latency verification task.
- *
- * This information should be passed as an intent extra when calling the bindService
- * or startService methods.
- */
- public static final String KEY_ENABLE_LATENCY_VERIFIER = "key_enable_latency_verifier";
-
- /**
- * Constant used as a key in order to specify the alpha (or smoothing) factor to be used in
- * the exponential moving average calculated from the different latency samples. This only
- * makes sense if the latency verification feature is enabled of course.
- *
- * This information should be passed as an intent extra when calling the bindService
- * or startService methods.
- */
- public static final String KEY_NODE_LATENCY_SMOOTHING_FACTOR = "key_node_latency_smoothing_factor";
-
- /**
- * Key used to pass via intent a boolean extra to specify whether the connection should
- * be automatically established.
- *
- * This information should be passed as an intent extra when calling the bindService
- * or startService methods.
- */
- public static final String KEY_AUTO_CONNECT = "key_auto_connect";
-
- /**
- * Key used to pass via intent a list of node URLs. The value passed should be a String
- * containing a simple comma separated list of URLs.
- *
- * For example:
- *
- * wss://domain1.com/ws,wss://domain2.com/ws,wss://domain3.com/ws
- *
- * This information should be passed as an intent extra when calling the bindService
- * or startService methods.
- */
- public static final String KEY_NODE_URLS = "key_node_urls";
-
- private final IBinder mBinder = new LocalBinder();
-
private WebSocket mWebSocket;
// Username and password used to connect to a specific node
@@ -170,7 +94,7 @@ public class NetworkService extends Service {
private long mCurrentId = 0;
// Requested APIs passed to this service
- private int mRequestedApis;
+ private int mRequestedApis = ApiAccess.API_DATABASE | ApiAccess.API_HISTORY | ApiAccess.API_NETWORK_BROADCAST;
// Variable used to keep track of the currently obtained API accesses
private HashMap mApiIds = new HashMap();
@@ -216,6 +140,31 @@ public class NetworkService extends Service {
// suited for every response type.
private DeserializationMap mDeserializationMap = new DeserializationMap();
+ /**
+ * Singleton reference
+ */
+ private static NetworkService instance;
+
+ /**
+ * Private constructor
+ */
+ private NetworkService(){}
+
+ /**
+ * Thread-safe singleton getter.
+ * @return A NetworkService instance.
+ */
+ public static NetworkService getInstance(){
+ if(instance == null) {
+ synchronized (NetworkService.class) {
+ if(instance == null) {
+ instance = new NetworkService();
+ }
+ }
+ }
+ return instance;
+ }
+
/**
* Actually establishes a connection from this Service to one of the full nodes.
*/
@@ -230,11 +179,11 @@ public class NetworkService extends Service {
synchronized (mWebSocketListener){
mSelectedNode = nodeProvider.getBestNode();
if(mSelectedNode != null){
- Log.d(TAG,"Trying to connect to: "+ mSelectedNode.getUrl());
+ System.out.println("Trying to connect to: "+ mSelectedNode.getUrl());
Request request = new Request.Builder().url(mSelectedNode.getUrl()).build();
mWebSocket = client.newWebSocket(request, mWebSocketListener);
}else{
- Log.d(TAG,"Could not find best node, reescheduling");
+ System.out.println("Could not find best node, reescheduling");
// If no node could be found yet, schedule a new attempt in DEFAULT_INITIAL_DELAY ms
mHandler.postDelayed(mConnectAttempt, DEFAULT_INITIAL_DELAY);
}
@@ -244,7 +193,7 @@ public class NetworkService extends Service {
public long sendMessage(String message){
if(mWebSocket != null){
if(mWebSocket.send(message)){
- Log.v(TAG,"-> " + message);
+ System.out.println("-> " + message);
return mCurrentId;
}
}else{
@@ -270,7 +219,7 @@ public class NetworkService extends Service {
ApiCall call = apiCallable.toApiCall(apiId, ++mCurrentId);
mRequestClassMap.put(mCurrentId, apiCallable.getClass());
if(mWebSocket != null && mWebSocket.send(call.toJsonString())){
- Log.v(TAG,"-> "+call.toJsonString());
+ System.out.println("-> "+call.toJsonString());
return mCurrentId;
}
}
@@ -285,8 +234,10 @@ public class NetworkService extends Service {
return mWebSocket != null && isLoggedIn;
}
- @Override
- public void onDestroy() {
+ /**
+ * Stops the service by closing the connection and stopping the latency verifier.
+ */
+ public void stop() {
if(mWebSocket != null)
mWebSocket.close(NORMAL_CLOSURE_STATUS, null);
@@ -294,63 +245,32 @@ public class NetworkService extends Service {
nodeLatencyVerifier.stop();
}
- @Nullable
- @Override
- public IBinder onBind(Intent intent) {
- return mBinder;
- }
-
/**
- * Initialize information and try to connect to a node accordingly. This methods were moved
- * from onBind to avoid crashes due to components other than {@link NetworkServiceManager}
- * binding to the service without submitting the proper information.
- *
- * @param extras Bundle that contains all required information for a proper initialization
+ * Starts the connection
*/
- public void bootstrapService(Bundle extras) {
+ public void start(String[] urls, double alpha) {
// Retrieving credentials and requested API data from the shared preferences
- mUsername = extras.getString(NetworkService.KEY_USERNAME, "");
- mPassword = extras.getString(NetworkService.KEY_PASSWORD, "");
- mRequestedApis = extras.getInt(NetworkService.KEY_REQUESTED_APIS, 0);
- boolean mAutoConnect = extras.getBoolean(NetworkService.KEY_AUTO_CONNECT, true);
- boolean verifyNodeLatency = extras.getBoolean(NetworkService.KEY_ENABLE_LATENCY_VERIFIER, false);
+ mUsername = "";
+ mPassword = "";
- // If the user of the library desires, a custom list of node URLs can
- // be passed using the KEY_NODE_URLS constant
- String nodeURLStr = extras.getString(NetworkService.KEY_NODE_URLS, "");
- if(nodeURLStr.equals("")){
- throw new MissingResourceException("A comma-separated list of node URLs must be provided as an intent extra", String.class.getName(), NetworkService.KEY_NODE_URLS);
+ if(urls == null || urls.length == 0){
+ throw new MissingResourceException("Expecting at least a node URL to be provided", String.class.getName(), "urls");
}
- // Adding user-provided list of node URLs
- String[] urls = nodeURLStr.split(",");
-
// Feeding all node information to the NodeProvider instance
for(String nodeUrl : urls){
nodeProvider.addNode(new FullNode(nodeUrl));
}
- if (!mAutoConnect && !verifyNodeLatency) {
- throw new IllegalArgumentException("NetworkService$bootstrapService: verifyNodeLatency cannot be false when autoConnect is false too.");
+ ArrayList fullNodes = new ArrayList<>();
+ for(String url : urls){
+ fullNodes.add(new FullNode(url, alpha));
}
+ nodeLatencyVerifier = new NodeLatencyVerifier(fullNodes);
+ fullNodePublishSubject = nodeLatencyVerifier.start();
+ fullNodePublishSubject.observeOn(AndroidSchedulers.mainThread()).subscribe(nodeLatencyObserver);
- if (verifyNodeLatency) {
- double alpha = extras.getDouble(KEY_NODE_LATENCY_SMOOTHING_FACTOR, ExponentialMovingAverage.DEFAULT_ALPHA);
- ArrayList fullNodes = new ArrayList<>();
- for(String url : urls){
- fullNodes.add(new FullNode(url, alpha));
- }
- nodeLatencyVerifier = new NodeLatencyVerifier(fullNodes);
- fullNodePublishSubject = nodeLatencyVerifier.start();
- fullNodePublishSubject.observeOn(AndroidSchedulers.mainThread()).subscribe(nodeLatencyObserver);
- }
-
- if (mAutoConnect)
- connect();
- else
- mHandler.postDelayed(mConnectAttempt, DEFAULT_INITIAL_DELAY);
-
- // TODO make sure (verifyNodeLatency==false && mAutoConnect==true) is a valid/useful combination, else simplify and use only one of those arguments
+ mHandler.postDelayed(mConnectAttempt, DEFAULT_INITIAL_DELAY);
}
/**
@@ -374,7 +294,7 @@ public class NetworkService extends Service {
public void run() {
FullNode fullNode = nodeProvider.getBestNode();
if(fullNode != null){
- Log.i(TAG, String.format("Connected with %d latency results", latencyUpdateCounter));
+ System.out.println( String.format("Connected with %d latency results", latencyUpdateCounter));
connect();
}else{
mHandler.postDelayed(this, DEFAULT_INITIAL_DELAY);
@@ -398,24 +318,13 @@ public class NetworkService extends Service {
@Override
public void onError(Throwable e) {
- Log.e(TAG,"nodeLatencyObserver.onError.Msg: "+e.getMessage());
+ System.out.println("nodeLatencyObserver.onError.Msg: "+e.getMessage());
}
@Override
public void onComplete() { }
};
- /**
- * Class used for the client Binder. Because we know this service always
- * runs in the same process as its clients, we don't need to deal with IPC.
- */
- public class LocalBinder extends Binder {
- public NetworkService getService() {
- // Return this instance of LocalService so clients can call public methods
- return NetworkService.this;
- }
- }
-
private WebSocketListener mWebSocketListener = new WebSocketListener() {
@Override
@@ -446,7 +355,7 @@ public class NetworkService extends Service {
@Override
public synchronized void onMessage(WebSocket webSocket, String text) {
super.onMessage(webSocket, text);
- Log.v(TAG,"<- "+text);
+ System.out.println("<- "+text);
JsonRpcNotification notification = gson.fromJson(text, JsonRpcNotification.class);
if(notification.method != null){
@@ -514,7 +423,7 @@ public class NetworkService extends Service {
}
if(response.error != null && response.error.message != null){
// We could not make sense of this incoming message, just log a warning
- Log.w(TAG,"Error.Msg: "+response.error.message);
+ System.out.println("Error.Msg: "+response.error.message);
}
// Properly de-serialize all other fields and broadcasts to the event bus
handleJsonRpcResponse(response, text);
@@ -597,10 +506,10 @@ public class NetworkService extends Service {
Type GetAssetsResponse = new TypeToken>>(){}.getType();
parsedResponse = gson.fromJson(text, GetAssetsResponse);
}else {
- Log.w(TAG,"Unknown request class");
+ System.out.println("Unknown request class");
}
}else{
- Log.w(TAG,"Unhandled situation");
+ System.out.println("Unhandled situation");
}
}
@@ -680,15 +589,15 @@ public class NetworkService extends Service {
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
super.onFailure(webSocket, t, response);
- Log.e(TAG,"onFailure. Exception: "+t.getClass().getName()+", Msg: "+t.getMessage());
+ System.out.println("onFailure. Exception: "+t.getClass().getName()+", Msg: "+t.getMessage());
// Logging error stack trace
for(StackTraceElement element : t.getStackTrace()){
- Log.v(TAG,String.format("%s#%s:%s", element.getClassName(), element.getMethodName(), element.getLineNumber()));
+ System.out.println(String.format("%s#%s:%s", element.getClassName(), element.getMethodName(), element.getLineNumber()));
}
// If there is a response, we print it
if(response != null){
- Log.e(TAG,"Response: "+response.message());
+ System.out.println("Response: "+response.message());
}
handleWebSocketDisconnection(true, true);
@@ -702,7 +611,7 @@ public class NetworkService extends Service {
* @param penalizeNode Whether or not to penalize the current node with a very high latency reading.
*/
private synchronized void handleWebSocketDisconnection(boolean tryReconnection, boolean penalizeNode) {
- Log.d(TAG,"handleWebSocketDisconnection. try reconnection: " + tryReconnection + ", penalizeNode: " + penalizeNode);
+ System.out.println("handleWebSocketDisconnection. try reconnection: " + tryReconnection + ", penalizeNode: " + penalizeNode);
RxBus.getBusInstance().send(new ConnectionStatusUpdate(ConnectionStatusUpdate.DISCONNECTED, ApiAccess.API_NONE));
isLoggedIn = false;
@@ -733,8 +642,7 @@ public class NetworkService extends Service {
RxBus.getBusInstance().send(new ConnectionStatusUpdate(ConnectionStatusUpdate.DISCONNECTED, ApiAccess.API_NONE));
if (nodeProvider.getBestNode() == null) {
- Log.e(TAG, "Giving up on connections");
- stopSelf();
+ System.out.println( "Giving up on connections");
} else {
mHandler.postDelayed(new Runnable() {
@Override
diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkServiceManager.java b/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkServiceManager.java
deleted file mode 100644
index f3b65be..0000000
--- a/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkServiceManager.java
+++ /dev/null
@@ -1,325 +0,0 @@
-package cy.agorise.graphenej.api.android;
-
-import android.app.Activity;
-import android.app.Application;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import cy.agorise.graphenej.stats.ExponentialMovingAverage;
-
-/**
- * This class should be instantiated at the application level of the android app.
- *
- * It will monitor the interaction between the different activities of an app and help us decide
- * when the connection to the full node should be interrupted.
- */
-
-public class NetworkServiceManager implements Application.ActivityLifecycleCallbacks {
- private final String TAG = this.getClass().getName();
- /**
- * Constant used to specify how long will the app wait for another activity to go through its starting life
- * cycle events before running the teardownConnectionTask task.
- *
- * This is used as a means to detect whether or not the user has left the app.
- */
- private static final int DISCONNECT_DELAY = 1500;
-
- /**
- * Handler instance used to schedule tasks back to the main thread
- */
- private Handler mHandler = new Handler();
-
- /**
- * Weak reference to the application context
- */
- private WeakReference mContextReference;
-
- // In case we want to interact directly with the service
- private NetworkService mService;
-
- // Attributes that might need to be passed to the NetworkService
- private String mUserName = "";
- private String mPassword = "";
- private int mRequestedApis;
- private List mCustomNodeUrls = new ArrayList<>();
- private boolean mAutoConnect;
- private boolean mVerifyLatency;
- // Flag used to make sure we only call 'bindService' once.
-// private boolean mStartingService;
-
- /**
- * Runnable used to schedule a service disconnection once the app is not visible to the user for
- * more than DISCONNECT_DELAY milliseconds.
- */
- private final Runnable mDisconnectRunnable = new Runnable() {
- @Override
- public void run() {
- Context context = mContextReference.get();
- if(mService != null){
- context.unbindService(mServiceConnection);
- mService = null;
- }
- context.stopService(new Intent(context, NetworkService.class));
- }
- };
-
- private NetworkServiceManager(Context context){
- mContextReference = new WeakReference<>(context);
- }
-
- @Override
- public void onActivityCreated(Activity activity, Bundle bundle) { }
-
- @Override
- public void onActivityStarted(Activity activity) { }
-
- @Override
- public void onActivityResumed(Activity activity) {
- mHandler.removeCallbacks(mDisconnectRunnable);
- if(mService == null){
- // Creating a new Intent that will be used to start the NetworkService
- Context context = mContextReference.get();
- Intent intent = new Intent(context, NetworkService.class);
- context.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
- }
- }
-
- /**
- * This method passes all the required information to the NetworkService to properly
- * initialize itself
- */
- private void passRequiredInfoToConfigureService() {
- // Adding user-provided node URLs
- StringBuilder stringBuilder = new StringBuilder();
- Iterator it = mCustomNodeUrls.iterator();
- while(it.hasNext()){
- stringBuilder.append(it.next());
- if(it.hasNext()) stringBuilder.append(",");
- }
- String customNodes = stringBuilder.toString();
-
- Bundle b = new Bundle();
-
- // Adding all
- b.putString(NetworkService.KEY_USERNAME, mUserName);
- b.putString(NetworkService.KEY_PASSWORD, mPassword);
- b.putInt(NetworkService.KEY_REQUESTED_APIS, mRequestedApis);
- b.putString(NetworkService.KEY_NODE_URLS, customNodes);
- b.putBoolean(NetworkService.KEY_AUTO_CONNECT, mAutoConnect);
- b.putBoolean(NetworkService.KEY_ENABLE_LATENCY_VERIFIER, mVerifyLatency);
-
- mService.bootstrapService(b);
- }
-
- @Override
- public void onActivityPaused(Activity activity) {
- mHandler.postDelayed(mDisconnectRunnable, DISCONNECT_DELAY);
- }
-
- @Override
- public void onActivityStopped(Activity activity) {}
-
- @Override
- public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {}
-
- @Override
- public void onActivityDestroyed(Activity activity) {}
-
- /** Defines callbacks for backend binding, passed to bindService() */
- private ServiceConnection mServiceConnection = new ServiceConnection() {
-
- @Override
- public void onServiceConnected(ComponentName className,
- IBinder service) {
- // We've bound to LocalService, cast the IBinder and get LocalService instance
- NetworkService.LocalBinder binder = (NetworkService.LocalBinder) service;
- boolean passInfo = false;
- if(mService == null){
- mService = binder.getService();
- // We only pass the required information in case this is the first time we get a reference
- // to the NetworkService instance.
- passInfo = true;
- }
- if(passInfo)
- passRequiredInfoToConfigureService();
- }
-
- @Override
- public void onServiceDisconnected(ComponentName componentName) {
- }
- };
-
- public String getUserName() {
- return mUserName;
- }
-
- public void setUserName(String userName) {
- this.mUserName = userName;
- }
-
- public String getPassword() {
- return mPassword;
- }
-
- public void setPassword(String mPassword) {
- this.mPassword = mPassword;
- }
-
- public int getRequestedApis() {
- return mRequestedApis;
- }
-
- public void setRequestedApis(int mRequestedApis) {
- this.mRequestedApis = mRequestedApis;
- }
-
- public List getCustomNodeUrls() {
- return mCustomNodeUrls;
- }
-
- public void setCustomNodeUrls(List mCustomNodeUrls) {
- this.mCustomNodeUrls = mCustomNodeUrls;
- }
-
- public boolean isAutoConnect() {
- return mAutoConnect;
- }
-
- public void setAutoConnect(boolean mAutoConnect) {
- this.mAutoConnect = mAutoConnect;
- }
-
- public boolean isVerifyLatency() {
- return mVerifyLatency;
- }
-
- public void setVerifyLatency(boolean mVerifyLatency) {
- this.mVerifyLatency = mVerifyLatency;
- }
-
- /**
- * Class used to create a {@link NetworkServiceManager} with specific attributes.
- */
- public static class Builder {
- private String username;
- private String password;
- private int requestedApis;
- private List customNodeUrls;
- private boolean autoconnect = true;
- private boolean verifyNodeLatency;
- private double alpha = ExponentialMovingAverage.DEFAULT_ALPHA;
-
- /**
- * Sets the user name, if required to connect to a node.
- * @param name User name
- * @return The Builder instance
- */
- public Builder setUserName(String name){
- this.username = name;
- return this;
- }
-
- /**
- * Sets the password, if required to connect to a node.
- * @param password Password
- * @return The Builder instance
- */
- public Builder setPassword(String password){
- this.password = password;
- return this;
- }
-
- /**
- * Sets an integer with the requested APIs encoded as binary flags.
- * @param apis Integer representing the different APIs we require from the node.
- * @return The Builder instance
- */
- public Builder setRequestedApis(int apis){
- this.requestedApis = apis;
- return this;
- }
-
- /**
- * Adds a list of custom node URLs.
- * @param nodeUrls List of custom full node URLs.
- * @return The Builder instance
- */
- public Builder setCustomNodeUrls(List nodeUrls){
- this.customNodeUrls = nodeUrls;
- return this;
- }
-
- /**
- * Adds a list of custom node URLs.
- * @param nodeUrls List of custom full node URLs.
- * @return The Builder instance
- */
- public Builder setCustomNodeUrls(String nodeUrls){
- String[] urls = nodeUrls.split(",");
- for(String url : urls){
- if(customNodeUrls == null) customNodeUrls = new ArrayList<>();
- customNodeUrls.add(url);
- }
- return this;
- }
-
- /**
- * Sets the autoconnect flag. This is true by default.
- * @param autoConnect True if we want the service to connect automatically, false otherwise.
- * @return The Builder instance
- */
- public Builder setAutoConnect(boolean autoConnect){
- this.autoconnect = autoConnect;
- return this;
- }
-
- /**
- * Sets the node-verification flag. This is false by default.
- * @param verifyLatency True if we want the service to perform a latency analysis before connecting.
- * @return The Builder instance.
- */
- public Builder setNodeLatencyVerification(boolean verifyLatency){
- this.verifyNodeLatency = verifyLatency;
- return this;
- }
-
- /**
- * Sets the node latency verification's exponential moving average alpha parameter.
- * @param alpha The alpha parameter to use when computing the exponential moving average of the
- * measured latencies.
- * @return The Builder instance.
- */
- public Builder setLatencyAverageAlpha(double alpha){
- this.alpha = alpha;
- return this;
- }
-
- /**
- * Method used to build a {@link NetworkServiceManager} instance with all of the characteristics
- * passed as parameters.
- * @param context A Context of the application package implementing
- * this class.
- * @return Instance of the NetworkServiceManager class.
- */
- public NetworkServiceManager build(Context context){
- NetworkServiceManager manager = new NetworkServiceManager(context);
- if(username != null) manager.setUserName(username); else manager.setUserName("");
- if(password != null) manager.setPassword(password); else manager.setPassword("");
- if(customNodeUrls != null) manager.setCustomNodeUrls(customNodeUrls);
- manager.setRequestedApis(requestedApis);
- manager.setAutoConnect(autoconnect);
- manager.setVerifyLatency(verifyNodeLatency);
- return manager;
- }
- }
-}
diff --git a/sample/src/main/java/cy/agorise/labs/sample/ConnectedActivity.java b/sample/src/main/java/cy/agorise/labs/sample/ConnectedActivity.java
index 805f850..723af72 100644
--- a/sample/src/main/java/cy/agorise/labs/sample/ConnectedActivity.java
+++ b/sample/src/main/java/cy/agorise/labs/sample/ConnectedActivity.java
@@ -1,62 +1,20 @@
package cy.agorise.labs.sample;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.IBinder;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
-import android.util.Log;
import cy.agorise.graphenej.api.android.NetworkService;
-import cy.agorise.graphenej.network.NodeLatencyVerifier;
-public abstract class ConnectedActivity extends AppCompatActivity implements ServiceConnection {
+public abstract class ConnectedActivity extends AppCompatActivity {
private final String TAG = this.getClass().getName();
/* Network service connection */
protected NetworkService mNetworkService;
- /**
- * Flag used to keep track of the NetworkService binding state
- */
- private boolean mShouldUnbindNetwork;
-
- private ServiceConnection mNetworkServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName className,
- IBinder service) {
- // We've bound to LocalService, cast the IBinder and get LocalService instance
- NetworkService.LocalBinder binder = (NetworkService.LocalBinder) service;
- mNetworkService = binder.getService();
- ConnectedActivity.this.onServiceConnected(className, service);
- }
-
- @Override
- public void onServiceDisconnected(ComponentName componentName) {
- ConnectedActivity.this.onServiceDisconnected(componentName);
- }
- };
-
@Override
- protected void onStart() {
- super.onStart();
- Intent intent = new Intent(this, NetworkService.class);
- // Binding to NetworkService
- if(bindService(intent, mNetworkServiceConnection, Context.BIND_AUTO_CREATE)){
- mShouldUnbindNetwork = true;
- }else{
- Log.e(TAG,"Binding to the network service failed.");
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- // Unbinding from network service
- if(mShouldUnbindNetwork){
- unbindService(mNetworkServiceConnection);
- mShouldUnbindNetwork = false;
- }
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mNetworkService = NetworkService.getInstance();
}
}
diff --git a/sample/src/main/java/cy/agorise/labs/sample/HtlcActivity.java b/sample/src/main/java/cy/agorise/labs/sample/HtlcActivity.java
index d452898..95f8aec 100644
--- a/sample/src/main/java/cy/agorise/labs/sample/HtlcActivity.java
+++ b/sample/src/main/java/cy/agorise/labs/sample/HtlcActivity.java
@@ -1,8 +1,6 @@
package cy.agorise.labs.sample;
-import android.content.ComponentName;
import android.os.Bundle;
-import android.os.IBinder;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.widget.Toolbar;
@@ -239,10 +237,4 @@ public class HtlcActivity extends ConnectedActivity implements
// Return a newly built transaction
return new Transaction(privKey, blockData, operations);
}
-
- @Override
- public void onServiceConnected(ComponentName componentName, IBinder iBinder) { }
-
- @Override
- public void onServiceDisconnected(ComponentName componentName) { }
}
diff --git a/sample/src/main/java/cy/agorise/labs/sample/PerformCallActivity.java b/sample/src/main/java/cy/agorise/labs/sample/PerformCallActivity.java
index 041b4e8..8f7cd4a 100644
--- a/sample/src/main/java/cy/agorise/labs/sample/PerformCallActivity.java
+++ b/sample/src/main/java/cy/agorise/labs/sample/PerformCallActivity.java
@@ -1,9 +1,7 @@
package cy.agorise.labs.sample;
-import android.content.ComponentName;
import android.content.res.Resources;
import android.os.Bundle;
-import android.os.IBinder;
import android.support.design.widget.TextInputEditText;
import android.support.design.widget.TextInputLayout;
import android.text.InputType;
@@ -418,12 +416,14 @@ public class PerformCallActivity extends ConnectedActivity {
}
private void sendGetObjectsRequest(){
+ Log.d(TAG,"sendGetObjectsRequest");
String objectId = param1.getText().toString();
if(objectId.matches("\\d\\.\\d{1,3}\\.\\d{1,10}")){
ArrayList array = new ArrayList<>();
array.add(objectId);
GetObjects getObjects = new GetObjects(array);
long id = mNetworkService.sendMessage(getObjects, GetObjects.REQUIRED_API);
+ Log.d(TAG,"sendGetObnjetcsRequest id: "+id);
responseMap.put(id, mRPC);
}else{
param1.setError(getResources().getString(R.string.error_input_id));
@@ -639,14 +639,4 @@ public class PerformCallActivity extends ConnectedActivity {
if(!mDisposable.isDisposed())
mDisposable.dispose();
}
-
- @Override
- public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
- // Called upon NetworkService connection
- }
-
- @Override
- public void onServiceDisconnected(ComponentName componentName) {
- // Called upon NetworkService disconnection
- }
}
diff --git a/sample/src/main/java/cy/agorise/labs/sample/RemoveNodeActivity.java b/sample/src/main/java/cy/agorise/labs/sample/RemoveNodeActivity.java
index db7f861..499b4ca 100644
--- a/sample/src/main/java/cy/agorise/labs/sample/RemoveNodeActivity.java
+++ b/sample/src/main/java/cy/agorise/labs/sample/RemoveNodeActivity.java
@@ -1,17 +1,12 @@
package cy.agorise.labs.sample;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Build;
import android.os.Bundle;
-import android.os.IBinder;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
-import android.support.v7.app.AppCompatActivity;
import android.support.v7.util.SortedList;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
@@ -40,7 +35,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.subjects.PublishSubject;
-public class RemoveNodeActivity extends AppCompatActivity implements ServiceConnection {
+public class RemoveNodeActivity extends ConnectedActivity {
private final String TAG = this.getClass().getName();
@@ -66,18 +61,6 @@ public class RemoveNodeActivity extends AppCompatActivity implements ServiceConn
rvNodes.setLayoutManager(new LinearLayoutManager(this));
nodesAdapter = new FullNodesAdapter(this, LATENCY_COMPARATOR);
rvNodes.setAdapter(nodesAdapter);
- }
-
- @OnClick(R.id.btnReconnectNode)
- public void removeCurrentNode() {
- mNetworkService.reconnectNode();
- }
-
- @Override
- public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
- // We've bound to LocalService, cast the IBinder and get LocalService instance
- NetworkService.LocalBinder binder = (NetworkService.LocalBinder) iBinder;
- mNetworkService = binder.getService();
if(mNetworkService != null){
// PublishSubject used to announce full node latencies updates
@@ -90,9 +73,9 @@ public class RemoveNodeActivity extends AppCompatActivity implements ServiceConn
}
}
- @Override
- public void onServiceDisconnected(ComponentName componentName) {
- mNetworkService = null;
+ @OnClick(R.id.btnReconnectNode)
+ public void removeCurrentNode() {
+ mNetworkService.reconnectNode();
}
/**
@@ -119,20 +102,6 @@ public class RemoveNodeActivity extends AppCompatActivity implements ServiceConn
public void onComplete() { }
};
- @Override
- protected void onStart() {
- super.onStart();
- // Bind to LocalService
- Intent intent = new Intent(this, NetworkService.class);
- bindService(intent, this, Context.BIND_AUTO_CREATE);
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- unbindService(this);
- }
-
class FullNodesAdapter extends RecyclerView.Adapter {
class ViewHolder extends RecyclerView.ViewHolder {
diff --git a/sample/src/main/java/cy/agorise/labs/sample/SampleApplication.java b/sample/src/main/java/cy/agorise/labs/sample/SampleApplication.java
index 4039e61..80b3d2b 100644
--- a/sample/src/main/java/cy/agorise/labs/sample/SampleApplication.java
+++ b/sample/src/main/java/cy/agorise/labs/sample/SampleApplication.java
@@ -2,12 +2,7 @@ package cy.agorise.labs.sample;
import android.app.Application;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-
-import cy.agorise.graphenej.api.ApiAccess;
-import cy.agorise.graphenej.api.android.NetworkServiceManager;
+import cy.agorise.labs.sample.network.NetworkServiceManager;
/**
* Sample application class
@@ -18,9 +13,6 @@ public class SampleApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
- // Specifying some important information regarding the connection, such as the
- // credentials and the requested API accesses
- int requestedApis = ApiAccess.API_DATABASE | ApiAccess.API_HISTORY | ApiAccess.API_NETWORK_BROADCAST;
String[] nodeURLs = new String[]{
"wss://bitshares.openledger.info/ws",
@@ -29,38 +21,11 @@ public class SampleApplication extends Application {
"wss://citadel.li/node",
"wss://api.bts.mobi/ws"
};
- List nodeList = Arrays.asList(nodeURLs);
- String nodes = join(nodeList, ",");
- NetworkServiceManager networkManager = new NetworkServiceManager.Builder()
- .setUserName("username")
- .setPassword("secret")
- .setRequestedApis(requestedApis)
- .setCustomNodeUrls(nodes)
- .setAutoConnect(true)
- .setNodeLatencyVerification(true)
- .setLatencyAverageAlpha(0.1f)
- .build(this);
+ NetworkServiceManager networkManager = new NetworkServiceManager(nodeURLs);
// Registering this class as a listener to all activity's callback cycle events, in order to
// better estimate when the user has left the app and it is safe to disconnect the websocket connection
registerActivityLifecycleCallbacks(networkManager);
}
-
- /**
- * Private method used to join a sequence of Strings given a iterable representation
- * and a delimiter.
- *
- *
- * @param s Any collection of CharSequence that implements the Iterable interface.
- * @param delimiter The delimiter which will be used to join the different strings together.
- * @return A single string combining all the iterable pieces with the delimiter.
- */
- private String join(Iterable extends CharSequence> s, String delimiter) {
- Iterator extends CharSequence> iter = s.iterator();
- if (!iter.hasNext()) return "";
- StringBuilder buffer = new StringBuilder(iter.next());
- while (iter.hasNext()) buffer.append(delimiter).append(iter.next());
- return buffer.toString();
- }
}
diff --git a/sample/src/main/java/cy/agorise/labs/sample/SubscriptionActivity.java b/sample/src/main/java/cy/agorise/labs/sample/SubscriptionActivity.java
index a6b7daa..d3e5787 100644
--- a/sample/src/main/java/cy/agorise/labs/sample/SubscriptionActivity.java
+++ b/sample/src/main/java/cy/agorise/labs/sample/SubscriptionActivity.java
@@ -1,12 +1,6 @@
package cy.agorise.labs.sample;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
import android.os.Bundle;
-import android.os.IBinder;
-import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
@@ -14,7 +8,6 @@ import android.widget.TextView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
-import cy.agorise.graphenej.api.android.NetworkService;
import cy.agorise.graphenej.api.android.RxBus;
import cy.agorise.graphenej.api.calls.CancelAllSubscriptions;
import cy.agorise.graphenej.api.calls.SetSubscribeCallback;
@@ -23,16 +16,13 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
-public class SubscriptionActivity extends AppCompatActivity {
+public class SubscriptionActivity extends ConnectedActivity {
private final String TAG = this.getClass().getName();
@BindView(R.id.text_field)
TextView mTextField;
- // In case we want to interact directly with the service
- private NetworkService mService;
-
private Disposable mDisposable;
// Notification counter
@@ -63,51 +53,19 @@ public class SubscriptionActivity extends AppCompatActivity {
});
}
- @Override
- protected void onStart() {
- super.onStart();
- // Bind to LocalService
- Intent intent = new Intent(this, NetworkService.class);
- bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- unbindService(mConnection);
- }
-
@Override
protected void onDestroy() {
super.onDestroy();
mDisposable.dispose();
}
- /** Defines callbacks for backend binding, passed to bindService() */
- private ServiceConnection mConnection = new ServiceConnection() {
-
- @Override
- public void onServiceConnected(ComponentName className,
- IBinder service) {
- Log.d(TAG,"onServiceConnected");
- // We've bound to LocalService, cast the IBinder and get LocalService instance
- NetworkService.LocalBinder binder = (NetworkService.LocalBinder) service;
- mService = binder.getService();
- }
-
- @Override
- public void onServiceDisconnected(ComponentName componentName) {
- Log.d(TAG,"onServiceDisconnected");
- }
- };
-
@OnClick(R.id.subscribe)
public void onTransferFeeUsdClicked(View v){
- mService.sendMessage(new SetSubscribeCallback(true), SetSubscribeCallback.REQUIRED_API);
+ mNetworkService.sendMessage(new SetSubscribeCallback(true), SetSubscribeCallback.REQUIRED_API);
}
@OnClick(R.id.unsubscribe)
public void onTransferFeeBtsClicked(View v){
- mService.sendMessage(new CancelAllSubscriptions(), CancelAllSubscriptions.REQUIRED_API);
+ mNetworkService.sendMessage(new CancelAllSubscriptions(), CancelAllSubscriptions.REQUIRED_API);
}
}
diff --git a/sample/src/main/java/cy/agorise/labs/sample/network/NetworkServiceManager.java b/sample/src/main/java/cy/agorise/labs/sample/network/NetworkServiceManager.java
new file mode 100644
index 0000000..151037b
--- /dev/null
+++ b/sample/src/main/java/cy/agorise/labs/sample/network/NetworkServiceManager.java
@@ -0,0 +1,76 @@
+package cy.agorise.labs.sample.network;
+
+import android.app.Activity;
+import android.app.Application;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+
+import cy.agorise.graphenej.api.android.NetworkService;
+import cy.agorise.graphenej.stats.ExponentialMovingAverage;
+
+public class NetworkServiceManager implements Application.ActivityLifecycleCallbacks {
+ private final String TAG = this.getClass().getName();
+
+ /**
+ * Constant used to specify how long will the app wait for another activity to go through its starting life
+ * cycle events before running the teardownConnectionTask task.
+ *
+ * This is used as a means to detect whether or not the user has left the app.
+ */
+ private static final int DISCONNECT_DELAY = 1500;
+
+ /**
+ * Handler instance used to schedule tasks back to the main thread
+ */
+ private Handler mHandler = new Handler();
+
+ private NetworkService mNetworkService;
+
+ private String[] mNodeUrls;
+
+ public NetworkServiceManager(String[] nodes){
+ this.mNodeUrls = nodes;
+ }
+
+ /**
+ * Runnable used to schedule a service disconnection once the app is not visible to the user for
+ * more than DISCONNECT_DELAY milliseconds.
+ */
+ private final Runnable mDisconnectRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mNetworkService.stop();
+ mNetworkService = null;
+ }
+ };
+
+ @Override
+ public void onActivityCreated(Activity activity, Bundle bundle) { }
+
+ @Override
+ public void onActivityStarted(Activity activity) { }
+
+ @Override
+ public void onActivityResumed(Activity activity) {
+ mHandler.removeCallbacks(mDisconnectRunnable);
+ if(mNetworkService == null) {
+ mNetworkService = NetworkService.getInstance();
+ mNetworkService.start(this.mNodeUrls, ExponentialMovingAverage.DEFAULT_ALPHA);
+ }
+ }
+
+ @Override
+ public void onActivityPaused(Activity activity) {
+ mHandler.postDelayed(mDisconnectRunnable, DISCONNECT_DELAY);
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {}
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {}
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {}
+}