- Added partial support for the 'get_objects' API call in the single connection mode

- Changed the layout of the sample app in order to present a list of all supported API calls in the first activity
- Introducing a specific activity to perform the API calls in the sample app
develop
Nelson R. Perez 2018-06-28 23:42:40 -05:00
parent c5ffc614f4
commit 222fd88afa
15 changed files with 720 additions and 12 deletions

View File

@ -22,7 +22,7 @@ public class RPC {
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";

View File

@ -75,11 +75,9 @@ public class GetObjects extends BaseGrapheneHandler {
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
ArrayList<Serializable> params = new ArrayList<>();
ArrayList<Serializable> 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());
}

View File

@ -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{

View File

@ -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){

View File

@ -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<JsonRpcResponse<List<BucketObject>>>(){}.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){

View File

@ -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<String> ids;
public GetObjects(List<String> ids){
this.ids = ids;
}
@Override
public ApiCall toApiCall(int apiId, long sequenceId) {
ArrayList<Serializable> params = new ArrayList<>();
ArrayList<String> subParams = new ArrayList<>(ids);
params.add(subParams);
return new ApiCall(apiId, RPC.CALL_GET_OBJECTS, params, RPC.VERSION, sequenceId);
}
}

View File

@ -1,24 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cy.agorise.labs.sample">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".SampleApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:name=".SampleApplication">
<activity android:name=".MainActivity">
android:theme="@style/AppTheme">
<activity android:name=".MainActivity"></activity>
<activity android:name=".SecondActivity" />
<activity android:name=".CallsActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SecondActivity"></activity>
<activity android:name=".PerformCallActivity"></activity>
</application>
</manifest>

View File

@ -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<CallAdapter.ViewHolder> {
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;
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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";
}

View File

@ -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<Long, String> 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<Object>() {
@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<String> 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
}
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/call_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CallsActivity">
</android.support.v7.widget.RecyclerView>

View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".PerformCallActivity">
<ScrollView
android:id="@+id/output_text_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toTopOf="@+id/container_param1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/response"
tools:text="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
android:layout_width="match_parent"
android:layout_height="match_parent" />
</ScrollView>
<android.support.design.widget.TextInputLayout
android:id="@+id/container_param1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toTopOf="@+id/container_param2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/output_text_container">
<android.support.design.widget.TextInputEditText
android:id="@+id/param1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/container_param2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toTopOf="@+id/container_param3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/container_param1">
<android.support.design.widget.TextInputEditText
android:id="@+id/param2"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/container_param3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toTopOf="@+id/container_param4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/container_param2">
<android.support.design.widget.TextInputEditText
android:id="@+id/param3"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/container_param4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toTopOf="@+id/button_send"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/container_param3">
<android.support.design.widget.TextInputEditText
android:id="@+id/param4"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.TextInputLayout>
<Button
android:id="@+id/button_send"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:text="@string/action_send"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/container_param4" />
</android.support.constraint.ConstraintLayout>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:gravity="center"
android:textSize="18sp"
android:textStyle="bold"
android:padding="16dp"
android:background="?attr/selectableItemBackground"
android:clickable="true"
tools:text="Sample">
</TextView>

View File

@ -1,3 +1,35 @@
<resources>
<string name="app_name">Sample</string>
<string name="error_input_id">The entered value doesn\'t seem to be an object id</string>
<!-- Actions, buttons, etc -->
<string name="action_send">Send</string>
<!-- GetObjects input field -->
<string name="get_objects_arg1">Object id</string>
<!-- GetAccounts input field -->
<string name="get_accounts_arg1">Account id</string>
<!-- GetBlock & GetBlockHeader input field -->
<string name="get_block_arg1">Block id</string>
<!-- GetMarketHistory input fields -->
<string name="get_market_history_arg1">Base asset</string>
<string name="get_market_history_arg2">Quote asset</string>
<string name="get_market_history_arg3">Start timestamp (Latest)</string>
<string name="get_market_history_arg4">End timestamp (Earliest)</string>
<!-- GetRelativeAccountHistory input fields -->
<string name="get_relative_account_history_arg1">User account</string>
<string name="get_relative_account_history_arg2">Stop timestamp (Latest)</string>
<string name="get_relative_account_history_arg3">Limit</string>
<string name="get_relative_account_history_arg4">Start timestamp (Earliest)</string>
<!-- LookupAssetSymbols input fields -->
<string name="lookup_asset_symbols_arg1">Asset 1 id</string>
<string name="lookup_asset_symbols_arg2">Asset 2 id</string>
<string name="lookup_asset_symbols_arg3">Asset 3 id</string>
<string name="lookup_asset_symbols_arg4">Asset 4 id</string>
</resources>