Added support for message subscriptions on the single connection mode
This commit is contained in:
parent
d2390b0a45
commit
7e2ef7b705
12 changed files with 312 additions and 91 deletions
|
@ -9,6 +9,7 @@ import android.preference.PreferenceManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
@ -22,6 +23,8 @@ import cy.agorise.graphenej.Asset;
|
||||||
import cy.agorise.graphenej.AssetAmount;
|
import cy.agorise.graphenej.AssetAmount;
|
||||||
import cy.agorise.graphenej.LimitOrder;
|
import cy.agorise.graphenej.LimitOrder;
|
||||||
import cy.agorise.graphenej.RPC;
|
import cy.agorise.graphenej.RPC;
|
||||||
|
import cy.agorise.graphenej.Transaction;
|
||||||
|
import cy.agorise.graphenej.UserAccount;
|
||||||
import cy.agorise.graphenej.api.ApiAccess;
|
import cy.agorise.graphenej.api.ApiAccess;
|
||||||
import cy.agorise.graphenej.api.ConnectionStatusUpdate;
|
import cy.agorise.graphenej.api.ConnectionStatusUpdate;
|
||||||
import cy.agorise.graphenej.api.bitshares.Nodes;
|
import cy.agorise.graphenej.api.bitshares.Nodes;
|
||||||
|
@ -38,8 +41,14 @@ import cy.agorise.graphenej.models.ApiCall;
|
||||||
import cy.agorise.graphenej.models.Block;
|
import cy.agorise.graphenej.models.Block;
|
||||||
import cy.agorise.graphenej.models.BlockHeader;
|
import cy.agorise.graphenej.models.BlockHeader;
|
||||||
import cy.agorise.graphenej.models.BucketObject;
|
import cy.agorise.graphenej.models.BucketObject;
|
||||||
|
import cy.agorise.graphenej.models.DynamicGlobalProperties;
|
||||||
|
import cy.agorise.graphenej.models.JsonRpcNotification;
|
||||||
import cy.agorise.graphenej.models.JsonRpcResponse;
|
import cy.agorise.graphenej.models.JsonRpcResponse;
|
||||||
import cy.agorise.graphenej.models.OperationHistory;
|
import cy.agorise.graphenej.models.OperationHistory;
|
||||||
|
import cy.agorise.graphenej.objects.Memo;
|
||||||
|
import cy.agorise.graphenej.operations.CustomOperation;
|
||||||
|
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
|
||||||
|
import cy.agorise.graphenej.operations.TransferOperation;
|
||||||
import io.reactivex.annotations.Nullable;
|
import io.reactivex.annotations.Nullable;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
|
@ -95,7 +104,18 @@ public class NetworkService extends Service {
|
||||||
|
|
||||||
private ArrayList<String> mNodeUrls = new ArrayList<>();
|
private ArrayList<String> mNodeUrls = new ArrayList<>();
|
||||||
|
|
||||||
private Gson gson = new Gson();
|
private Gson gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(Transaction.class, new Transaction.TransactionDeserializer())
|
||||||
|
.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer())
|
||||||
|
.registerTypeAdapter(LimitOrderCreateOperation.class, new LimitOrderCreateOperation.LimitOrderCreateDeserializer())
|
||||||
|
.registerTypeAdapter(CustomOperation.class, new CustomOperation.CustomOperationDeserializer())
|
||||||
|
.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer())
|
||||||
|
.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountSimpleDeserializer())
|
||||||
|
.registerTypeAdapter(DynamicGlobalProperties.class, new DynamicGlobalProperties.DynamicGlobalPropertiesDeserializer())
|
||||||
|
.registerTypeAdapter(Memo.class, new Memo.MemoDeserializer())
|
||||||
|
.registerTypeAdapter(OperationHistory.class, new OperationHistory.OperationHistoryDeserializer())
|
||||||
|
.registerTypeAdapter(JsonRpcNotification.class, new JsonRpcNotification.JsonRpcNotificationDeserializer())
|
||||||
|
.create();
|
||||||
|
|
||||||
// Map used to keep track of outgoing request ids and its request types. This is just
|
// Map used to keep track of outgoing request ids and its request types. This is just
|
||||||
// one of two required mappings. The second one is implemented by the DeserializationMap
|
// one of two required mappings. The second one is implemented by the DeserializationMap
|
||||||
|
@ -287,10 +307,32 @@ public class NetworkService extends Service {
|
||||||
mLastCall = "";
|
mLastCall = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Properly de-serialize all other fields and broadcasts to the event bus
|
||||||
|
handleJsonRpcResponse(response, text);
|
||||||
}else{
|
}else{
|
||||||
|
// If no 'result' field was found, this incoming message probably corresponds to a
|
||||||
|
// JSON-RPC notification message, which should have a 'method' field with the string
|
||||||
|
// 'notice' as its value
|
||||||
|
JsonRpcNotification notification = gson.fromJson(text, JsonRpcNotification.class);
|
||||||
|
if(notification.method != null && notification.method.equals("notice")){
|
||||||
|
handleJsonRpcNotification(notification);
|
||||||
|
}else{
|
||||||
|
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);
|
Log.w(TAG,"Error.Msg: "+response.error.message);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private method that will de-serialize all fields of every kind of JSON-RPC response
|
||||||
|
* and broadcast it to the event bus.
|
||||||
|
*
|
||||||
|
* @param response De-serialized response
|
||||||
|
* @param text Raw text, as received
|
||||||
|
*/
|
||||||
|
private void handleJsonRpcResponse(JsonRpcResponse response, String text){
|
||||||
JsonRpcResponse parsedResponse = null;
|
JsonRpcResponse parsedResponse = null;
|
||||||
|
|
||||||
Class requestClass = mRequestClassMap.get(response.id);
|
Class requestClass = mRequestClassMap.get(response.id);
|
||||||
|
@ -355,6 +397,15 @@ public class NetworkService extends Service {
|
||||||
RxBus.getBusInstance().send(parsedResponse);
|
RxBus.getBusInstance().send(parsedResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private method that will just broadcast a de-serialized notification to all interested parties
|
||||||
|
* @param notification De-serialized notification
|
||||||
|
*/
|
||||||
|
private void handleJsonRpcNotification(JsonRpcNotification notification){
|
||||||
|
// Broadcasting the parsed notification to all interested listeners
|
||||||
|
RxBus.getBusInstance().send(notification);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method used to try to deserialize a 'get_objects' API call. Since this request can be used
|
* Method used to try to deserialize a 'get_objects' API call. Since this request can be used
|
||||||
* for several types of objects, the de-serialization procedure can be a bit more complex.
|
* for several types of objects, the de-serialization procedure can be a bit more complex.
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package cy.agorise.graphenej.api.calls;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import cy.agorise.graphenej.RPC;
|
||||||
|
import cy.agorise.graphenej.api.ApiAccess;
|
||||||
|
import cy.agorise.graphenej.models.ApiCall;
|
||||||
|
|
||||||
|
public class CancelAllSubscriptions implements ApiCallable {
|
||||||
|
public static final int REQUIRED_API = ApiAccess.API_DATABASE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ApiCall toApiCall(int apiId, long sequenceId) {
|
||||||
|
return new ApiCall(apiId, RPC.CALL_CANCEL_ALL_SUBSCRIPTIONS, new ArrayList<Serializable>(), RPC.VERSION, sequenceId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package cy.agorise.graphenej.api.calls;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import cy.agorise.graphenej.RPC;
|
||||||
|
import cy.agorise.graphenej.api.ApiAccess;
|
||||||
|
import cy.agorise.graphenej.models.ApiCall;
|
||||||
|
|
||||||
|
public class SetSubscribeCallback implements ApiCallable {
|
||||||
|
public static final int REQUIRED_API = ApiAccess.API_DATABASE;
|
||||||
|
|
||||||
|
private boolean clearFilter;
|
||||||
|
|
||||||
|
public SetSubscribeCallback(boolean clearFilter){
|
||||||
|
this.clearFilter = clearFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ApiCall toApiCall(int apiId, long sequenceId) {
|
||||||
|
ArrayList<Serializable> subscriptionParams = new ArrayList<>();
|
||||||
|
subscriptionParams.add(String.format("%d", sequenceId));
|
||||||
|
subscriptionParams.add(clearFilter);
|
||||||
|
return new ApiCall(apiId, RPC.CALL_SET_SUBSCRIBE_CALLBACK, subscriptionParams, RPC.VERSION, sequenceId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
package cy.agorise.graphenej.models;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import cy.agorise.graphenej.GrapheneObject;
|
||||||
|
import cy.agorise.graphenej.ObjectType;
|
||||||
|
import cy.agorise.graphenej.OperationType;
|
||||||
|
import cy.agorise.graphenej.Transaction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that represents a generic subscription notification.
|
||||||
|
* The template for every subscription response is the following:
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* "method": "notice"
|
||||||
|
* "params": [
|
||||||
|
* SUBSCRIPTION_ID,
|
||||||
|
* [[
|
||||||
|
* { "id": "2.1.0", ... },
|
||||||
|
* { "id": ... },
|
||||||
|
* { "id": ... },
|
||||||
|
* { "id": ... }
|
||||||
|
* ]]
|
||||||
|
* ],
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
public class JsonRpcNotification {
|
||||||
|
public static final String KEY_METHOD = "method";
|
||||||
|
public static final String KEY_PARAMS = "params";
|
||||||
|
|
||||||
|
public String method;
|
||||||
|
public List<Serializable> params;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inner static class used to parse and deserialize subscription notifications.
|
||||||
|
*/
|
||||||
|
public static class JsonRpcNotificationDeserializer implements JsonDeserializer<JsonRpcNotification> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonRpcNotification deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||||
|
JsonRpcNotification notification = new JsonRpcNotification();
|
||||||
|
JsonObject responseObject = json.getAsJsonObject();
|
||||||
|
if(!responseObject.has(KEY_METHOD)){
|
||||||
|
return notification;
|
||||||
|
}
|
||||||
|
notification.method = responseObject.get(KEY_METHOD).getAsString();
|
||||||
|
|
||||||
|
JsonArray paramsArray = responseObject.get(KEY_PARAMS).getAsJsonArray();
|
||||||
|
notification.params = new ArrayList<>();
|
||||||
|
notification.params.add(paramsArray.get(0).getAsInt());
|
||||||
|
ArrayList<Serializable> secondArgument = new ArrayList<>();
|
||||||
|
notification.params.add(secondArgument);
|
||||||
|
|
||||||
|
JsonArray subArray = paramsArray.get(1).getAsJsonArray().get(0).getAsJsonArray();
|
||||||
|
for(JsonElement object : subArray){
|
||||||
|
if(object.isJsonObject()){
|
||||||
|
GrapheneObject grapheneObject = new GrapheneObject(object.getAsJsonObject().get(GrapheneObject.KEY_ID).getAsString());
|
||||||
|
|
||||||
|
JsonObject jsonObject = object.getAsJsonObject();
|
||||||
|
if(grapheneObject.getObjectType() == ObjectType.ACCOUNT_BALANCE_OBJECT){
|
||||||
|
AccountBalanceUpdate balanceObject = new AccountBalanceUpdate(grapheneObject.getObjectId());
|
||||||
|
balanceObject.owner = jsonObject.get(AccountBalanceUpdate.KEY_OWNER).getAsString();
|
||||||
|
balanceObject.asset_type = jsonObject.get(AccountBalanceUpdate.KEY_ASSET_TYPE).getAsString();
|
||||||
|
balanceObject.balance = jsonObject.get(AccountBalanceUpdate.KEY_BALANCE).getAsLong();
|
||||||
|
secondArgument.add(balanceObject);
|
||||||
|
}else if(grapheneObject.getObjectType() == ObjectType.DYNAMIC_GLOBAL_PROPERTY_OBJECT){
|
||||||
|
DynamicGlobalProperties dynamicGlobalProperties = context.deserialize(object, DynamicGlobalProperties.class);
|
||||||
|
secondArgument.add(dynamicGlobalProperties);
|
||||||
|
}else if(grapheneObject.getObjectType() == ObjectType.TRANSACTION_OBJECT){
|
||||||
|
BroadcastedTransaction broadcastedTransaction = new BroadcastedTransaction(grapheneObject.getObjectId());
|
||||||
|
broadcastedTransaction.setTransaction((Transaction) context.deserialize(jsonObject.get(BroadcastedTransaction.KEY_TRX), Transaction.class));
|
||||||
|
broadcastedTransaction.setTransactionId(jsonObject.get(BroadcastedTransaction.KEY_TRX_ID).getAsString());
|
||||||
|
secondArgument.add(broadcastedTransaction);
|
||||||
|
}else if(grapheneObject.getObjectType() == ObjectType.OPERATION_HISTORY_OBJECT){
|
||||||
|
if(jsonObject.get(OperationHistory.KEY_OP).getAsJsonArray().get(0).getAsLong() == OperationType.TRANSFER_OPERATION.ordinal()){
|
||||||
|
OperationHistory operationHistory = context.deserialize(jsonObject, OperationHistory.class);
|
||||||
|
secondArgument.add(operationHistory);
|
||||||
|
}else{
|
||||||
|
//TODO: Add support for other operations
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
//TODO: Add support for other types of objects
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
secondArgument.add(object.getAsString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return notification;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,15 +44,12 @@ import cy.agorise.graphenej.interfaces.SubscriptionListener;
|
||||||
* To minimize CPU usage, we introduce a scheme of selective parsing, implemented by the static inner class
|
* To minimize CPU usage, we introduce a scheme of selective parsing, implemented by the static inner class
|
||||||
* SubscriptionResponseDeserializer.
|
* SubscriptionResponseDeserializer.
|
||||||
*
|
*
|
||||||
* Created by nelson on 1/12/17.
|
|
||||||
*/
|
*/
|
||||||
public class SubscriptionResponse {
|
public class SubscriptionResponse {
|
||||||
private static final String TAG = "SubscriptionResponse";
|
|
||||||
public static final String KEY_ID = "id";
|
public static final String KEY_ID = "id";
|
||||||
public static final String KEY_METHOD = "method";
|
public static final String KEY_METHOD = "method";
|
||||||
public static final String KEY_PARAMS = "params";
|
public static final String KEY_PARAMS = "params";
|
||||||
|
|
||||||
public int id;
|
|
||||||
public String method;
|
public String method;
|
||||||
public List<Serializable> params;
|
public List<Serializable> params;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
package cy.agorise.graphenej.models;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import cy.agorise.graphenej.AssetAmount;
|
||||||
|
import cy.agorise.graphenej.Transaction;
|
||||||
|
import cy.agorise.graphenej.UserAccount;
|
||||||
|
import cy.agorise.graphenej.objects.Memo;
|
||||||
|
import cy.agorise.graphenej.operations.CustomOperation;
|
||||||
|
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
|
||||||
|
import cy.agorise.graphenej.operations.TransferOperation;
|
||||||
|
|
||||||
|
public class JsonRpcNotificationTest {
|
||||||
|
|
||||||
|
private String text = "{\"method\":\"notice\",\"params\":[3,[[{\"id\":\"2.1.0\",\"head_block_number\":30071834,\"head_block_id\":\"01cadc1a5f3f517e2eba9588111aef3af3c59916\",\"time\":\"2018-08-30T18:19:45\",\"current_witness\":\"1.6.74\",\"next_maintenance_time\":\"2018-08-30T19:00:00\",\"last_budget_time\":\"2018-08-30T18:00:00\",\"witness_budget\":80800000,\"accounts_registered_this_interval\":9,\"recently_missed_count\":0,\"current_aslot\":30228263,\"recent_slots_filled\":\"340282366920938463463374607431768211455\",\"dynamic_flags\":0,\"last_irreversible_block_num\":30071813}]]]}";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failResponseDeserialization(){
|
||||||
|
Gson gson = new Gson();
|
||||||
|
JsonRpcResponse<?> response = gson.fromJson(text, JsonRpcResponse.class);
|
||||||
|
// The result field of this de-serialized object should be null
|
||||||
|
Assert.assertNull(response.result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void succeedNotificationDeserialization(){
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(Transaction.class, new Transaction.TransactionDeserializer())
|
||||||
|
.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer())
|
||||||
|
.registerTypeAdapter(LimitOrderCreateOperation.class, new LimitOrderCreateOperation.LimitOrderCreateDeserializer())
|
||||||
|
.registerTypeAdapter(CustomOperation.class, new CustomOperation.CustomOperationDeserializer())
|
||||||
|
.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer())
|
||||||
|
.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountSimpleDeserializer())
|
||||||
|
.registerTypeAdapter(DynamicGlobalProperties.class, new DynamicGlobalProperties.DynamicGlobalPropertiesDeserializer())
|
||||||
|
.registerTypeAdapter(Memo.class, new Memo.MemoDeserializer())
|
||||||
|
.registerTypeAdapter(OperationHistory.class, new OperationHistory.OperationHistoryDeserializer())
|
||||||
|
.registerTypeAdapter(JsonRpcNotification.class, new JsonRpcNotification.JsonRpcNotificationDeserializer())
|
||||||
|
.create();
|
||||||
|
JsonRpcNotification notification = gson.fromJson(text, JsonRpcNotification.class);
|
||||||
|
// Should deserialize a 'params' array with 2 elements
|
||||||
|
Assert.assertEquals(2, notification.params.size());
|
||||||
|
// The first element should be the number 3
|
||||||
|
Assert.assertEquals(3, notification.params.get(0));
|
||||||
|
ArrayList<Serializable> secondArgument = (ArrayList<Serializable>) notification.params.get(1);
|
||||||
|
// The second element should be an array of length 1
|
||||||
|
Assert.assertEquals(1, secondArgument.size());
|
||||||
|
// Extracting the payload, which should be in itself another array
|
||||||
|
DynamicGlobalProperties payload = (DynamicGlobalProperties) secondArgument.get(0);
|
||||||
|
// Dynamic global properties head_block_number should match
|
||||||
|
Assert.assertEquals(30071834, payload.head_block_number);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package cy.agorise.graphenej.models;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
|
import junit.framework.Assert;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class JsonRpcResponseTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deserializeJsonRpcResponse(){
|
||||||
|
String text = "{\"id\":4,\"jsonrpc\":\"2.0\",\"result\":[{\"id\":\"2.1.0\",\"head_block_number\":30071833,\"head_block_id\":\"01cadc1964cb04ab551463e26033ab0f159bc8e1\",\"time\":\"2018-08-30T18:19:42\",\"current_witness\":\"1.6.71\",\"next_maintenance_time\":\"2018-08-30T19:00:00\",\"last_budget_time\":\"2018-08-30T18:00:00\",\"witness_budget\":80900000,\"accounts_registered_this_interval\":9,\"recently_missed_count\":0,\"current_aslot\":30228262,\"recent_slots_filled\":\"340282366920938463463374607431768211455\",\"dynamic_flags\":0,\"last_irreversible_block_num\":30071813}]}";
|
||||||
|
Gson gson = new Gson();
|
||||||
|
JsonRpcResponse<?> response = gson.fromJson(text, JsonRpcResponse.class);
|
||||||
|
System.out.println("response: "+response.result);
|
||||||
|
Assert.assertNotNull(response);
|
||||||
|
Assert.assertNotNull(response.result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,11 +12,10 @@
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme">
|
||||||
<activity android:name=".SecondActivity" />
|
<activity android:name=".SubscriptionActivity" />
|
||||||
<activity android:name=".CallsActivity">
|
<activity android:name=".CallsActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
|
@ -16,6 +16,7 @@ import butterknife.ButterKnife;
|
||||||
import cy.agorise.graphenej.RPC;
|
import cy.agorise.graphenej.RPC;
|
||||||
|
|
||||||
public class CallsActivity extends AppCompatActivity {
|
public class CallsActivity extends AppCompatActivity {
|
||||||
|
private final String TAG = this.getClass().getName();
|
||||||
|
|
||||||
@BindView(R.id.call_list)
|
@BindView(R.id.call_list)
|
||||||
RecyclerView mRecyclerView;
|
RecyclerView mRecyclerView;
|
||||||
|
@ -45,7 +46,8 @@ public class CallsActivity extends AppCompatActivity {
|
||||||
RPC.CALL_LOOKUP_ASSET_SYMBOLS,
|
RPC.CALL_LOOKUP_ASSET_SYMBOLS,
|
||||||
RPC.CALL_LIST_ASSETS,
|
RPC.CALL_LIST_ASSETS,
|
||||||
RPC.CALL_GET_ACCOUNT_BY_NAME,
|
RPC.CALL_GET_ACCOUNT_BY_NAME,
|
||||||
RPC.CALL_GET_LIMIT_ORDERS
|
RPC.CALL_GET_LIMIT_ORDERS,
|
||||||
|
RPC.CALL_SET_SUBSCRIBE_CALLBACK
|
||||||
};
|
};
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@ -62,9 +64,14 @@ public class CallsActivity extends AppCompatActivity {
|
||||||
String formattedName = name.replace("_", " ").toUpperCase();
|
String formattedName = name.replace("_", " ").toUpperCase();
|
||||||
holder.mCallNameView.setText(formattedName);
|
holder.mCallNameView.setText(formattedName);
|
||||||
holder.mCallNameView.setOnClickListener((view) -> {
|
holder.mCallNameView.setOnClickListener((view) -> {
|
||||||
Intent intent = new Intent(CallsActivity.this, PerformCallActivity.class);
|
String selectedCall = supportedCalls[position];
|
||||||
String selectedCall = ((TextView)view).getText().toString().replace(" ", "_").toLowerCase();
|
Intent intent;
|
||||||
|
if(selectedCall.equals(RPC.CALL_SET_SUBSCRIBE_CALLBACK)){
|
||||||
|
intent = new Intent(CallsActivity.this, SubscriptionActivity.class);
|
||||||
|
}else{
|
||||||
|
intent = new Intent(CallsActivity.this, PerformCallActivity.class);
|
||||||
intent.putExtra(Constants.KEY_SELECTED_CALL, selectedCall);
|
intent.putExtra(Constants.KEY_SELECTED_CALL, selectedCall);
|
||||||
|
}
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,9 @@ public class SampleApplication extends Application {
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.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
|
// Specifying some important information regarding the connection, such as the
|
||||||
// credentials and the requested API accesses
|
// credentials and the requested API accesses
|
||||||
int requestedApis = ApiAccess.API_DATABASE | ApiAccess.API_HISTORY | ApiAccess.API_NETWORK_BROADCAST;
|
int requestedApis = ApiAccess.API_DATABASE | ApiAccess.API_HISTORY | ApiAccess.API_NETWORK_BROADCAST;
|
||||||
|
@ -26,6 +29,7 @@ public class SampleApplication extends Application {
|
||||||
.putString(NetworkService.KEY_USERNAME, "nelson")
|
.putString(NetworkService.KEY_USERNAME, "nelson")
|
||||||
.putString(NetworkService.KEY_PASSWORD, "secret")
|
.putString(NetworkService.KEY_PASSWORD, "secret")
|
||||||
.putInt(NetworkService.KEY_REQUESTED_APIS, requestedApis)
|
.putInt(NetworkService.KEY_REQUESTED_APIS, requestedApis)
|
||||||
|
// .putString(NetworkService.KEY_CUSTOM_NODE_URLS, customNodes)
|
||||||
.apply();
|
.apply();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -11,30 +11,19 @@ import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.google.common.primitives.UnsignedLong;
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
import butterknife.OnClick;
|
import butterknife.OnClick;
|
||||||
import cy.agorise.graphenej.Asset;
|
|
||||||
import cy.agorise.graphenej.AssetAmount;
|
|
||||||
import cy.agorise.graphenej.BaseOperation;
|
|
||||||
import cy.agorise.graphenej.UserAccount;
|
|
||||||
import cy.agorise.graphenej.api.android.NetworkService;
|
import cy.agorise.graphenej.api.android.NetworkService;
|
||||||
import cy.agorise.graphenej.api.android.RxBus;
|
import cy.agorise.graphenej.api.android.RxBus;
|
||||||
import cy.agorise.graphenej.api.calls.GetRequiredFees;
|
import cy.agorise.graphenej.api.calls.CancelAllSubscriptions;
|
||||||
import cy.agorise.graphenej.models.JsonRpcResponse;
|
import cy.agorise.graphenej.api.calls.SetSubscribeCallback;
|
||||||
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
|
import cy.agorise.graphenej.models.JsonRpcNotification;
|
||||||
import cy.agorise.graphenej.operations.TransferOperation;
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.disposables.Disposable;
|
import io.reactivex.disposables.Disposable;
|
||||||
import io.reactivex.functions.Consumer;
|
import io.reactivex.functions.Consumer;
|
||||||
|
|
||||||
public class SecondActivity extends AppCompatActivity {
|
public class SubscriptionActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private final String TAG = this.getClass().getName();
|
private final String TAG = this.getClass().getName();
|
||||||
|
|
||||||
|
@ -44,15 +33,15 @@ public class SecondActivity extends AppCompatActivity {
|
||||||
// In case we want to interact directly with the service
|
// In case we want to interact directly with the service
|
||||||
private NetworkService mService;
|
private NetworkService mService;
|
||||||
|
|
||||||
private Gson gson = new Gson();
|
|
||||||
|
|
||||||
private Disposable mDisposable;
|
private Disposable mDisposable;
|
||||||
|
|
||||||
|
// Notification counter
|
||||||
|
private int counter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_second);
|
setContentView(R.layout.activity_second);
|
||||||
Log.d(TAG,"onCreate");
|
|
||||||
|
|
||||||
ButterKnife.bind(this);
|
ButterKnife.bind(this);
|
||||||
|
|
||||||
|
@ -66,8 +55,9 @@ public class SecondActivity extends AppCompatActivity {
|
||||||
if(message instanceof String){
|
if(message instanceof String){
|
||||||
Log.d(TAG,"Got text message: "+(message));
|
Log.d(TAG,"Got text message: "+(message));
|
||||||
mTextField.setText(mTextField.getText() + ((String) message) + "\n");
|
mTextField.setText(mTextField.getText() + ((String) message) + "\n");
|
||||||
}else if(message instanceof JsonRpcResponse){
|
}else if(message instanceof JsonRpcNotification){
|
||||||
mTextField.setText(mTextField.getText() + gson.toJson(message, JsonRpcResponse.class) + "\n");
|
counter++;
|
||||||
|
mTextField.setText(String.format("Got %d notifications so far", counter));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -111,49 +101,13 @@ public class SecondActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@OnClick(R.id.transfer_fee_usd)
|
@OnClick(R.id.subscribe)
|
||||||
public void onTransferFeeUsdClicked(View v){
|
public void onTransferFeeUsdClicked(View v){
|
||||||
List<BaseOperation> operations = getTransferOperation();
|
mService.sendMessage(new SetSubscribeCallback(true), SetSubscribeCallback.REQUIRED_API);
|
||||||
mService.sendMessage(new GetRequiredFees(operations, new Asset("1.3.121")), GetRequiredFees.REQUIRED_API);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnClick(R.id.transfer_fee_bts)
|
@OnClick(R.id.unsubscribe)
|
||||||
public void onTransferFeeBtsClicked(View v){
|
public void onTransferFeeBtsClicked(View v){
|
||||||
List<BaseOperation> operations = getTransferOperation();
|
mService.sendMessage(new CancelAllSubscriptions(), CancelAllSubscriptions.REQUIRED_API);
|
||||||
mService.sendMessage(new GetRequiredFees(operations, new Asset("1.3.0")), GetRequiredFees.REQUIRED_API);
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnClick(R.id.exchange_fee_usd)
|
|
||||||
public void onExchangeFeeUsdClicked(View v){
|
|
||||||
List<BaseOperation> operations = getExchangeOperation();
|
|
||||||
mService.sendMessage(new GetRequiredFees(operations, new Asset("1.3.121")), GetRequiredFees.REQUIRED_API);
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnClick(R.id.exchange_fee_bts)
|
|
||||||
public void onExchangeFeeBtsClicked(View v){
|
|
||||||
List<BaseOperation> operations = getExchangeOperation();
|
|
||||||
mService.sendMessage(new GetRequiredFees(operations, new Asset("1.3.0")), GetRequiredFees.REQUIRED_API);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<BaseOperation> getTransferOperation(){
|
|
||||||
TransferOperation transferOperation = new TransferOperation(
|
|
||||||
new UserAccount("1.2.138632"),
|
|
||||||
new UserAccount("1.2.129848"),
|
|
||||||
new AssetAmount(UnsignedLong.ONE, new Asset("1.3.0")));
|
|
||||||
ArrayList<BaseOperation> operations = new ArrayList();
|
|
||||||
operations.add(transferOperation);
|
|
||||||
return operations;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<BaseOperation> getExchangeOperation() {
|
|
||||||
LimitOrderCreateOperation operation = new LimitOrderCreateOperation(
|
|
||||||
new UserAccount("1.2.138632"),
|
|
||||||
new AssetAmount(UnsignedLong.valueOf(10000), new Asset("1.3.0")),
|
|
||||||
new AssetAmount(UnsignedLong.valueOf(10), new Asset("1.3.121")),
|
|
||||||
1000000,
|
|
||||||
true);
|
|
||||||
ArrayList<BaseOperation> operations = new ArrayList();
|
|
||||||
operations.add(operation);
|
|
||||||
return operations;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,7 +4,7 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context="cy.agorise.labs.sample.SecondActivity">
|
tools:context="cy.agorise.labs.sample.SubscriptionActivity">
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text_field"
|
android:id="@+id/text_field"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
@ -18,35 +18,21 @@
|
||||||
android:id="@+id/buttons_container"
|
android:id="@+id/buttons_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:weightSum="4"
|
android:weightSum="2"
|
||||||
app:layout_constraintBottom_toBottomOf="parent">
|
app:layout_constraintBottom_toBottomOf="parent">
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/transfer_fee_usd"
|
android:id="@+id/subscribe"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:textSize="11sp"
|
android:textSize="11sp"
|
||||||
android:text="Transfer (USD)"/>
|
android:text="Subscribe"/>
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/transfer_fee_bts"
|
android:id="@+id/unsubscribe"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:textSize="11sp"
|
android:textSize="11sp"
|
||||||
android:text="Transfer (BTS)"/>
|
android:text="Unsubscribe"/>
|
||||||
<Button
|
|
||||||
android:id="@+id/exchange_fee_usd"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:textSize="11sp"
|
|
||||||
android:text="Exchange (USD)"/>
|
|
||||||
<Button
|
|
||||||
android:id="@+id/exchange_fee_bts"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:textSize="11sp"
|
|
||||||
android:text="Exchange (BTS)"/>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</android.support.constraint.ConstraintLayout>
|
</android.support.constraint.ConstraintLayout>
|
||||||
|
|
Loading…
Reference in a new issue