Extend an Android app on SAP HANA One in 20 minutes
What’s new |
---|
|
Introduction
- 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.
- Remarks:
- use more features of the XS odata API to select some more meaningful data: query filters, input parameters, …
- 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)
Prerequisites
Further Readings
Source Code Upload
- 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
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
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” >
</TableRow>
<TableRow
android:id=”@+id/user_table_item_row3″
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:paddingTop=”3dp”
>
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>
<resources>
<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>
import org.json.JSONException;
import org.json.JSONObject;
import java.util.List;
import android.widget.EditText;
import java.util.Calendar;
import java.text.SimpleDateFormat;
import java.io.InputStream;
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.IOException;
import android.util.Log;
- odata4j-core-0.7.0.jar e. g. from http://repo1.maven.org/maven2/org/odata4j/odata4j-examples/0.7.0/odata4j-examples-0.7.0-sources.jar
- httpmime-4.2.3.jar e. g. from http://repo1.maven.org/maven2/org/apache/httpcomponents/httpmime/4.2.3/httpmime-4.2.3-sources.jar
- apache-mime4j.jar e. g. from http://www.java2s.com/Code/JarDownload/apache-mime4j/apache-mime4j.jar.zip
- public class ShowMarketPriceData extends Activity {
- delete
ShowDialogAsyncTask aTask = new ShowDialogAsyncTask();aTask.execute();
- create
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());
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 ));
aTask = new ShowDialogAsyncTask();
aTask.execute();
- delete the orange parts and replace them
String JASONrs = “You will get there!”;
try {
“http://:80/androidapphana/StockDataIF.xsodata/History?$format=json“);
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);
String inputLine;
JASONrs += inputLine;
in.close();
e.printStackTrace();
return “Error”;
}
return JASONrs;
}
- so, replace the orange parts with this
// 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();
DefaultHttpClient httpClient = new DefaultHttpClient();
httpClient.getCredentialsProvider().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(“<HANA user name>”, “<your pw>”));
HttpPost httpPost = new HttpPost(“http://<hana.server.name>:80<HANA_instance_number>/androidapphana/StockDataIF.xsodata/$batch“);
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 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;
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);
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
} 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:
- delete
- create
@Override
protected void onPostExecute(String result) {
String day_close = “”;
String json_strings[];
performance.clear();
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);
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
Adapt the Android App
- 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.
DefaultHttpClient httpClient = new DefaultHttpClient();
httpClient.getCredentialsProvider().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(“<HANA user name>”, “<your pw>”));
HttpPost httpPost = new HttpPost(“http://<hana.server.name>:80<HANA_instance_number>/androidapphana/StockDataIF.xsodata/$batch“)
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 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;
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);
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
} 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
URL myhana = new URL(
URLConnection hanacon;
hanacon = myhana.openConnection();
hanacon.setReadTimeout(1000);
hanacon.setConnectTimeout(1000);
String basicAuth = “Basic ” + new String(Base64.encode(userpass.getBytes(), 0));
hanacon.setRequestProperty (“Authorization”, basicAuth);
hanacon.getInputStream()));
String inputLine;
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.
- delete the orange lines
JSONrs.add(day_close);
- and replace them by these lines
JSONObject res = results.getJSONObject(j);
JSONrs.add(res.getString(“NSIN_GROWTH”));
- Finally, replace the way how to combine strings for display.
- delete these lines
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());
}
- 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”);
}
Adapt the HANA XS App
- 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
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 (
where NSIN = :NSIN2
and DATE <= :START_DATE ) – 100 )
AS NSIN_GROWTH from “COMPARE_STOCK”.”androidapphana::PRICE_HISTORY”
where NSIN = :NSIN2
and DATE <= :END_DATE
) ;
- 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…)
- In the Project Explorer tab on the left, double click -> androidapponhana –> StockDataIF.xsodata. Add 1 row so that it looks in total:
- 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):
- 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.
What did we learn?
- We can achieve the same goals with 2 different techniques, one using frontend computing and therefore getting some more data to the device – which is normally a bad idea (of course acceptable for 4 fields) – the other one using backend computing in HANA.
- The xsodata service definitions in HANA are pretty lighweight. They offer input and output parameters.
- SQL Script is a powerful tool to do selection and computing (as well as aggregations, call of business functions, predictive ….some restricted imperative programming) in HANA. You would only return the results to the device.
- In SQL Script, the logics of what you want to do has to be transformed because of the SQL approach of handling data.
After making the change in the 1st blog to announce that the 2nd blog is out it was waiting for approval. This has been done - finally.
So, now you should be able to see it again!
Sorry for that, Jan
Jan,
I love the idea of getting in XS and Android programming. I just notices that both the Android SDK and ABAP in Eclipse use the same acronym - ADT.
Any chance you can make the final source code available for the 2 new cases? Doing the delete/add operations is a bit error prone.
Albrecht
Albrecht,
good idea!
I added a section "Source Code Upload" and the ziped content. But please keep in mind that you would still have to change IP addresses, users, passwords and add DB content...
Jan
Hello Jan,
Very nice blog but I'm still stuggeling with the previous -> http://scn.sap.com/blogs/jan_t/2013/02/16/how-to-create-an-android-app-on-sap-hana-one-in-7-steps
It's working up to the part "http://<hana.server.name>:80<HANA_instance_number>/androidapponhana/StockDataIF.xsodata/History"
If I try this it's asking for a user / password, is this the user SYSTEM / password or another one ?
Any idea's
Regards
Hi,
yes, I was just using the SYSTEM / pw.
On HANA One I also used the SYSTEM user.
Does your Security group contain an open TCP port “8000” ? Did you replace the "androidapponhana" with the real project name? Please check again thoroughly!
Jan
Hi,
Thanks for your reply.
What do you mean by Security group ?
I've used the same project name "androidapponhana"
the link I should use, is this something like this ?
"http://<host>:80<instance>/androidapponhana/StockDataIF.xsodata/History" where host is "hanacloud" and instance is "00" like mentioned in http://scn.sap.com/docs/DOC-28191
Regards
Hi,
I mean: AWS Management Console --> Instances --> Security Groups --> (select yours) --> Inbound.
Project name is OK - I just wanted you to check in HANA Studio and in the browser again. And <host> and <instance> (always 00 on AWS) seem OK as well.
And it still doesn't work? You could also try an easier service: <host>:8000/androidapponhana/StockDataIF.xsodata/$metadata
Jan
Hi,
I'm using CloudShare (30 day trial)
Hana studio is installed, but I don't think AWS managment console
Regards
Hi,
please follow me so that I can send your a direct message. I would like to logon to your account.
Jan
Hi,
after getting in touch with you and getting the credentials, I was able to connect my HANA Studio to your HANA server on Cloudshare. It seems as if there were some WiFi / internet connectivity issues. Everything in your HANA server looked fine and the http connection to HANA worked and showed the expected result.
Jan
Hi Jan,
i was trying to create a sample android application as mentioned in your previous blog while launching the application in android emulator , i am getting following error :-
unfortunately, Android app_name has stopped.
Please help on this.
Regards,
Neeraj
Hello Jan,
following your nice tutorial on
how-to-create-an-android-app-on-sap-hana-one-in-7-steps Part 1
in similar fashion i have got o-data response of table in Web-based Development Workbench in HCP Trail account
i want to parse it and consume that o-data in android device in the same way you parsed your o-data in JSON format in part 1.
the manner of o-data generated is different in tutorial of part1 and o-data in WEB ID is different although it give o-data in output
can you assist me for that i have attached WEB ID and it response when service.odata file is executed.
i tried the method as of part1 like
try {
URL myhana = new URL(
"https://s8hanaxs.hanatrial.ondemand.com/p1941467736trial/test/Demo8XS/services.xsodata?$format=json");
URLConnection hanacon;
hanacon = myhana.openConnection();
hanacon.setReadTimeout(1000);
hanacon.setConnectTimeout(1000);
String userpass = "DEV_6FP7U6IHUDUEWSM07IXW29C9H" + ":" + "xxx";
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";
}
but it give
System.err(17209): org.json.JSONException: Value You of type java.lang.String cannot be converted to JSONObject
i tried other ways also bit it gives file not found on device
Regards & Thanks
Lakhan