What's new |
---|
- [2013-03-21] Adding zipped source code and 2 screen shots
|
|
Introduction
Sorry, for waiting so long to get this 2nd blog out - there are so many things to be done...
- parse the json format which was returned from HANA XS in order to
- show results in a little bit nicer way (just readable)
- Remarks:
- I know that a real Android app would look much more appealing.
- I know that date handling and error handling both require a lot more sophistication.
- For the sake of brevity I keep it like this - it's not the focus of this blog.
- use more features of the XS odata API to select some more meaningful data: query filters, input parameters, ...
As the focus in this blog is again on how to leverage SAP HANA, I compare 2 techniques to get data and calculate results:
- Frontend approach: I fire several odata service calls in 1 http request and do some math with the results in the Android device. (~ 15 min)
- Backend approach: I use a Calculation View to get data and calculate the correct results in HANA (~ 10 min)
And all of that in 15 - 25 minutes again. Let's see how it works.
Prerequisites
I had a problem in XS engine until I upgraded my local HANA server to Revision >= 48. HANA One on AWS now is on Revision 48.
Source Code Upload
If it is too cumersome for you to make the code changes proposed below, then use the zipped content in
Dropbox:
There are 4 parts in it:
- XS project "androidaponhana" - import it via:
- right-click in left-most "Project" area -> New -> Project -> SAP HANA Development Project -> XS Project -> Next -> Project Name: <new project name>
- -> File -> Import... -> General -> File System -> From directory: <directory> -> check "androidapponhana" -> Into Folder: <new project name> -> when asked "Overwrite .project in <new project name>?" select "Yes To All"
- Then, right-click your project -> Team -> Share Project... -> select your <repository workspace> -> Finish
- Team -> Commit and -> Team -> Activate
- Calculation View is in the HANA Content and therefore not part of the XS project. Therefore, import "XSE" via:
- -> File -> Import... -> SAP HANA Content -> Developer Mode -> Next -> <Select vour system> -> Folder location "XSE" -> Objects for import -> Content -> androidapphana -> calculation views -> COMPARE_STOCK_CV.calculationview -> add it to the right -> Finish
- Import the 2 Android projects Frontend / Backend via:
- -> File -> Import -> Android -> Existing Android Code into Workspace -> Root Directory <path\>AndroidAppOnHANAExtensionBackendApproach -> Finish
- -> File -> Import -> Android -> Existing Android Code into Workspace -> Root Directory <path\>AndroidAppOnHANAExtensionFrontendApproach -> Finish
That is it from a source code point of view. You still have to create some test data. By the way, this is an export of my implementation containing my users, passwords, IP addresses etc. Please change them as described below!!!!!! It won't work otherwise!
Also, there is a Frontend Learning and a Backend Learning section below. Don't forget to read them!
Enhance the Android App for the Frontend Approach
Copy project AndroidAppOnHANABaseScenario to AndroidAppOnHANAExtensionFrontendApproach. Run it as Android Application once to see if it still works.
Below, the existing parts are shown in grey, whereas the new parts are shown in blue, to be deleted parts in orange:
Then let's do the UI xml files first.
Open -> res -> layout -> activity_show_market_price_data.xml (xml layout, not the Graphical one) and replace the whole existing content by this one (which will show a table layout with some input/ output fields and a button in between):
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/user_table_1" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TableRow android:id="@+id/user_table_item_row1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:paddingTop="3dp" > <TextView android:id="@+id/TextDisplay1" android:layout_width="0dp" android:layout_weight="1.0" android:layout_height="wrap_content" android:singleLine="true" android:text="@string/stock1" /> <EditText android:id="@+id/nsin1" android:layout_width="0dp" android:layout_weight="1.0" android:layout_height="wrap_content" android:singleLine="true" android:textAppearance="@android:style/TextAppearance.Medium" android:paddingLeft="3dp" android:gravity="left|center_vertical" android:inputType="text" > </EditText> </TableRow>
<TableRow
android:id="@+id/user_table_item_row2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="3dp"
>
<TextView
android:id="@+id/TextDisplay2"
android:layout_width="0dp"
android:layout_weight="1.0"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/stock2" />
<EditText
android:id="@+id/nsin2"
android:layout_width="0dp"
android:layout_weight="1.0"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="@android:style/TextAppearance.Medium"
android:paddingLeft="3dp"
android:gravity="left|center_vertical"
android:inputType="text" >
</EditText>
</TableRow>
<TableRow
android:id="@+id/user_table_item_row3"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="3dp"
>
<EditText
android:id="@+id/start_date"
android:layout_width="0dp"
android:layout_weight="1.0"
android:layout_height="wrap_content"
android:singleLine="true"
android:inputType="text|date" />
<TextView
android:id="@+id/TextDisplay4"
android:layout_width="0dp"
android:layout_weight="1.0"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/end_text" />
<EditText
android:id="@+id/end_date"
android:layout_width="0dp"
android:layout_weight="1.0"
android:layout_height="wrap_content"
android:singleLine="true"
android:inputType="text|date" />
</TableRow>
<TableRow
android:id="@+id/user_table_item_row4"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="3dp"
>
<Button
android:id="@+id/button"
android:layout_width="0dp"
android:layout_weight="1.0"
android:layout_height="wrap_content"
android:onClick="calculate"
android:text="@string/button" />
</TableRow>
<TableRow
android:id="@+id/user_table_item_row5"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="3dp"
>
<GridView
android:id="@+id/gridView1"
android:layout_width="0dp"
android:layout_weight="1.0"
android:layout_height="wrap_content"
android:numColumns="2" >
</GridView>
</TableRow>
</TableLayout>
Then, go to -> res -> values -> strings.xml and replace the existing content by the following:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Compare 2 stocks!</string>
<string name="menu_settings">Settings</string>
<string name="stock1">Stock 1</string>
<string name="stock2">Stock 2</string>
<string name="start_text">since</string>
<string name="end_text">through</string>
<string name="nsin1">716460</string>
<string name="nsin2">LU0173001990</string>
<string name="button">Compare</string>
<string name="result">Performance of </string>
<string-array name="textContent">
<item>Stock 1</item>
<item>Stock 2</item>
<item>000716460</item>
<item>dummy</item>
<item>LU0173001990</item>
<item>01/01/2013</item>
<item>today</item>
<item>Performance of </item>
</string-array>
</resources>
Then, let's enhance the coding. Go to -> src -> com.example.androidapponhanabasescenario -> ShowMarketPriceData.java.
At first, you will need some more libraries:
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import android.widget.EditText;
import java.util.Calendar;
import java.text.SimpleDateFormat;
import java.io.InputStream;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicHeader;
import org.odata4j.core.Guid;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.james.mime4j.util.CharsetUtil;
import java.io.UnsupportedEncodingException;
import java.io.IOException;
import android.util.Log;
Now, for some of them you need to download additional jar files from the internet. You need to download
and put them into libs directory in eclipse as described in the last blog.
Enhance your class with some class attributes:
- public class ShowMarketPriceData extends Activity {
JSONArray results = null;
GridView gvMain;
ArrayAdapter<String> adapter;
private ShowMarketPriceData mContext;
final List<String> performance = new ArrayList<String>();
In the onCreate method, add an initialization method call and its definition as well as a calculation method (to react on a button click):
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initialize(this);
....
- delete
ShowDialogAsyncTask aTask = new ShowDialogAsyncTask();
aTask.execute();
- create
public void initialize(ShowMarketPriceData Context) {
mContext = Context;
gvMain = (GridView) findViewById(R.id.gridView1);
EditText nsin1Field = (EditText) findViewById(R.id.nsin1);EditText nsin2Field = (EditText) findViewById(R.id.nsin2);EditText startDate = (EditText) findViewById(R.id.start_date);EditText endDate = (EditText) findViewById(R.id.end_date);
// Get today as a Calendar Calendar today = Calendar.getInstance(); // Make an SQL Date out of that java.sql.Date todayText = new java.sql.Date(today.getTimeInMillis()); // Subtract 1 day Calendar month_ago = Calendar.getInstance(); month_ago.add(Calendar.DATE, -31); // Make an SQL Date out of that java.sql.Date monthAgoText = new java.sql.Date(month_ago.getTimeInMillis()); SimpleDateFormat sdf = new SimpleDateFormat( "yyyy/MM/dd" );
String[] texts = getResources().getStringArray(R.array.textContent);
nsin1Field.setText( texts[2] ); nsin2Field.setText( texts[4] ); startDate.setText( sdf.format( monthAgoText )); endDate.setText( sdf.format( todayText )); }
public void calculate(View view) {
ShowDialogAsyncTask aTask;
aTask = new ShowDialogAsyncTask();
aTask.execute();
}
- delete the orange parts and replace them
public String getOdata() {String JASONrs = "You will get there!";
try { hanacon = myhana.openConnection(); hanacon.setReadTimeout(1000); hanacon.setConnectTimeout(1000);
String userpass = "SYSTEM" + ":" + "manager";// String userpass = "SYSTEM" + ":" + "Hana2012"; String basicAuth = "Basic " + new String(Base64.encode(userpass.getBytes(), 0)); hanacon.setRequestProperty ("Authorization", basicAuth); BufferedReader in = new BufferedReader(new InputStreamReader(hanacon.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
JASONrs += inputLine;
in.close();
} catch (Exception e) { e.printStackTrace(); return "Error"; } return JASONrs; } - so, replace the orange parts with this
InputStream is = null;
//-------------------------------- for http watching// Properties props = System.getProperties();// props.put("http.proxyHost", "<my laptop's IP address>"); //// props.put("http.proxyPort", "8888");//--------------------------------
EditText nsin1Field = (EditText) findViewById(R.id.nsin1); EditText nsin2Field = (EditText) findViewById(R.id.nsin2); EditText start_date = (EditText) findViewById(R.id.start_date); EditText end_date = (EditText) findViewById(R.id.end_date);
String nsin1String = nsin1Field.getText().toString(); String nsin2String = nsin2Field.getText().toString(); String startDateString = start_date.getText().toString(); String endDateString = end_date.getText().toString();
try {
DefaultHttpClient httpClient = new DefaultHttpClient();
httpClient.getCredentialsProvider().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("<HANA user name>", "<your pw>"));
httpPost.addHeader(new BasicHeader("Accept", "*/*")); httpPost.addHeader(new BasicHeader("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.3")); httpPost.addHeader(new BasicHeader("Accept-Encoding", "gzip,deflate,sdch")); httpPost.addHeader(new BasicHeader("Accept-Language", "de-DE.de;q=0.8,en-US;q=0.6,en;q=0.4"));
String batchBoundary = "batch_" + Guid.randomGuid().toString(); String batchMultipartBoundary = "multipart/mixed; boundary=" + batchBoundary; httpPost.addHeader(new BasicHeader("Content-Type", batchMultipartBoundary));
httpPost.addHeader(new BasicHeader("Host", "<hana.server.name>:80<HANA_instance_number>")); String starter = "\r\n \r\n--" + batchBoundary + "\r\n"; String CS = "Content-Type: application/http\r\nContent-Transfer-Encoding:binary\r\n\r\nGET /History/?$top=1&$filter=NSIN%20eq%20'"; String DC = "'&$orderby=DATE%20desc&$select=DAY_CLOSE&$format=json HTTP/1.1\r\nAccept:application/json\r\n\r\n ";
String sb1 = CS + nsin1String + "'%20and%20DATE%20le%20datetime'" + startDateString + DC; String sb2 = CS + nsin1String + "'%20and%20DATE%20le%20datetime'" + endDateString + DC; String sb3 = CS + nsin2String + "'%20and%20DATE%20le%20datetime'" + startDateString + DC; String sb4 = CS + nsin2String + "'%20and%20DATE%20le%20datetime'" + endDateString + DC; String total_string = starter + sb1 + starter + sb2 + starter + sb3 + starter + sb4; StringBody all = new StringBody(total_string, "dummy", CharsetUtil.ISO_8859_1);
MultipartEntity multipartContent = new MultipartEntity(HttpMultipartMode.STRICT, batchBoundary, CharsetUtil.ISO_8859_1); multipartContent.addPart("GET", all); httpPost.setEntity(multipartContent);
HttpResponse httpResponse = httpClient.execute(httpPost); HttpEntity httpEntity = httpResponse.getEntity(); is = httpEntity.getContent(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } try { BufferedReader reader = new BufferedReader(new InputStreamReader(is, "iso-8859-1"), 8); StringBuilder sb = new StringBuilder(); String line = null; while ((line = reader.readLine()) != null) sb.append(line + "\n"); is.close(); JASONrs = sb.toString(); - Make sure to replace "<HANA user name>" (for instance "SYSTEM") and <your pw> (for instance "manager")
- Also make sure to replace "<hana.server.name>:80<HANA_instance_number>" with your HANA system (for instance "23.20.228.188:8000")
- Then, enhance ShowDialogAsyncTask like this:
TextView tv = (TextView) findViewById(R.id.MyTextResponse);
//Object obj=JSONValue.parse(result);
tv.setText(result);
- create
@Override
protected void onPostExecute(String result) {
List<String> JSONrs = new ArrayList<String>();
String day_close = "";
String json_strings[];
// split the 1 resturn string into the 4 json strings (we had 4 requests initially)
JSONrs.clear();
performance.clear();
json_strings = result.split("\"d\":"); int i; for(i=0; i<json_strings.length - 1; i++) { json_strings[i] = "{\"d\":" + json_strings[i+1].substring(0, json_strings[i+1].lastIndexOf("}")+1); } json_strings[i] = null; i=0;
String[] texts = getResources().getStringArray(R.array.textContent); // parse the json objects
try { while ((json_strings[i]) != null) { try { JSONObject jsn = new JSONObject( json_strings[i] );
JSONObject c = jsn.getJSONObject("d"); JSONArray results = c.getJSONArray("results");
for(int j = 0; j < results. length(); j++){ JSONObject res = results.getJSONObject(j); day_close = res.getString("DAY_CLOSE"); } JSONrs.add(day_close);
} catch (JSONException e) { e.printStackTrace(); } i++; }
} catch (Exception e) { e.printStackTrace();}
try {
for(int j = 0; j < 4; j++){
Float f= ( Float.valueOf(JSONrs.get(j+1)) / Float.valueOf(JSONrs.get(j)) * 100 ) - 100;
performance.add(texts[7] + " " + texts[j + 2]);
performance.add(f.toString());
j++;
}
} catch (IndexOutOfBoundsException e) {
performance.add("no results");
}
if (gvMain.getAdapter() == null) { adapter = new ArrayAdapter<String>(mContext, android.R.layout.simple_list_item_1, performance); gvMain.setAdapter(adapter);} else adapter.notifyDataSetChanged(); - OK, you're done. But before you can test you need to create some more test data. Type and execute in HANA Studio (like described in last blog):
- insert into "COMPARE_STOCK"."androidapponhana::PRICE_HISTORY" ( NSIN, DATE, TIME, DAY_OPEN, DAY_HIGH, DAY_LOW, DAY_CLOSE, VOLUME ) values ('000716460', '20130209', '130000', 60.01, 59.55, 57.91, 60.1, 2313291);
- insert into "COMPARE_STOCK"."androidapponhana::PRICE_HISTORY" ( NSIN, DATE, TIME, DAY_OPEN, DAY_HIGH, DAY_LOW, DAY_CLOSE, VOLUME ) values ('000716460', '20130313', '130000', 61.01, 62.55, 61.91, 63.1, 2313291);
- insert into "COMPARE_STOCK"."androidapponhana::PRICE_HISTORY" ( NSIN, DATE, TIME, DAY_OPEN, DAY_HIGH, DAY_LOW, DAY_CLOSE, VOLUME ) values ('LU0173001990', '20130209', '130000', 59.19, 59.55, 58.91, 59.2, 2313291);
- insert into "COMPARE_STOCK"."androidapponhana::PRICE_HISTORY" ( NSIN, DATE, TIME, DAY_OPEN, DAY_HIGH, DAY_LOW, DAY_CLOSE, VOLUME ) values ('LU0173001990', '20130313', '130000', 60.19, 60.55, 59.91, 60.2, 2313291);
- Important when testing in the future: When making these INSERTs into the table, please use the following inputs in the Android App:
- start date: after March 9th, 2013
- end date: between February 9th and March 9th.
- Othewise you won't see anything!!!
- OK. This is it. Run it and click the "Compare" button. This is what it should look like:
What did we learn?
- Of course, it is possible to send several asynchronous http GET requests to HANA, get the data and calculate the result. This is a bad idea though, as “backend calls” via mobile connections may have a long runtime.
- So, a “batch request” of several http GET requests combined into 1 Multipart http POST request is a better way of doing this. HANA XS requires a certain format which is described in the developer documentation. It took me some time to learn how to get my Android code into a shape that is working correctly. Still, there is an unnecessary “dummy” part which I didn’t manage to get rid of and I simply ignore. I leave this as a task for Multipart experts.
- Accordingly, the parsing of the JSON leads to a little bit weird numbering in the for loop. Sorry. You might find more perfect solutions.
- Interestingly enough, we learn how odata parameters are used in HANA XS.
- Furthermore, please note that the exact json object that is tailored by the XS engine according to my table design in HANA is parsed here (objects “d”, “results”, “DAY_CLOSE” – especially that is part of my HANA table design). So, here we learn how to change this for use in other XS services and with other tables.
- Finally, the math that is being done with 4 different query results is pretty simple:
- Float f= ( Float.valueOf(JASONrs[j+1]) / Float.valueOf(JASONrs[j]) * 100 ) - 100;
- I calculate the performance of a stock of date 2 on the basis of its value at
date 1 in percent of change (be it pos or neg). - Comparing the 2 technologies with the same "mathematical problem" we learn their strengths and what it needs to use them.
Enhance the Android App for the Backend Approach
In this approach, we are again focusing on the HANA side and are going to create some more HANA artifacts. But let's first adapt our Android code.
Adapt the Android App
In case you are starting form here: keep in mind that instead of copying from the ForntendApproach project you can copy from the BaseScenario project, but then you must do the stuff with activity_show_market_price_data.xml / strings.xml / imports / class attributes as described above.
Copy project AndroidAppOnHANAExtensionFrontendApproach to AndroidAppOnHANAExtensionBackendApproach.
- Basically revert most the changes in the code of getOdata(). We will be using HTTP GET again in this approach as most fo the work is being done in the backend. So, we only make 1 call to the backend. Go to -> src -> com.example.androidapponhanabaseScenario -> ShowMarketPriceData.java.
delete the orange part:
public String getOdata() {
...
String startDateString = start_date.getText().toString();
String endDateString = end_date.getText().toString();
try {
DefaultHttpClient httpClient = new DefaultHttpClient();
httpClient.getCredentialsProvider().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("<HANA user name>", "<your pw>"));
") httpPost.addHeader(new BasicHeader("Accept", "*/*"));
httpPost.addHeader(new BasicHeader("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.3"));
httpPost.addHeader(new BasicHeader("Accept-Encoding", "gzip,deflate,sdch"));
httpPost.addHeader(new BasicHeader("Accept-Language", "de-DE.de;q=0.8,en-US;q=0.6,en;q=0.4"));
String batchBoundary = "batch_" + Guid.randomGuid().toString();
String batchMultipartBoundary = "multipart/mixed; boundary=" + batchBoundary;
httpPost.addHeader(new BasicHeader("Content-Type", batchMultipartBoundary));
httpPost.addHeader(new BasicHeader("Host", "<hana.server.name>:80<HANA_instance_number>"))
String starter = "\r\n \r\n--" + batchBoundary + "\r\n";
String CS = "Content-Type: application/http\r\nContent-Transfer-Encoding:binary\r\n\r\nGET /History/?$top=1&$filter=NSIN%20eq%20'";
String DC = "'&$orderby=DATE%20desc&$select=DAY_CLOSE&$format=json HTTP/1.1\r\nAccept:application/json\r\n\r\n ";
String sb1 = CS + nsin1String + "'%20and%20DATE%20le%20datetime'" + startDateString + DC;
String sb2 = CS + nsin1String + "'%20and%20DATE%20le%20datetime'" + endDateString + DC;
String sb3 = CS + nsin2String + "'%20and%20DATE%20le%20datetime'" + startDateString + DC;
String sb4 = CS + nsin2String + "'%20and%20DATE%20le%20datetime'" + endDateString + DC;
String total_string = starter + sb1 + starter + sb2 + starter + sb3 + starter + sb4;
StringBody all = new StringBody(total_string, "dummy", CharsetUtil.ISO_8859_1);
MultipartEntity multipartContent = new MultipartEntity(HttpMultipartMode.STRICT, batchBoundary, CharsetUtil.ISO_8859_1);
multipartContent.addPart("GET", all);
httpPost.setEntity(multipartContent);
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
is = httpEntity.getContent();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "iso-8859-1"), 8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) sb.append(line + "\n");
is.close();
JASONrs = sb.toString();
- instead, create these lines
try {
URL myhana = new URL(
"AppParams(NSIN1='" + nsin1String + "',NSIN2='" + nsin2String +
"',START_DATE=datetime'" + startDateString + "T00:00:00.0000000'," +
"END_DATE=datetime'" + endDateString + "T00:00:00.0000000')/AppResults?$format=json");
URLConnection hanacon;
hanacon = myhana.openConnection();
hanacon.setReadTimeout(1000);
hanacon.setConnectTimeout(1000);
String userpass = "<HANA user name>" + ":" + "<your pw>";
String basicAuth = "Basic " + new String(Base64.encode(userpass.getBytes(), 0));
hanacon.setRequestProperty ("Authorization", basicAuth);
BufferedReader in = new BufferedReader(new InputStreamReader(
hanacon.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
JASONrs += inputLine;
in.close();
- Also make sure to replace "<hana.server.name>:80<HANA_instance_number>" with your HANA system (for instance "23.20.228.188:8000")
- Make sure to replace "<HANA user name>" (for instance "SYSTEM") and <your pw> (for instance "manager")
- Then, adapt the JSON parsing. Now, we don't get the raw data (table column "DAY_CLOSE") but we get the calculated result instead ("NSIN_GROWTH"), 2 lines of them, exactly 2 ones we want to show on the UI.
private class ShowDialogAsyncTask extends AsyncTask<Void , Void , String>{
...
for(int j = 0; j < results. length(); j++){
JSONObject res = results.getJSONObject(j);
day_close = res.getString("DAY_CLOSE");
}
JSONrs.add(day_close);
- and replace them by these lines
for(int j = 0; j < results. length(); j++){
JSONObject res = results.getJSONObject(j);
JSONrs.add(res.getString("NSIN_GROWTH"));
}
- Finally, replace the way how to combine strings for display.
for(int j = 0; j < 4; j++){
Float f= ( Float.valueOf(JSONrs.get(j+1)) / Float.valueOf(JSONrs.get(j)) * 100 ) - 100;
performance.add(texts[7] + " " + texts[j + 2]);
performance.add(f.toString());
j++;
}
- and replace them by these lines in blue
try {
performance.add(texts[7] + " " + texts[2]);
performance.add(JSONrs.get(0));
performance.add(texts[7] + " " + texts[4]);
performance.add(JSONrs.get(1));
} catch (IndexOutOfBoundsException e) {
performance.add("no results");
}
This is it on the Android side. Now, we can't test this yet as we are now missing the HANA Calculation which we are calling out of the Android code.
So, let's get this done in the HANA Studio.
Adapt the HANA XS App
Start your HANA Studio. Connect to your HANA (as described in the last Blog).
- It is important to do this first: create the CalcView before adapting the xsodata service definition! (There is a dependency chain.) Go to the SAP HANA Development perspective and go to the Navigator tab in the leftmost pane this time. Navigate to -> Content -> androidapponhana -> right-click Calculation Views and click New...
- Put the Name "COMPARE_STOCK_CV", select view type SQL Script, leave everything else as defaulted and click Finish. You will get a graphical CalculationView development canvass with 3 panes. (May-be you have to click in the middle of the Ouput box in the left-most of the three panes first.)
- Right-click Input Parameters in the righ-most pane and click New... Create 4 input parameters:
- Name "NSIN1", Data Type = NVARCHAR, length = 12, enable "Is Mandatory".
- Name "NSIN2", Data Type = NVARCHAR, length = 12, enable "Is Mandatory".
- Name "START_DATE", Type = Date, enable "Is Mandatory".
- Name "END_DATE", Type = Date, enable "Is Mandatory".
- Then, click in the middle of the Script box in the left-most of the three panes and put this SQL Script code:
/********* Begin Procedure Script ************/
BEGIN
var_out = select top 1 ( 100 * DAY_CLOSE / ( select top 1 DAY_CLOSE from "COMPARE_STOCK"."androidapphana::PRICE_HISTORY"
where NSIN = :NSIN1
and DATE <= :START_DATE ) - 100 )
AS NSIN_GROWTH from "COMPARE_STOCK"."androidapphana::PRICE_HISTORY"
where NSIN = :NSIN1
and DATE <= :END_DATE
union (
select top 1 ( 100 * DAY_CLOSE / ( select top 1 DAY_CLOSE from "COMPARE_STOCK"."androidapphana::PRICE_HISTORY"
where NSIN = :NSIN2
and DATE <= :START_DATE ) - 100 )
AS NSIN_GROWTH from "COMPARE_STOCK"."androidapphana::PRICE_HISTORY"
where NSIN = :NSIN2
and DATE <= :END_DATE
) ;
END /********* End Procedure Script ************/
- Let me comment this SQL Script a little bit.
- In order to do a division of a field of 2 records of the same table, I am using a subselect to get the result
- In order to return 2 such results with different selection criteria (NSIN1 / NSIN2), I simply use a union of two such single results.
- In order to stay near to the notion of a "view" I use subselects/unions in thei CalcView. I could have also used other syntax elements to do it.
- It is pretty short and easy to read.
- It should look like this in HANA Studio:
- Finally, right-click on Output Parameter var_out in the right-most pane, click Create... and on the upcoming popup, click the small green + and put:
- Name = "NSIN_GROWTH", Data Type = Decimal, Length = 25, Scale = 12
- Now, click the Output box in the left-most pane of the three panes again, right-click NSIN_GROWTH in the middle pane and click Add Attribute. (As a consequence, you will see it appear in the right-most pane.) Right-click NSIN_GROWTH and click Create Variable. In the popup, activate Multiple Values and click OK.
- In the Navigator pane on the left, right-click COMPARE_STOCK_CV and click Activate. In the upcoming popup COMPARE_STOCK_CV is already being selected, just click Activate. (In case you see a "Completed with errors" in the Job Log display, double click on the line: you only have to make sure that in the upper table "Summary Report" there is Success for the activation of your Calculation View. There might still be other errors not important for us...)
The last step thatneeds to be done is to adapt the xsodata service definition:
- In the Project Explorer tab on the left, double click -> androidapponhana --> StockDataIF.xsodata. Add 1 row so that it looks in total:
service {
"androidapphana::PRICE_HISTORY" as "History" ;
"androidapphana::COMPARE_STOCK_CV" as "CalcView" keys generate local "ID" parameters via entity "AppParams" results property "AppResults" ;
}
- In addition to our existing service definition "History" we now have a service CalcView which needs 4 input parameters. They are input in the HTTP Request like this (which we have done in the App coding):
"AppParams(NSIN1='" + nsin1String + "',NSIN2='" + nsin2String +
"',START_DATE=datetime'" + startDateString + "T00:00:00.0000000'," +
"END_DATE=datetime'" + endDateString + "T00:00:00.0000000')/AppResults?$format=json"
- Also make sure to replace "<hana.server.name>:80<HANA_instance_number>" with your HANA system (for instance "23.20.228.188:8000")
- Click the overall Save button.
- In Project Explorer, right-click androidapponhana --> Team --> Commit.
- In Project Explorer, right-click androidapponhana --> Team --> Activate.
That is it. Now test the App again (with the dates mentioned above). No surprise, it looks the same as in the frontend approach.
What did we learn?
- We can achieve the same goals with 2 different techniques, one using frontend computing and therefo...