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 58f69d1..cc485d1 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 @@ -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 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{ - Log.w(TAG,"Error.Msg: "+response.error.message); + // 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. diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/calls/CancelAllSubscriptions.java b/graphenej/src/main/java/cy/agorise/graphenej/api/calls/CancelAllSubscriptions.java new file mode 100644 index 0000000..9ebc831 --- /dev/null +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/calls/CancelAllSubscriptions.java @@ -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(), RPC.VERSION, sequenceId); + } +} diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/calls/SetSubscribeCallback.java b/graphenej/src/main/java/cy/agorise/graphenej/api/calls/SetSubscribeCallback.java new file mode 100644 index 0000000..66472d7 --- /dev/null +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/calls/SetSubscribeCallback.java @@ -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 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); + } +} diff --git a/graphenej/src/main/java/cy/agorise/graphenej/models/JsonRpcNotification.java b/graphenej/src/main/java/cy/agorise/graphenej/models/JsonRpcNotification.java new file mode 100644 index 0000000..dfb5632 --- /dev/null +++ b/graphenej/src/main/java/cy/agorise/graphenej/models/JsonRpcNotification.java @@ -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 params; + + /** + * Inner static class used to parse and deserialize subscription notifications. + */ + public static class JsonRpcNotificationDeserializer implements JsonDeserializer { + + @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 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; + } + } +} diff --git a/graphenej/src/main/java/cy/agorise/graphenej/models/SubscriptionResponse.java b/graphenej/src/main/java/cy/agorise/graphenej/models/SubscriptionResponse.java index cf048b0..700b36e 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/models/SubscriptionResponse.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/models/SubscriptionResponse.java @@ -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 params; diff --git a/graphenej/src/test/java/cy/agorise/graphenej/models/JsonRpcNotificationTest.java b/graphenej/src/test/java/cy/agorise/graphenej/models/JsonRpcNotificationTest.java new file mode 100644 index 0000000..cef2d29 --- /dev/null +++ b/graphenej/src/test/java/cy/agorise/graphenej/models/JsonRpcNotificationTest.java @@ -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 secondArgument = (ArrayList) 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); + } +} diff --git a/graphenej/src/test/java/cy/agorise/graphenej/models/JsonRpcResponseTest.java b/graphenej/src/test/java/cy/agorise/graphenej/models/JsonRpcResponseTest.java new file mode 100644 index 0000000..335fd89 --- /dev/null +++ b/graphenej/src/test/java/cy/agorise/graphenej/models/JsonRpcResponseTest.java @@ -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); + } +} diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index c0e82b0..de61cce 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -12,11 +12,10 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> - + - diff --git a/sample/src/main/java/cy/agorise/labs/sample/CallsActivity.java b/sample/src/main/java/cy/agorise/labs/sample/CallsActivity.java index 37df8fa..6ccf3ef 100644 --- a/sample/src/main/java/cy/agorise/labs/sample/CallsActivity.java +++ b/sample/src/main/java/cy/agorise/labs/sample/CallsActivity.java @@ -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(); - intent.putExtra(Constants.KEY_SELECTED_CALL, selectedCall); + 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); }); } 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 bb45d70..ba6a0d1 100644 --- a/sample/src/main/java/cy/agorise/labs/sample/SampleApplication.java +++ b/sample/src/main/java/cy/agorise/labs/sample/SampleApplication.java @@ -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(); /* diff --git a/sample/src/main/java/cy/agorise/labs/sample/SecondActivity.java b/sample/src/main/java/cy/agorise/labs/sample/SubscriptionActivity.java similarity index 52% rename from sample/src/main/java/cy/agorise/labs/sample/SecondActivity.java rename to sample/src/main/java/cy/agorise/labs/sample/SubscriptionActivity.java index 224200d..a6b7daa 100644 --- a/sample/src/main/java/cy/agorise/labs/sample/SecondActivity.java +++ b/sample/src/main/java/cy/agorise/labs/sample/SubscriptionActivity.java @@ -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 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 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 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 operations = getExchangeOperation(); - mService.sendMessage(new GetRequiredFees(operations, new Asset("1.3.0")), GetRequiredFees.REQUIRED_API); - } - - private List 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 operations = new ArrayList(); - operations.add(transferOperation); - return operations; - } - - public List 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 operations = new ArrayList(); - operations.add(operation); - return operations; + mService.sendMessage(new CancelAllSubscriptions(), CancelAllSubscriptions.REQUIRED_API); } } diff --git a/sample/src/main/res/layout/activity_second.xml b/sample/src/main/res/layout/activity_second.xml index a49aa4d..1965533 100644 --- a/sample/src/main/res/layout/activity_second.xml +++ b/sample/src/main/res/layout/activity_second.xml @@ -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">