From e056c16aa900b3fcb09050318d85b4b6ff33d120 Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Tue, 12 Jun 2018 13:56:59 -0500 Subject: [PATCH] - Added back the files for the sample project to the repository - Updated the sample project to test the 'get_block_header' api call wrapper using the single connection mode --- .gitignore | 1 - .../labs/sample/ExampleInstrumentedTest.java | 26 ++ .../cy/agorise/labs/sample/MainActivity.java | 230 ++++++++++++++++++ .../labs/sample/SampleApplication.java | 37 +++ .../agorise/labs/sample/SecondActivity.java | 159 ++++++++++++ sample/src/main/res/layout/activity_main.xml | 16 ++ .../sample/labs/sample/ExampleUnitTest.java | 17 ++ 7 files changed, 485 insertions(+), 1 deletion(-) create mode 100644 sample/src/androidTest/java/cy/sample/labs/sample/ExampleInstrumentedTest.java create mode 100644 sample/src/main/java/cy/agorise/labs/sample/MainActivity.java create mode 100644 sample/src/main/java/cy/agorise/labs/sample/SampleApplication.java create mode 100644 sample/src/main/java/cy/agorise/labs/sample/SecondActivity.java create mode 100644 sample/src/test/java/cy/sample/labs/sample/ExampleUnitTest.java diff --git a/.gitignore b/.gitignore index 4c2a140..729bc70 100644 --- a/.gitignore +++ b/.gitignore @@ -104,4 +104,3 @@ graphenej/build local.properties -sample diff --git a/sample/src/androidTest/java/cy/sample/labs/sample/ExampleInstrumentedTest.java b/sample/src/androidTest/java/cy/sample/labs/sample/ExampleInstrumentedTest.java new file mode 100644 index 0000000..9b859ad --- /dev/null +++ b/sample/src/androidTest/java/cy/sample/labs/sample/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package cy.sample.labs.sample; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.luminiasoft.labs.sample", appContext.getPackageName()); + } +} diff --git a/sample/src/main/java/cy/agorise/labs/sample/MainActivity.java b/sample/src/main/java/cy/agorise/labs/sample/MainActivity.java new file mode 100644 index 0000000..a8ee0fc --- /dev/null +++ b/sample/src/main/java/cy/agorise/labs/sample/MainActivity.java @@ -0,0 +1,230 @@ +package cy.agorise.labs.sample; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.View; +import android.widget.EditText; +import android.widget.TextView; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.util.HashMap; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import cy.agorise.graphenej.UserAccount; +import cy.agorise.graphenej.api.ConnectionStatusUpdate; +import cy.agorise.graphenej.api.android.DeserializationMap; +import cy.agorise.graphenej.api.android.NetworkService; +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.GetBlockHeader; +import cy.agorise.graphenej.api.calls.GetRelativeAccountHistory; +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 MainActivity extends AppCompatActivity { + private final String TAG = this.getClass().getName(); + + @BindView(R.id.connection_status) + TextView mConnectionStatus; + + @BindView(R.id.response) + TextView mResponse; + + @BindView(R.id.argument_get_accounts) + EditText mArgumentGetAccounts; + + @BindView(R.id.argument_get_block) + EditText mArgumentGetBlock; + + @BindView(R.id.argument_get_block_header) + EditText mArgumentGetBlockHeader; + + @BindView(R.id.argument_get_relative_account_history) + EditText mArgumentGetRelativeAccountHistory; + + // In case we want to interact directly with the service + private NetworkService mService; + + 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(); + + private Disposable mDisposable; + + private HashMap responseMap = new HashMap<>(); + + private final int GET_ACCOUNTS_RESPONSE = 0; + private final int GET_BLOCK_RESPONSE = 1; + private final int GET_BLOCK_HEADER_RESPONSE = 2; + private final int GET_RELATIVE_ACCOUNT_HISTORY_RESPONSE = 3; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + ButterKnife.bind(this); + + mDisposable = RxBus.getBusInstance() + .asFlowable() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Consumer() { + + @Override + public void accept(Object message) throws Exception { + if(message instanceof String){ + mResponse.setText(mResponse.getText() + ((String) message) + "\n"); + handleTextMessage((String) message); + }else if(message instanceof ConnectionStatusUpdate){ + mConnectionStatus.setText(((ConnectionStatusUpdate) message).getConnectionStatus()); + }else if(message instanceof JsonRpcResponse){ + handleJsonRpcResponse((JsonRpcResponse) message); + } + } + }); + } + + private void handleTextMessage(String text){ + + } + + /** + * 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){ + int responseId = responseMap.get(id); + switch(responseId){ + case GET_BLOCK_RESPONSE: + mResponse.setText(mResponse.getText() + gson.toJson(response, JsonRpcResponse.class) + "\n"); + break; + case GET_ACCOUNTS_RESPONSE: + mResponse.setText(mResponse.getText() + gson.toJson(response, JsonRpcResponse.class) + "\n"); + break; + case GET_RELATIVE_ACCOUNT_HISTORY_RESPONSE: + mResponse.setText(mResponse.getText() + gson.toJson(response, JsonRpcResponse.class) + "\n"); + break; + case GET_BLOCK_HEADER_RESPONSE: + mResponse.setText(mResponse.getText() + gson.toJson(response, JsonRpcResponse.class) + "\n"); + break; + default: + Log.w(TAG,"Case not handled"); + } + // 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"); + mResponse.setText(mResponse.getText() + gson.toJson(response, JsonRpcResponse.class) + "\n"); + } + } + + @OnClick(R.id.call_get_block) + public void onGetBlock(View v){ + String str = mArgumentGetBlock.getText().toString(); + try{ + Long blockNum = Long.parseLong(str); + GetBlock getBlock = new GetBlock(blockNum); + long id = mService.sendMessage(getBlock, GetBlock.REQUIRED_API); + // Registering the used sequence id + responseMap.put(id, GET_BLOCK_RESPONSE); + }catch(NumberFormatException e){ + Log.e(TAG,"NumberFormatException. Msg: "+e.getMessage()); + } + } + + @OnClick(R.id.call_get_block_header) + public void onGetBlockHeader(View v){ + String str = mArgumentGetBlockHeader.getText().toString(); + try{ + Long blockNum = Long.parseLong(str); + GetBlockHeader getBlockHeader = new GetBlockHeader(blockNum); + long id = mService.sendMessage(getBlockHeader, GetBlockHeader.REQUIRED_API); + // Registering the used sequence id + responseMap.put(id, GET_BLOCK_HEADER_RESPONSE); + }catch(NumberFormatException e){ + Log.e(TAG,"NumberFormatException. Msg: "+e.getMessage()); + } + } + + @OnClick(R.id.call_get_accounts) + public void onGetAccounts(View v){ + String userId = mArgumentGetAccounts.getText().toString(); + GetAccounts getAccounts = new GetAccounts(new UserAccount(userId)); + long id = mService.sendMessage(getAccounts, GetBlock.REQUIRED_API); + // Registering the used sequence id + responseMap.put(id, GET_ACCOUNTS_RESPONSE); + } + + @OnClick(R.id.call_get_relative_account_history) + public void onGetRelativeAccountHistory(View v){ + String userId = mArgumentGetRelativeAccountHistory.getText().toString(); + UserAccount userAccount = new UserAccount(userId); + GetRelativeAccountHistory getRelativeAccountHistory = new GetRelativeAccountHistory(userAccount, 0, 20, 0); + long id = mService.sendMessage(getRelativeAccountHistory, GetRelativeAccountHistory.REQUIRED_API); + responseMap.put(id, GET_RELATIVE_ACCOUNT_HISTORY_RESPONSE); + } + + @OnClick(R.id.next_activity) + public void onNextActivity(View v){ + Intent intent = new Intent(this, SecondActivity.class); + startActivity(intent); + } + + @Override + protected void onStart() { + super.onStart(); + // Bind to LocalService + Intent intent = new Intent(this, NetworkService.class); + bindService(intent, mConnection, Context.BIND_AUTO_CREATE); + } + + @Override + protected void onPause() { + super.onPause(); + unbindService(mConnection); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mDisposable.dispose(); + } + + /** Defines callbacks for backend binding, passed to bindService() */ + private ServiceConnection mConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName className, + IBinder service) { + Log.d(TAG,"onServiceConnected"); + // We've bound to LocalService, cast the IBinder and get LocalService instance + NetworkService.LocalBinder binder = (NetworkService.LocalBinder) service; + mService = binder.getService(); + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { + Log.d(TAG,"onServiceDisconnected"); + } + }; +} diff --git a/sample/src/main/java/cy/agorise/labs/sample/SampleApplication.java b/sample/src/main/java/cy/agorise/labs/sample/SampleApplication.java new file mode 100644 index 0000000..bb45d70 --- /dev/null +++ b/sample/src/main/java/cy/agorise/labs/sample/SampleApplication.java @@ -0,0 +1,37 @@ +package cy.agorise.labs.sample; + +import android.app.Application; +import android.preference.PreferenceManager; + +import cy.agorise.graphenej.api.ApiAccess; +import cy.agorise.graphenej.api.android.NetworkService; +import cy.agorise.graphenej.api.android.NetworkServiceManager; + +/** + * Sample application class + */ + +public class SampleApplication extends Application { + private final String TAG = this.getClass().getName(); + + @Override + public void onCreate() { + super.onCreate(); + + // Specifying some important information regarding the connection, such as the + // credentials and the requested API accesses + int requestedApis = ApiAccess.API_DATABASE | ApiAccess.API_HISTORY | ApiAccess.API_NETWORK_BROADCAST; + PreferenceManager.getDefaultSharedPreferences(this) + .edit() + .putString(NetworkService.KEY_USERNAME, "nelson") + .putString(NetworkService.KEY_PASSWORD, "secret") + .putInt(NetworkService.KEY_REQUESTED_APIS, requestedApis) + .apply(); + + /* + * Registering this class as a listener to all activity's callback cycle events, in order to + * better estimate when the user has left the app and it is safe to disconnect the websocket connection + */ + registerActivityLifecycleCallbacks(new NetworkServiceManager(this)); + } +} diff --git a/sample/src/main/java/cy/agorise/labs/sample/SecondActivity.java b/sample/src/main/java/cy/agorise/labs/sample/SecondActivity.java new file mode 100644 index 0000000..224200d --- /dev/null +++ b/sample/src/main/java/cy/agorise/labs/sample/SecondActivity.java @@ -0,0 +1,159 @@ +package cy.agorise.labs.sample; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.support.v7.app.AppCompatActivity; +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 io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.functions.Consumer; + +public class SecondActivity extends AppCompatActivity { + + private final String TAG = this.getClass().getName(); + + @BindView(R.id.text_field) + TextView mTextField; + + // In case we want to interact directly with the service + private NetworkService mService; + + private Gson gson = new Gson(); + + private Disposable mDisposable; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_second); + Log.d(TAG,"onCreate"); + + ButterKnife.bind(this); + + mDisposable = RxBus.getBusInstance() + .asFlowable() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Consumer() { + + @Override + public void accept(Object message) throws Exception { + 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"); + } + } + }); + } + + @Override + protected void onStart() { + super.onStart(); + // Bind to LocalService + Intent intent = new Intent(this, NetworkService.class); + bindService(intent, mConnection, Context.BIND_AUTO_CREATE); + } + + @Override + protected void onPause() { + super.onPause(); + unbindService(mConnection); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mDisposable.dispose(); + } + + /** Defines callbacks for backend binding, passed to bindService() */ + private ServiceConnection mConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName className, + IBinder service) { + Log.d(TAG,"onServiceConnected"); + // We've bound to LocalService, cast the IBinder and get LocalService instance + NetworkService.LocalBinder binder = (NetworkService.LocalBinder) service; + mService = binder.getService(); + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { + Log.d(TAG,"onServiceDisconnected"); + } + }; + + @OnClick(R.id.transfer_fee_usd) + public void onTransferFeeUsdClicked(View v){ + List operations = getTransferOperation(); + mService.sendMessage(new GetRequiredFees(operations, new Asset("1.3.121")), GetRequiredFees.REQUIRED_API); + } + + @OnClick(R.id.transfer_fee_bts) + 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; + } +} diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml index 52b1955..3dc31e8 100644 --- a/sample/src/main/res/layout/activity_main.xml +++ b/sample/src/main/res/layout/activity_main.xml @@ -62,6 +62,22 @@ android:layout_height="wrap_content" android:text="100000"/> + +