diff --git a/graphenej/build.gradle b/graphenej/build.gradle index fe19180..ee89cb7 100644 --- a/graphenej/build.gradle +++ b/graphenej/build.gradle @@ -33,8 +33,8 @@ dependencies { implementation group: "org.tukaani", name: "xz", version: "1.6" // Rx dependencies - implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' - implementation 'io.reactivex.rxjava2:rxjava:2.1.9' - implementation 'com.jakewharton.rxrelay2:rxrelay:2.0.0' - implementation 'com.squareup.okhttp3:okhttp:3.5.0' + api 'io.reactivex.rxjava2:rxandroid:2.0.2' + api 'io.reactivex.rxjava2:rxjava:2.1.16' + api 'com.jakewharton.rxrelay2:rxrelay:2.0.0' + api 'com.squareup.okhttp3:okhttp:3.5.0' } \ No newline at end of file diff --git a/graphenej/src/main/java/cy/agorise/graphenej/RPC.java b/graphenej/src/main/java/cy/agorise/graphenej/RPC.java index ea6c509..d16660f 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/RPC.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/RPC.java @@ -19,9 +19,10 @@ public class RPC { public static final String CALL_GET_REQUIRED_FEES = "get_required_fees"; public static final String CALL_GET_KEY_REFERENCES = "get_key_references"; public static final String CALL_GET_RELATIVE_ACCOUNT_HISTORY = "get_relative_account_history"; + public static final String CALL_GET_ACCOUNT_HISTORY = "get_account_history"; public static final String CALL_LOOKUP_ACCOUNTS = "lookup_accounts"; public static final String CALL_LIST_ASSETS = "list_assets"; - public static final String GET_OBJECTS = "get_objects"; + public static final String CALL_GET_OBJECTS = "get_objects"; public static final String GET_ACCOUNT_BALANCES = "get_account_balances"; public static final String CALL_LOOKUP_ASSET_SYMBOLS = "lookup_asset_symbols"; public static final String CALL_GET_BLOCK_HEADER = "get_block_header"; diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/GetObjects.java b/graphenej/src/main/java/cy/agorise/graphenej/api/GetObjects.java index 91d83be..e9c0c4f 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/api/GetObjects.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/GetObjects.java @@ -75,11 +75,9 @@ public class GetObjects extends BaseGrapheneHandler { public void onConnected(WebSocket websocket, Map> headers) throws Exception { ArrayList params = new ArrayList<>(); ArrayList subParams = new ArrayList<>(); - for(String id : this.ids){ - subParams.add(id); - } + subParams.addAll(this.ids); params.add(subParams); - ApiCall apiCall = new ApiCall(0, RPC.GET_OBJECTS, params, RPC.VERSION, 0); + ApiCall apiCall = new ApiCall(0, RPC.CALL_GET_OBJECTS, params, RPC.VERSION, 0); websocket.sendText(apiCall.toJsonString()); } diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/SubscriptionMessagesHub.java b/graphenej/src/main/java/cy/agorise/graphenej/api/SubscriptionMessagesHub.java index a0c533e..de55c28 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/api/SubscriptionMessagesHub.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/SubscriptionMessagesHub.java @@ -188,7 +188,7 @@ public class SubscriptionMessagesHub extends BaseGrapheneHandler implements Subs } payload.add(objects); - ApiCall subscribe = new ApiCall(databaseApiId, RPC.GET_OBJECTS, payload, RPC.VERSION, MANUAL_SUBSCRIPTION_ID); + ApiCall subscribe = new ApiCall(databaseApiId, RPC.CALL_GET_OBJECTS, payload, RPC.VERSION, MANUAL_SUBSCRIPTION_ID); websocket.sendText(subscribe.toJsonString()); subscriptionCounter++; }else{ diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/android/DeserializationMap.java b/graphenej/src/main/java/cy/agorise/graphenej/api/android/DeserializationMap.java index f3726df..e5fbf02 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/api/android/DeserializationMap.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/android/DeserializationMap.java @@ -18,6 +18,7 @@ import cy.agorise.graphenej.api.calls.GetAccounts; import cy.agorise.graphenej.api.calls.GetBlock; import cy.agorise.graphenej.api.calls.GetBlockHeader; import cy.agorise.graphenej.api.calls.GetMarketHistory; +import cy.agorise.graphenej.api.calls.GetObjects; import cy.agorise.graphenej.api.calls.GetRelativeAccountHistory; import cy.agorise.graphenej.api.calls.GetRequiredFees; import cy.agorise.graphenej.api.calls.LookupAssetSymbols; @@ -101,6 +102,13 @@ public class DeserializationMap { .registerTypeAdapter(Asset.class, new Asset.AssetDeserializer()) .create(); mGsonMap.put(LookupAssetSymbols.class, lookupAssetSymbolGson); + + // GetObjects + mClassMap.put(GetObjects.class, List.class); + Gson getObjectsGson = new GsonBuilder() + .registerTypeAdapter(Asset.class, new Asset.AssetDeserializer()) + .create(); + mGsonMap.put(GetObjects.class, getObjectsGson); } public Class getReceivedClass(Class _class){ 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 4ab7c83..f4ef94d 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 @@ -25,6 +25,7 @@ import cy.agorise.graphenej.api.bitshares.Nodes; import cy.agorise.graphenej.api.calls.ApiCallable; import cy.agorise.graphenej.api.calls.GetAccounts; import cy.agorise.graphenej.api.calls.GetMarketHistory; +import cy.agorise.graphenej.api.calls.GetObjects; import cy.agorise.graphenej.api.calls.GetRelativeAccountHistory; import cy.agorise.graphenej.api.calls.GetRequiredFees; import cy.agorise.graphenej.models.AccountProperties; @@ -314,6 +315,8 @@ public class NetworkService extends Service { }else if(requestClass == GetMarketHistory.class){ Type GetMarketHistoryResponse = new TypeToken>>(){}.getType(); parsedResponse = gson.fromJson(text, GetMarketHistoryResponse); + }else if(requestClass == GetObjects.class){ + parsedResponse = handleGetObject(text); }else{ Log.w(TAG,"Unknown request class"); } @@ -331,6 +334,26 @@ public class NetworkService extends Service { RxBus.getBusInstance().send(parsedResponse); } + /** + * 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. + * + * @param response Response to a 'get_objects' API call + */ + private JsonRpcResponse handleGetObject(String response){ + //TODO: Implement a proper de-serialization logic + return null; + } + + /** + * Method used to check all possible API accesses. + * + * The service will try to obtain sequentially API access ids for the following APIs: + * + * - Database + * - History + * - Network broadcast + */ private void checkNextRequestedApiAccess(){ if( (mRequestedApis & ApiAccess.API_DATABASE) == ApiAccess.API_DATABASE && mApiIds.get(ApiAccess.API_DATABASE) == null){ diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/calls/GetAccountHistory.java b/graphenej/src/main/java/cy/agorise/graphenej/api/calls/GetAccountHistory.java new file mode 100644 index 0000000..0a43ef4 --- /dev/null +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/calls/GetAccountHistory.java @@ -0,0 +1,43 @@ +package cy.agorise.graphenej.api.calls; + +import java.io.Serializable; +import java.util.ArrayList; + +import cy.agorise.graphenej.RPC; +import cy.agorise.graphenej.UserAccount; +import cy.agorise.graphenej.api.ApiAccess; +import cy.agorise.graphenej.models.ApiCall; + +public class GetAccountHistory implements ApiCallable { + public static final int REQUIRED_API = ApiAccess.API_HISTORY; + + private UserAccount mUserAccount; + private String startOperation; + private String endOperation; + private int limit; + + public GetAccountHistory(UserAccount userAccount, String start, String end, int limit){ + this.mUserAccount = userAccount; + this.startOperation = start; + this.endOperation = end; + this.limit = limit; + } + + public GetAccountHistory(String userId, String start, String end, int limit){ + this.mUserAccount = new UserAccount(userId); + this.startOperation = start; + this.endOperation = end; + this.limit = limit; + } + + + @Override + public ApiCall toApiCall(int apiId, long sequenceId) { + ArrayList params = new ArrayList<>(); + params.add(mUserAccount.getObjectId()); + params.add(endOperation); + params.add(limit); + params.add(startOperation); + return new ApiCall(apiId, RPC.CALL_GET_ACCOUNT_HISTORY, params, RPC.VERSION, sequenceId); + } +} diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/calls/GetObjects.java b/graphenej/src/main/java/cy/agorise/graphenej/api/calls/GetObjects.java new file mode 100644 index 0000000..d6661d3 --- /dev/null +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/calls/GetObjects.java @@ -0,0 +1,29 @@ +package cy.agorise.graphenej.api.calls; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import cy.agorise.graphenej.RPC; +import cy.agorise.graphenej.api.ApiAccess; +import cy.agorise.graphenej.models.ApiCall; + +/** + * Wrapper around the "get_objects" API call. + */ +public class GetObjects implements ApiCallable { + public static final int REQUIRED_API = ApiAccess.API_DATABASE; + private List ids; + + public GetObjects(List ids){ + this.ids = ids; + } + + @Override + public ApiCall toApiCall(int apiId, long sequenceId) { + ArrayList params = new ArrayList<>(); + ArrayList subParams = new ArrayList<>(ids); + params.add(subParams); + return new ApiCall(apiId, RPC.CALL_GET_OBJECTS, params, RPC.VERSION, sequenceId); + } +} diff --git a/graphenej/src/test/java/cy/agorise/graphenej/api/calls/GetAccountHistoryTest.java b/graphenej/src/test/java/cy/agorise/graphenej/api/calls/GetAccountHistoryTest.java new file mode 100644 index 0000000..f14b7f6 --- /dev/null +++ b/graphenej/src/test/java/cy/agorise/graphenej/api/calls/GetAccountHistoryTest.java @@ -0,0 +1,25 @@ +package cy.agorise.graphenej.api.calls; + +import junit.framework.Assert; + +import org.junit.Test; + +import cy.agorise.graphenej.UserAccount; +import cy.agorise.graphenej.models.ApiCall; + +public class GetAccountHistoryTest { + + @Test + public void testSerialization(){ + UserAccount userAccount = new UserAccount("1.2.139293"); + String end = "1.11.225030218"; + String start = "1.11.225487973"; + int limit = 20; + GetAccountHistory getAccountHistory = new GetAccountHistory(userAccount, start, end, limit); + ApiCall apiCall = getAccountHistory.toApiCall(2, 3); + String serialized = apiCall.toJsonString(); + System.out.println("> "+serialized); + String expected = "{\"id\":3,\"method\":\"call\",\"params\":[2,\"get_account_history\",[\"1.2.139293\",\"1.11.225030218\",20,\"1.11.225487973\"]],\"jsonrpc\":\"2.0\"}"; + Assert.assertEquals("Serialized is as expected", expected, serialized); + } +} diff --git a/sample/build.gradle b/sample/build.gradle index 62f2f52..de76e02 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -1,13 +1,13 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 26 + compileSdkVersion 27 defaultConfig { applicationId "cy.agorise.labs.sample" minSdkVersion 14 - targetSdkVersion 26 + targetSdkVersion 27 versionCode 1 versionName "1.0" @@ -30,8 +30,10 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) api project(':graphenej') - implementation 'com.android.support:appcompat-v7:26.1.0' - implementation 'com.android.support.constraint:constraint-layout:1.0.2' + implementation 'com.android.support:appcompat-v7:27.1.1' + implementation 'com.android.support:recyclerview-v7:27.1.1' + implementation 'com.android.support:design:27.1.1' + implementation 'com.android.support.constraint:constraint-layout:1.1.2' implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1' implementation 'com.jakewharton:butterknife:8.8.1' implementation 'com.google.code.gson:gson:2.8.4' diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index ca0c464..d13297e 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -1,24 +1,27 @@ - + + + - + android:theme="@style/AppTheme"> + + + - - + \ No newline at end of file diff --git a/sample/src/main/java/cy/agorise/labs/sample/CallsActivity.java b/sample/src/main/java/cy/agorise/labs/sample/CallsActivity.java new file mode 100644 index 0000000..40fc28b --- /dev/null +++ b/sample/src/main/java/cy/agorise/labs/sample/CallsActivity.java @@ -0,0 +1,83 @@ +package cy.agorise.labs.sample; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.DividerItemDecoration; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.widget.TextView; + +import butterknife.BindView; +import butterknife.ButterKnife; +import cy.agorise.graphenej.RPC; + +public class CallsActivity extends AppCompatActivity { + + @BindView(R.id.call_list) + RecyclerView mRecyclerView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + 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.setAdapter(new CallAdapter()); + } + + private final class CallAdapter extends RecyclerView.Adapter { + + private String[] supportedCalls = new String[]{ + RPC.CALL_GET_OBJECTS, + RPC.CALL_GET_ACCOUNTS, + RPC.CALL_GET_BLOCK, + RPC.CALL_GET_BLOCK_HEADER, + RPC.CALL_GET_MARKET_HISTORY, + RPC.CALL_GET_RELATIVE_ACCOUNT_HISTORY, + RPC.CALL_GET_REQUIRED_FEES, + RPC.CALL_LOOKUP_ASSET_SYMBOLS + }; + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + TextView v = (TextView) LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_call, parent, false); + return new ViewHolder(v); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + String name = supportedCalls[position]; + 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); + startActivity(intent); + }); + } + + @Override + public int getItemCount() { + return supportedCalls.length; + } + + public class ViewHolder extends RecyclerView.ViewHolder { + public TextView mCallNameView; + + public ViewHolder(TextView view) { + super(view); + this.mCallNameView = view; + } + } + } +} diff --git a/sample/src/main/java/cy/agorise/labs/sample/ConnectedActivity.java b/sample/src/main/java/cy/agorise/labs/sample/ConnectedActivity.java new file mode 100644 index 0000000..b9e6389 --- /dev/null +++ b/sample/src/main/java/cy/agorise/labs/sample/ConnectedActivity.java @@ -0,0 +1,62 @@ +package cy.agorise.labs.sample; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; + +import cy.agorise.graphenej.api.android.NetworkService; + +public abstract class ConnectedActivity extends AppCompatActivity implements ServiceConnection { + private final String TAG = this.getClass().getName(); + + /* Network service connection */ + protected NetworkService mNetworkService; + + /** + * Flag used to keep track of the NetworkService binding state + */ + private boolean mShouldUnbindNetwork; + + private ServiceConnection mNetworkServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName className, + IBinder service) { + // We've bound to LocalService, cast the IBinder and get LocalService instance + NetworkService.LocalBinder binder = (NetworkService.LocalBinder) service; + mNetworkService = binder.getService(); + + ConnectedActivity.this.onServiceConnected(className, service); + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { + ConnectedActivity.this.onServiceDisconnected(componentName); + } + }; + + @Override + protected void onStart() { + super.onStart(); + // Binding to NetworkService + Intent intent = new Intent(this, NetworkService.class); + if(bindService(intent, mNetworkServiceConnection, Context.BIND_AUTO_CREATE)){ + mShouldUnbindNetwork = true; + }else{ + Log.e(TAG,"Binding to the network service failed."); + } + } + + @Override + protected void onPause() { + super.onPause(); + // Unbinding from network service + if(mShouldUnbindNetwork){ + unbindService(mNetworkServiceConnection); + mShouldUnbindNetwork = false; + } + } +} diff --git a/sample/src/main/java/cy/agorise/labs/sample/Constants.java b/sample/src/main/java/cy/agorise/labs/sample/Constants.java new file mode 100644 index 0000000..a77ed93 --- /dev/null +++ b/sample/src/main/java/cy/agorise/labs/sample/Constants.java @@ -0,0 +1,8 @@ +package cy.agorise.labs.sample; + +public class Constants { + /** + * Key used to pass the selected call as an intent extra + */ + public static final String KEY_SELECTED_CALL = "key_call"; +} diff --git a/sample/src/main/java/cy/agorise/labs/sample/PerformCallActivity.java b/sample/src/main/java/cy/agorise/labs/sample/PerformCallActivity.java new file mode 100644 index 0000000..db17e2e --- /dev/null +++ b/sample/src/main/java/cy/agorise/labs/sample/PerformCallActivity.java @@ -0,0 +1,322 @@ +package cy.agorise.labs.sample; + +import android.content.ComponentName; +import android.content.res.Resources; +import android.os.Bundle; +import android.os.IBinder; +import android.support.design.widget.TextInputEditText; +import android.support.design.widget.TextInputLayout; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.util.ArrayList; +import java.util.HashMap; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import cy.agorise.graphenej.RPC; +import cy.agorise.graphenej.UserAccount; +import cy.agorise.graphenej.api.ConnectionStatusUpdate; +import cy.agorise.graphenej.api.android.DeserializationMap; +import cy.agorise.graphenej.api.android.RxBus; +import cy.agorise.graphenej.api.calls.GetAccounts; +import cy.agorise.graphenej.api.calls.GetBlock; +import cy.agorise.graphenej.api.calls.GetObjects; +import cy.agorise.graphenej.models.JsonRpcResponse; +import cy.agorise.graphenej.objects.Memo; +import cy.agorise.graphenej.operations.TransferOperation; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.functions.Consumer; + +public class PerformCallActivity extends ConnectedActivity { + private final String TAG = this.getClass().getName(); + + @BindView(R.id.response) + TextView mResponseView; + + @BindView(R.id.container_param1) + TextInputLayout mParam1View; + + @BindView(R.id.container_param2) + TextInputLayout mParam2View; + + @BindView(R.id.container_param3) + TextInputLayout mParam3View; + + @BindView(R.id.container_param4) + TextInputLayout mParam4View; + + @BindView(R.id.param1) + TextInputEditText param1; + + @BindView(R.id.param2) + TextInputEditText param2; + + @BindView(R.id.param3) + TextInputEditText param3; + + @BindView(R.id.param4) + TextInputEditText param4; + + @BindView(R.id.button_send) + Button mButtonSend; + + // Field used to map a request id to its type + private HashMap responseMap = new HashMap<>(); + + // Current request type. Ex: 'get_objects', 'get_accounts', etc + private String mRPC; + + private Disposable mDisposable; + + private Gson gson = new GsonBuilder() + .setExclusionStrategies(new DeserializationMap.SkipAccountOptionsStrategy(), new DeserializationMap.SkipAssetOptionsStrategy()) + .registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer()) + .registerTypeAdapter(Memo.class, new Memo.MemoSerializer()) + .create(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_perform_call); + ButterKnife.bind(this); + + mRPC = getIntent().getStringExtra(Constants.KEY_SELECTED_CALL); + Log.d(TAG,"Selected call: "+mRPC); + switch (mRPC){ + case RPC.CALL_GET_OBJECTS: + setupGetObjects(); + break; + case RPC.CALL_GET_ACCOUNTS: + setupGetAccounts(); + break; + case RPC.CALL_GET_BLOCK: + setupGetBlock(); + break; + case RPC.CALL_GET_BLOCK_HEADER: + setupGetBlockHeader(); + break; + case RPC.CALL_GET_MARKET_HISTORY: + setupGetMarketHistory(); + break; + case RPC.CALL_GET_RELATIVE_ACCOUNT_HISTORY: + setupGetRelativeAccountHistory(); + break; + case RPC.CALL_GET_REQUIRED_FEES: + break; + case RPC.CALL_LOOKUP_ASSET_SYMBOLS: + setupLookupAssetSymbols(); + break; + default: + Log.d(TAG,"Default called"); + } + + mDisposable = RxBus.getBusInstance() + .asFlowable() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Consumer() { + + @Override + public void accept(Object message) throws Exception { + Log.d(TAG,"accept. Msg class: "+message.getClass()); + if(message instanceof ConnectionStatusUpdate){ + // TODO: Update UI ? + }else if(message instanceof JsonRpcResponse){ + handleJsonRpcResponse((JsonRpcResponse) message); + } + } + }); + } + + private void setupGetObjects(){ + requiredInput(1); + mParam1View.setHint(getResources().getString(R.string.get_objects_arg1)); + } + + private void setupGetAccounts(){ + requiredInput(1); + mParam1View.setHint(getResources().getString(R.string.get_accounts_arg1)); + } + + private void setupGetBlock(){ + requiredInput(1); + mParam1View.setHint(getResources().getString(R.string.get_block_arg1)); + } + + private void setupGetBlockHeader(){ + requiredInput(1); + mParam1View.setHint(getResources().getString(R.string.get_block_arg1)); + } + + private void setupGetMarketHistory(){ + requiredInput(4); + Resources resources = getResources(); + mParam1View.setHint(resources.getString(R.string.get_market_history_arg1)); + mParam2View.setHint(resources.getString(R.string.get_market_history_arg2)); + mParam3View.setHint(resources.getString(R.string.get_market_history_arg3)); + mParam4View.setHint(resources.getString(R.string.get_market_history_arg4)); + } + + private void setupGetRelativeAccountHistory(){ + requiredInput(4); + Resources resources = getResources(); + mParam1View.setHint(resources.getString(R.string.get_relative_account_history_arg1)); + mParam2View.setHint(resources.getString(R.string.get_relative_account_history_arg2)); + mParam3View.setHint(resources.getString(R.string.get_relative_account_history_arg3)); + mParam4View.setHint(resources.getString(R.string.get_relative_account_history_arg4)); + } + + private void setupLookupAssetSymbols(){ + requiredInput(4); + Resources resources = getResources(); + mParam1View.setHint(resources.getString(R.string.lookup_asset_symbols_arg1)); + mParam2View.setHint(resources.getString(R.string.lookup_asset_symbols_arg2)); + mParam3View.setHint(resources.getString(R.string.lookup_asset_symbols_arg3)); + mParam4View.setHint(resources.getString(R.string.lookup_asset_symbols_arg4)); + } + + private void requiredInput(int inputCount){ + if(inputCount == 1){ + mParam1View.setVisibility(View.VISIBLE); + mParam2View.setVisibility(View.GONE); + mParam3View.setVisibility(View.GONE); + mParam4View.setVisibility(View.GONE); + }else if(inputCount == 2){ + mParam1View.setVisibility(View.VISIBLE); + mParam2View.setVisibility(View.VISIBLE); + mParam3View.setVisibility(View.GONE); + mParam4View.setVisibility(View.GONE); + }else if(inputCount == 3){ + mParam1View.setVisibility(View.VISIBLE); + mParam2View.setVisibility(View.VISIBLE); + mParam3View.setVisibility(View.VISIBLE); + mParam4View.setVisibility(View.GONE); + }else if(inputCount == 4){ + mParam1View.setVisibility(View.VISIBLE); + mParam2View.setVisibility(View.VISIBLE); + mParam3View.setVisibility(View.VISIBLE); + mParam4View.setVisibility(View.VISIBLE); + } + } + + @OnClick(R.id.button_send) + public void onSendClicked(Button v){ + switch (mRPC){ + case RPC.CALL_GET_OBJECTS: + sendGetObjectsRequest(); + break; + case RPC.CALL_GET_ACCOUNTS: + sendGetAccountsRequest(); + break; + case RPC.CALL_GET_BLOCK: + break; + case RPC.CALL_GET_BLOCK_HEADER: + break; + case RPC.CALL_GET_MARKET_HISTORY: + break; + case RPC.CALL_GET_RELATIVE_ACCOUNT_HISTORY: + break; + case RPC.CALL_GET_REQUIRED_FEES: + break; + case RPC.CALL_LOOKUP_ASSET_SYMBOLS: + break; + default: + Log.d(TAG,"Default called"); + } + } + + private void sendGetObjectsRequest(){ + String objectId = param1.getText().toString(); + if(objectId.matches("\\d\\.\\d{1,3}\\.\\d{1,10}")){ + ArrayList array = new ArrayList<>(); + array.add(objectId); + GetObjects getObjects = new GetObjects(array); + long id = mNetworkService.sendMessage(getObjects, GetObjects.REQUIRED_API); + responseMap.put(id, mRPC); + }else{ + param1.setError(getResources().getString(R.string.error_input_id)); + } + } + + private void sendGetAccountsRequest(){ + String userId = param1.getText().toString(); + if(userId.matches("\\d\\.\\d{1,3}\\.\\d{1,10}")){ + GetAccounts getAccounts = new GetAccounts(new UserAccount(userId)); + long id = mNetworkService.sendMessage(getAccounts, GetBlock.REQUIRED_API); + responseMap.put(id, mRPC); + }else{ + param1.setError(getResources().getString(R.string.error_input_id)); + } + } + + /** + * Internal method that will decide what to do with each JSON-RPC response + * + * @param response The JSON-RPC api call response + */ + private void handleJsonRpcResponse(JsonRpcResponse response){ + long id = response.id; + if(responseMap.get(id) != null){ + String request = responseMap.get(id); + switch(request){ + case RPC.CALL_GET_ACCOUNTS: + mResponseView.setText(mResponseView.getText() + gson.toJson(response, JsonRpcResponse.class) + "\n"); + break; + case RPC.CALL_GET_BLOCK: + mResponseView.setText(mResponseView.getText() + gson.toJson(response, JsonRpcResponse.class) + "\n"); + break; + case RPC.CALL_GET_BLOCK_HEADER: + mResponseView.setText(mResponseView.getText() + gson.toJson(response, JsonRpcResponse.class) + "\n"); + break; + case RPC.CALL_GET_MARKET_HISTORY: + mResponseView.setText(mResponseView.getText() + gson.toJson(response, JsonRpcResponse.class) + "\n"); + break; + case RPC.CALL_GET_ACCOUNT_HISTORY: + mResponseView.setText(mResponseView.getText() + gson.toJson(response, JsonRpcResponse.class) + "\n"); + break; + case RPC.CALL_GET_RELATIVE_ACCOUNT_HISTORY: + mResponseView.setText(mResponseView.getText() + gson.toJson(response, JsonRpcResponse.class) + "\n"); + break; + case RPC.CALL_GET_REQUIRED_FEES: + mResponseView.setText(mResponseView.getText() + gson.toJson(response, JsonRpcResponse.class) + "\n"); + break; + case RPC.CALL_LOOKUP_ASSET_SYMBOLS: + mResponseView.setText(mResponseView.getText() + gson.toJson(response, JsonRpcResponse.class) + "\n"); + break; + default: + Log.w(TAG,"Case not handled"); + mResponseView.setText(mResponseView.getText() + response.result.toString()); + } + // Remember to remove the used id entry from the map, as it would + // otherwise just increase the app's memory usage + responseMap.remove(id); + }else{ + Log.d(TAG,"No entry"); + mResponseView.setText(mResponseView.getText() + gson.toJson(response, JsonRpcResponse.class) + "\n"); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if(!mDisposable.isDisposed()) + mDisposable.dispose(); + } + + @Override + public void onServiceConnected(ComponentName componentName, IBinder iBinder) { + // Called upon NetworkService connection + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { + // Called upon NetworkService disconnection + } +} diff --git a/sample/src/main/res/layout/activity_calls.xml b/sample/src/main/res/layout/activity_calls.xml new file mode 100644 index 0000000..e2f87a0 --- /dev/null +++ b/sample/src/main/res/layout/activity_calls.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/sample/src/main/res/layout/activity_perform_call.xml b/sample/src/main/res/layout/activity_perform_call.xml new file mode 100644 index 0000000..1a44ab0 --- /dev/null +++ b/sample/src/main/res/layout/activity_perform_call.xml @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + +