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 com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
@ -22,6 +23,8 @@ import cy.agorise.graphenej.Asset;
|
|||
import cy.agorise.graphenej.AssetAmount;
|
||||
import cy.agorise.graphenej.LimitOrder;
|
||||
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.ConnectionStatusUpdate;
|
||||
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.BlockHeader;
|
||||
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.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 okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
|
@ -95,7 +104,18 @@ public class NetworkService extends Service {
|
|||
|
||||
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
|
||||
// one of two required mappings. The second one is implemented by the DeserializationMap
|
||||
|
@ -287,10 +307,32 @@ public class NetworkService extends Service {
|
|||
mLastCall = "";
|
||||
}
|
||||
}
|
||||
// Properly de-serialize all other fields and broadcasts to the event bus
|
||||
handleJsonRpcResponse(response, text);
|
||||
}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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
Class requestClass = mRequestClassMap.get(response.id);
|
||||
|
@ -355,6 +397,15 @@ public class NetworkService extends Service {
|
|||
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
|
||||
* 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
|
||||
* SubscriptionResponseDeserializer.
|
||||
*
|
||||
* Created by nelson on 1/12/17.
|
||||
*/
|
||||
public class SubscriptionResponse {
|
||||
private static final String TAG = "SubscriptionResponse";
|
||||
public static final String KEY_ID = "id";
|
||||
public static final String KEY_METHOD = "method";
|
||||
public static final String KEY_PARAMS = "params";
|
||||
|
||||
public int id;
|
||||
public String method;
|
||||
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:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name=".SecondActivity" />
|
||||
<activity android:name=".SubscriptionActivity" />
|
||||
<activity android:name=".CallsActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
|
|
@ -16,6 +16,7 @@ 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;
|
||||
|
@ -45,7 +46,8 @@ public class CallsActivity extends AppCompatActivity {
|
|||
RPC.CALL_LOOKUP_ASSET_SYMBOLS,
|
||||
RPC.CALL_LIST_ASSETS,
|
||||
RPC.CALL_GET_ACCOUNT_BY_NAME,
|
||||
RPC.CALL_GET_LIMIT_ORDERS
|
||||
RPC.CALL_GET_LIMIT_ORDERS,
|
||||
RPC.CALL_SET_SUBSCRIBE_CALLBACK
|
||||
};
|
||||
|
||||
@NonNull
|
||||
|
@ -62,9 +64,14 @@ public class CallsActivity extends AppCompatActivity {
|
|||
String formattedName = name.replace("_", " ").toUpperCase();
|
||||
holder.mCallNameView.setText(formattedName);
|
||||
holder.mCallNameView.setOnClickListener((view) -> {
|
||||
Intent intent = new Intent(CallsActivity.this, PerformCallActivity.class);
|
||||
String selectedCall = ((TextView)view).getText().toString().replace(" ", "_").toLowerCase();
|
||||
String selectedCall = supportedCalls[position];
|
||||
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);
|
||||
}
|
||||
startActivity(intent);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -18,6 +18,9 @@ public class SampleApplication extends Application {
|
|||
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;
|
||||
|
@ -26,6 +29,7 @@ public class SampleApplication extends Application {
|
|||
.putString(NetworkService.KEY_USERNAME, "nelson")
|
||||
.putString(NetworkService.KEY_PASSWORD, "secret")
|
||||
.putInt(NetworkService.KEY_REQUESTED_APIS, requestedApis)
|
||||
// .putString(NetworkService.KEY_CUSTOM_NODE_URLS, customNodes)
|
||||
.apply();
|
||||
|
||||
/*
|
||||
|
|
|
@ -11,30 +11,19 @@ import android.util.Log;
|
|||
import android.view.View;
|
||||
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.ButterKnife;
|
||||
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.RxBus;
|
||||
import cy.agorise.graphenej.api.calls.GetRequiredFees;
|
||||
import cy.agorise.graphenej.models.JsonRpcResponse;
|
||||
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
|
||||
import cy.agorise.graphenej.operations.TransferOperation;
|
||||
import cy.agorise.graphenej.api.calls.CancelAllSubscriptions;
|
||||
import cy.agorise.graphenej.api.calls.SetSubscribeCallback;
|
||||
import cy.agorise.graphenej.models.JsonRpcNotification;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.functions.Consumer;
|
||||
|
||||
public class SecondActivity extends AppCompatActivity {
|
||||
public class SubscriptionActivity extends AppCompatActivity {
|
||||
|
||||
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
|
||||
private NetworkService mService;
|
||||
|
||||
private Gson gson = new Gson();
|
||||
|
||||
private Disposable mDisposable;
|
||||
|
||||
// Notification counter
|
||||
private int counter;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_second);
|
||||
Log.d(TAG,"onCreate");
|
||||
|
||||
ButterKnife.bind(this);
|
||||
|
||||
|
@ -66,8 +55,9 @@ public class SecondActivity extends AppCompatActivity {
|
|||
if(message instanceof String){
|
||||
Log.d(TAG,"Got text message: "+(message));
|
||||
mTextField.setText(mTextField.getText() + ((String) message) + "\n");
|
||||
}else if(message instanceof JsonRpcResponse){
|
||||
mTextField.setText(mTextField.getText() + gson.toJson(message, JsonRpcResponse.class) + "\n");
|
||||
}else if(message instanceof JsonRpcNotification){
|
||||
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){
|
||||
List<BaseOperation> operations = getTransferOperation();
|
||||
mService.sendMessage(new GetRequiredFees(operations, new Asset("1.3.121")), GetRequiredFees.REQUIRED_API);
|
||||
mService.sendMessage(new SetSubscribeCallback(true), SetSubscribeCallback.REQUIRED_API);
|
||||
}
|
||||
|
||||
@OnClick(R.id.transfer_fee_bts)
|
||||
@OnClick(R.id.unsubscribe)
|
||||
public void onTransferFeeBtsClicked(View v){
|
||||
List<BaseOperation> operations = getTransferOperation();
|
||||
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;
|
||||
mService.sendMessage(new CancelAllSubscriptions(), CancelAllSubscriptions.REQUIRED_API);
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="cy.agorise.labs.sample.SecondActivity">
|
||||
tools:context="cy.agorise.labs.sample.SubscriptionActivity">
|
||||
<TextView
|
||||
android:id="@+id/text_field"
|
||||
android:layout_width="0dp"
|
||||
|
@ -18,35 +18,21 @@
|
|||
android:id="@+id/buttons_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:weightSum="4"
|
||||
android:weightSum="2"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
<Button
|
||||
android:id="@+id/transfer_fee_usd"
|
||||
android:id="@+id/subscribe"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textSize="11sp"
|
||||
android:text="Transfer (USD)"/>
|
||||
android:text="Subscribe"/>
|
||||
<Button
|
||||
android:id="@+id/transfer_fee_bts"
|
||||
android:id="@+id/unsubscribe"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textSize="11sp"
|
||||
android:text="Transfer (BTS)"/>
|
||||
<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)"/>
|
||||
android:text="Unsubscribe"/>
|
||||
</LinearLayout>
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
|
Loading…
Reference in a new issue