Modified the NetworkService and the NetworkServiceManager classes in order to pass arguments to the service via Intent extras and to allow delayed network connection
This commit is contained in:
parent
25222e5ea9
commit
ccf61858e4
7 changed files with 214 additions and 75 deletions
|
@ -2,10 +2,8 @@ package cy.agorise.graphenej.api.android;
|
|||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
@ -23,6 +21,7 @@ import cy.agorise.graphenej.Asset;
|
|||
import cy.agorise.graphenej.AssetAmount;
|
||||
import cy.agorise.graphenej.BaseOperation;
|
||||
import cy.agorise.graphenej.LimitOrder;
|
||||
import cy.agorise.graphenej.Memo;
|
||||
import cy.agorise.graphenej.RPC;
|
||||
import cy.agorise.graphenej.Transaction;
|
||||
import cy.agorise.graphenej.UserAccount;
|
||||
|
@ -49,7 +48,9 @@ import cy.agorise.graphenej.models.HistoryOperationDetail;
|
|||
import cy.agorise.graphenej.models.JsonRpcNotification;
|
||||
import cy.agorise.graphenej.models.JsonRpcResponse;
|
||||
import cy.agorise.graphenej.models.OperationHistory;
|
||||
import cy.agorise.graphenej.Memo;
|
||||
import cy.agorise.graphenej.network.FullNode;
|
||||
import cy.agorise.graphenej.network.LatencyNodeProvider;
|
||||
import cy.agorise.graphenej.network.NodeProvider;
|
||||
import cy.agorise.graphenej.operations.CustomOperation;
|
||||
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
|
||||
import cy.agorise.graphenej.operations.TransferOperation;
|
||||
|
@ -75,6 +76,11 @@ public class NetworkService extends Service {
|
|||
|
||||
public static final String KEY_REQUESTED_APIS = "key_requested_apis";
|
||||
|
||||
/**
|
||||
* Shared preference
|
||||
*/
|
||||
public static final String KEY_AUTO_CONNECT = "key_auto_connect";
|
||||
|
||||
/**
|
||||
* Constant used to pass a custom list of node URLs. This should be a simple
|
||||
* comma separated list of URLs.
|
||||
|
@ -89,8 +95,6 @@ public class NetworkService extends Service {
|
|||
|
||||
private WebSocket mWebSocket;
|
||||
|
||||
private int mSocketIndex;
|
||||
|
||||
// Username and password used to connect to a specific node
|
||||
private String mUsername;
|
||||
private String mPassword;
|
||||
|
@ -100,13 +104,15 @@ public class NetworkService extends Service {
|
|||
private String mLastCall;
|
||||
private long mCurrentId = 0;
|
||||
|
||||
private boolean mAutoConnect;
|
||||
|
||||
// Requested APIs passed to this service
|
||||
private int mRequestedApis;
|
||||
|
||||
// Variable used to keep track of the currently obtained API accesses
|
||||
private HashMap<Integer, Integer> mApiIds = new HashMap<Integer, Integer>();
|
||||
|
||||
private ArrayList<String> mNodeUrls = new ArrayList<>();
|
||||
NodeProvider nodeProvider = new LatencyNodeProvider();
|
||||
|
||||
private Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(Transaction.class, new Transaction.TransactionDeserializer())
|
||||
|
@ -132,36 +138,15 @@ public class NetworkService extends Service {
|
|||
// suited for every response type.
|
||||
private DeserializationMap mDeserializationMap = new DeserializationMap();
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
|
||||
// Retrieving credentials and requested API data from the shared preferences
|
||||
mUsername = pref.getString(NetworkService.KEY_USERNAME, "");
|
||||
mPassword = pref.getString(NetworkService.KEY_PASSWORD, "");
|
||||
mRequestedApis = pref.getInt(NetworkService.KEY_REQUESTED_APIS, -1);
|
||||
|
||||
// If the user of the library desires, a custom list of node URLs can
|
||||
// be passed using the KEY_CUSTOM_NODE_URLS constant
|
||||
String serializedNodeUrls = pref.getString(NetworkService.KEY_CUSTOM_NODE_URLS, "");
|
||||
|
||||
// Deciding whether to use an externally provided list of node URLs, or use our internal one
|
||||
if(serializedNodeUrls.equals("")){
|
||||
mNodeUrls.addAll(Arrays.asList(Nodes.NODE_URLS));
|
||||
}else{
|
||||
String[] urls = serializedNodeUrls.split(",");
|
||||
mNodeUrls.addAll(Arrays.asList(urls));
|
||||
}
|
||||
connect();
|
||||
}
|
||||
|
||||
private void connect(){
|
||||
/**
|
||||
* Actually establishes a connection from this Service to one of the full nodes.
|
||||
*/
|
||||
public void connect(){
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
String url = mNodeUrls.get(mSocketIndex % mNodeUrls.size());
|
||||
Log.d(TAG,"Trying to connect with: "+url);
|
||||
Request request = new Request.Builder().url(url).build();
|
||||
client.newWebSocket(request, mWebSocketListener);
|
||||
FullNode fullNode = nodeProvider.getBestNode();
|
||||
Log.d(TAG,"connect.url: "+fullNode.getUrl());
|
||||
Request request = new Request.Builder().url(fullNode.getUrl()).build();
|
||||
mWebSocket = client.newWebSocket(request, mWebSocketListener);
|
||||
}
|
||||
|
||||
public long sendMessage(String message){
|
||||
|
@ -214,14 +199,38 @@ public class NetworkService extends Service {
|
|||
mWebSocket.close(NORMAL_CLOSURE_STATUS, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
Log.d(TAG,"onBind.intent: "+intent);
|
||||
// Retrieving credentials and requested API data from the shared preferences
|
||||
mUsername = intent.getStringExtra(NetworkService.KEY_USERNAME);
|
||||
mPassword = intent.getStringExtra(NetworkService.KEY_PASSWORD);
|
||||
mRequestedApis = intent.getIntExtra(NetworkService.KEY_REQUESTED_APIS, 0);
|
||||
mAutoConnect = intent.getBooleanExtra(NetworkService.KEY_AUTO_CONNECT, true);
|
||||
|
||||
|
||||
ArrayList<String> nodeUrls = new ArrayList<>();
|
||||
// If the user of the library desires, a custom list of node URLs can
|
||||
// be passed using the KEY_CUSTOM_NODE_URLS constant
|
||||
String customNodeUrls = intent.getStringExtra(NetworkService.KEY_CUSTOM_NODE_URLS);
|
||||
|
||||
// Adding user-provided list of node URLs first
|
||||
if(customNodeUrls != null){
|
||||
String[] urls = customNodeUrls.split(",");
|
||||
ArrayList<String> urlList = new ArrayList<>(Arrays.asList(urls));
|
||||
nodeUrls.addAll(urlList);
|
||||
}
|
||||
|
||||
// Adding the library-provided list of nodes second
|
||||
nodeUrls.addAll(Arrays.asList(Nodes.NODE_URLS));
|
||||
|
||||
for(String nodeUrl : nodeUrls){
|
||||
nodeProvider.addNode(new FullNode(nodeUrl));
|
||||
}
|
||||
|
||||
if(mAutoConnect) connect();
|
||||
|
||||
return mBinder;
|
||||
}
|
||||
|
||||
|
@ -241,7 +250,6 @@ public class NetworkService extends Service {
|
|||
@Override
|
||||
public void onOpen(WebSocket webSocket, Response response) {
|
||||
super.onOpen(webSocket, response);
|
||||
mWebSocket = webSocket;
|
||||
|
||||
// Notifying all listeners about the new connection status
|
||||
RxBus.getBusInstance().send(new ConnectionStatusUpdate(ConnectionStatusUpdate.CONNECTED, ApiAccess.API_NONE));
|
||||
|
@ -494,9 +502,8 @@ public class NetworkService extends Service {
|
|||
}
|
||||
|
||||
RxBus.getBusInstance().send(new ConnectionStatusUpdate(ConnectionStatusUpdate.DISCONNECTED, ApiAccess.API_NONE));
|
||||
mSocketIndex++;
|
||||
|
||||
if(mSocketIndex > mNodeUrls.size() * 3){
|
||||
if(nodeProvider.getBestNode() == null){
|
||||
Log.e(TAG,"Giving up on connections");
|
||||
stopSelf();
|
||||
}else{
|
||||
|
@ -516,11 +523,19 @@ public class NetworkService extends Service {
|
|||
return mApiIds.get(whichApi) != null;
|
||||
}
|
||||
|
||||
public ArrayList<String> getNodeUrls() {
|
||||
return mNodeUrls;
|
||||
/**
|
||||
* Updates the full node details
|
||||
* @param fullNode Updated {@link FullNode} instance
|
||||
*/
|
||||
public void updateNode(FullNode fullNode){
|
||||
nodeProvider.updateNode(fullNode);
|
||||
}
|
||||
|
||||
public void setNodeUrls(ArrayList<String> mNodeUrls) {
|
||||
this.mNodeUrls = mNodeUrls;
|
||||
/**
|
||||
* Returns a list of {@link FullNode} instances
|
||||
* @return List of full nodes
|
||||
*/
|
||||
List<FullNode> getSortedNodes(){
|
||||
return nodeProvider.getSortedNodes();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,9 @@ 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;
|
||||
|
||||
/**
|
||||
* This class should be instantiated at the application level of the android app.
|
||||
|
@ -20,8 +23,7 @@ import java.lang.ref.WeakReference;
|
|||
*/
|
||||
|
||||
public class NetworkServiceManager implements Application.ActivityLifecycleCallbacks {
|
||||
private final String TAG = this.getClass().getName();
|
||||
|
||||
private final static String TAG = "NetworkServiceManager";
|
||||
/**
|
||||
* 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.
|
||||
|
@ -43,6 +45,13 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb
|
|||
// 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<String> mCustomNodeUrls = new ArrayList<>();
|
||||
private boolean mAutoConnect;
|
||||
|
||||
/**
|
||||
* Runnable used to schedule a service disconnection once the app is not visible to the user for
|
||||
* more than DISCONNECT_DELAY milliseconds.
|
||||
|
@ -77,8 +86,26 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb
|
|||
public void onActivityStarted(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);
|
||||
|
||||
// Adding user-provided node URLs
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
Iterator<String> it = mCustomNodeUrls.iterator();
|
||||
while(it.hasNext()){
|
||||
stringBuilder.append(it.next());
|
||||
if(it.hasNext()) stringBuilder.append(",");
|
||||
}
|
||||
String customNodes = stringBuilder.toString();
|
||||
|
||||
// Adding all
|
||||
intent.putExtra(NetworkService.KEY_USERNAME, mUserName)
|
||||
.putExtra(NetworkService.KEY_PASSWORD, mPassword)
|
||||
.putExtra(NetworkService.KEY_REQUESTED_APIS, mRequestedApis)
|
||||
.putExtra(NetworkService.KEY_CUSTOM_NODE_URLS, customNodes)
|
||||
.putExtra(NetworkService.KEY_AUTO_CONNECT, mAutoConnect);
|
||||
|
||||
context.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
}
|
||||
|
@ -114,4 +141,99 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb
|
|||
@Override
|
||||
public void onServiceDisconnected(ComponentName componentName) {}
|
||||
};
|
||||
|
||||
public String getUserName() {
|
||||
return mUserName;
|
||||
}
|
||||
|
||||
public void setUserName(String mUserName) {
|
||||
this.mUserName = mUserName;
|
||||
}
|
||||
|
||||
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<String> getCustomNodeUrls() {
|
||||
return mCustomNodeUrls;
|
||||
}
|
||||
|
||||
public void setCustomNodeUrls(List<String> mCustomNodeUrls) {
|
||||
this.mCustomNodeUrls = mCustomNodeUrls;
|
||||
}
|
||||
|
||||
public boolean isAutoConnect() {
|
||||
return mAutoConnect;
|
||||
}
|
||||
|
||||
public void setAutoConnect(boolean mAutoConnect) {
|
||||
this.mAutoConnect = mAutoConnect;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<String> customNodeUrls;
|
||||
private boolean autoconnect = true;
|
||||
|
||||
public Builder setUserName(String name){
|
||||
this.username = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setPassword(String password){
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRequestedApis(int apis){
|
||||
this.requestedApis = apis;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCustomNodeUrls(List<String> nodeUrls){
|
||||
this.customNodeUrls = nodeUrls;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCustomNodeUrls(String nodeUrls){
|
||||
String[] urls = nodeUrls.split(",");
|
||||
for(String url : urls){
|
||||
if(customNodeUrls == null) customNodeUrls = new ArrayList<>();
|
||||
customNodeUrls.add(url);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAutoConnect(boolean autoConnect){
|
||||
this.autoconnect = autoConnect;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NetworkServiceManager build(Context context){
|
||||
NetworkServiceManager manager = new NetworkServiceManager(context);
|
||||
if(username != null) manager.setUserName(username);
|
||||
if(password != null) manager.setPassword(password);
|
||||
if(customNodeUrls != null) manager.setCustomNodeUrls(customNodeUrls);
|
||||
manager.setRequestedApis(requestedApis);
|
||||
manager.setAutoConnect(autoconnect);
|
||||
return manager;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,4 +60,17 @@ public class FullNode implements Comparable {
|
|||
FullNode node = (FullNode) o;
|
||||
return (int) Math.ceil(latency.getAverage() - node.getLatencyValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
FullNode fullNode = (FullNode) o;
|
||||
return mUrl.equals(fullNode.getUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mUrl.hashCode();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import java.util.List;
|
|||
public interface NodeProvider {
|
||||
|
||||
/**
|
||||
* Returns the node with the best characteristics.
|
||||
* Returns the node with the best characteristics. Returns null if there is no {@link FullNode}
|
||||
* @return A FullNode instance
|
||||
*/
|
||||
FullNode getBestNode();
|
||||
|
|
|
@ -16,7 +16,6 @@ import butterknife.ButterKnife;
|
|||
import cy.agorise.graphenej.RPC;
|
||||
|
||||
public class CallsActivity extends AppCompatActivity {
|
||||
private final String TAG = this.getClass().getName();
|
||||
|
||||
@BindView(R.id.call_list)
|
||||
RecyclerView mRecyclerView;
|
||||
|
@ -26,10 +25,9 @@ public class CallsActivity extends AppCompatActivity {
|
|||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_calls);
|
||||
ButterKnife.bind(this);
|
||||
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
|
||||
mRecyclerView.setHasFixedSize(true);
|
||||
mRecyclerView.setLayoutManager(linearLayoutManager);
|
||||
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, linearLayoutManager.getOrientation()));
|
||||
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
|
||||
mRecyclerView.setAdapter(new CallAdapter());
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@ public abstract class ConnectedActivity extends AppCompatActivity implements Ser
|
|||
// 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);
|
||||
}
|
||||
|
||||
|
@ -41,8 +40,8 @@ public abstract class ConnectedActivity extends AppCompatActivity implements Ser
|
|||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
// Binding to NetworkService
|
||||
Intent intent = new Intent(this, NetworkService.class);
|
||||
// Binding to NetworkService
|
||||
if(bindService(intent, mNetworkServiceConnection, Context.BIND_AUTO_CREATE)){
|
||||
mShouldUnbindNetwork = true;
|
||||
}else{
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
package cy.agorise.labs.sample;
|
||||
|
||||
import android.app.Application;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import cy.agorise.graphenej.api.ApiAccess;
|
||||
import cy.agorise.graphenej.api.android.NetworkService;
|
||||
import cy.agorise.graphenej.api.android.NetworkServiceManager;
|
||||
|
||||
/**
|
||||
|
@ -12,30 +10,24 @@ import cy.agorise.graphenej.api.android.NetworkServiceManager;
|
|||
*/
|
||||
|
||||
public class SampleApplication extends Application {
|
||||
private final String TAG = this.getClass().getName();
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
// This variable would hold a list of custom nodes
|
||||
String customNodes = "wss://mydomain.net/ws,wss://myotherdomain.com/ws";
|
||||
|
||||
// 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;
|
||||
PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.edit()
|
||||
.putString(NetworkService.KEY_USERNAME, "nelson")
|
||||
.putString(NetworkService.KEY_PASSWORD, "secret")
|
||||
.putInt(NetworkService.KEY_REQUESTED_APIS, requestedApis)
|
||||
// .putString(NetworkService.KEY_CUSTOM_NODE_URLS, customNodes)
|
||||
.apply();
|
||||
|
||||
/*
|
||||
* 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(new NetworkServiceManager(this));
|
||||
NetworkServiceManager networkManager = new NetworkServiceManager.Builder()
|
||||
.setUserName("nelson")
|
||||
.setPassword("secret")
|
||||
.setRequestedApis(requestedApis)
|
||||
.setCustomNodeUrls("wss://eu.nodes.bitshares.ws")
|
||||
.setAutoConnect(true)
|
||||
.build(this);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue