viernes, 11 de septiembre de 2015

SAP HANA through the looking Glass

This post was originally posted on SAP HANA through the looking Glass.



If you are an SAP Employee, please follow us on Jam.


Even when Google Glass is no more...at least not for individuals…I thought that connecting it to SAP HANA would make for a nice blog...because after all we have one at the d-shop…so here we go -;)


First, we need to create a Calculation View and call it “FLIGHTS_BY_CARRIER”. It will be composed of two tables, SCARR and SFLIGHT.

First, we need to create a Join object and link the table by MANDT and CARRID. From here select the following fields as output MANDT, CARRID, CARRNAME, PRICE and CURRENCY.

Then create an Aggregation object selecting the fields CARRNAME, PRICE (As Aggregated Column) and CURRENCY. Filter the CURRENCY field by ‘USD’.

Then create a Projection object and select only PRICE and CARRNAME.

On the Semantics object make sure to select “CROSS CLIENT” as the Default Client.


Now, switch to the SAP HANA Development View and create a new repository. Call it “Flights”.

Create a new “XS Engine” project and call it “Flights” as well. Link it to the “Flights” repository.

Create an empty “.xsapp” file.

Create a file called “.xsaccess” with the following code.

.xsaccess
{
          "exposed" : true,
          "authentication" : [ { "method" : "Basic" } ]
}

Finally create a file called “flights.xsodata” with the following code

Flights.xodata
service {
          "Blag/FLIGHTS_BY_CARRIER.calculationview" as "FLIGHTS" keys 
                                                        generate local "Id";
}

Activate your project and launch it on your browser, you should see something like this…


The SAP HANA part is done…so we can move into the Google Glass part…

First, download Android Studio and update your SDK Manager to include the following…API 19 including Google Glass Kit Preview.


Create a new project and call it “GLASS_HANA” or whatever you fancy…


On the next screen, uncheck “Phone and Tablet” and check “Glass”. Make sure that “Glass Development Kit Preview (API 19) is selected.


Choose “Immersion Activity”.


Leave the “MainActiviy” name and change the Activity Title if you want.


On the “MainActivity” file copy the following code

MainActivity.java
package glass.app.com.flightsreport;

import com.google.android.glass.media.Sounds;
import com.google.android.glass.widget.CardBuilder;
import com.google.android.glass.widget.CardScrollAdapter;
import com.google.android.glass.widget.CardScrollView;

import android.app.Activity;
import android.content.Context;
import android.media.AudioManager;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;
import android.speech.RecognizerIntent;
import android.speech.tts.TextToSpeech;

public class MainActivity extends Activity {

    /**
     * {@link CardScrollView} to use as the main content view.
     */
    private CardScrollView mCardScroller;
    private TextToSpeech mTTS;
    private RequestQueue mQueue;

    /**
     * "Hello World!" {@link View} generated by {@link #buildView()}.
     */
    private View mView;

    @Override
    protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);

        mView = buildView();

        mCardScroller = new CardScrollView(this);
        mCardScroller.setAdapter(new CardScrollAdapter() {
            @Override
            public int getCount() {
                return 1;
            }

            @Override
            public Object getItem(int position) {
                return mView;
            }

            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                return mView;
            }

            @Override
            public int getPosition(Object item) {
                if (mView.equals(item)) {
                    return 0;
                }
                return AdapterView.INVALID_POSITION;
            }
        });
        // Handle the TAP event.
        mCardScroller.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView parent, View view, int position, long id) {
                // Plays disallowed sound to indicate that TAP actions are not supported.
                AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
                am.playSoundEffect(Sounds.DISALLOWED);
            }
        });
        setContentView(mCardScroller);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mCardScroller.activate();
    }

    @Override
    protected void onPause() {
        mCardScroller.deactivate();
        mTTS.shutdown();
        super.onPause();
    }

    /**
     * Builds a Glass styled "Hello World!" view using the {@link CardBuilder} class.
     */
    private View buildView() {
        mTTS = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
            @Override
            public void onInit(int status) {
            }
        });
        final CardBuilder card = new CardBuilder(this, CardBuilder.Layout.TEXT);
        mQueue = Volley.newRequestQueue(this);
        final String carrName = getIntent().getExtras().getStringArrayList(RecognizerIntent.EXTRA_RESULTS).get(0);
        HANAFlightsAPI.getFlightsData(carrName, mQueue, new HANAFlightsAPI.Callback() {
            @Override
            public void onFlightsData(HANAFlightsAPI.FlightsData flightsData) {
                card.setText(flightsData.price);
                card.setFootnote(flightsData.carrierName);
                setContentView(card.getView());
            }
        });

        return card.getView();
    }
}

Create a new file and call it “HANAFlightsAPI” and copy the following code

HANAFlightsAPI.java
package glass.app.com.flightsreport;

import android.util.Log;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Base64;
import java.util.HashMap;
import java.util.Map;
import com.android.volley.AuthFailureError;
import java.net.URI;
import java.net.URISyntaxException;

public class HANAFlightsAPI {
    /**
     * Open Weather Map API Endpoint
     */
    public static final String URL = "http://YourServer:8000/Flights/flights.xsodata/FLIGHTS?$format=json&
                                      $filter=CARRNAME%20eq%20";

    /**
     * Object containing qualitative description of weather as well as temperature in Fahrenheit.
     */
    public static class FlightsData {

        public final String carrierName;
        public final String price;

        public FlightsData(String carrierName, String price) {
            this.carrierName = carrierName;
            this.price = price;
        }
    }

    public interface Callback {
        void onFlightsData(FlightsData flightsData);
    }

      public static void getFlightsData(String carrierName, RequestQueue queue, final Callback callback) {
        URI uri = null;
        try {
            uri = new URI(carrierName.replaceAll(" ", "%20"));
        } catch (URISyntaxException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        queue.add(new JsonObjectRequest(URL + "%27" + uri + "%27", null,
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) {
                        String carrier = "";
                        String price = "";
                        try {
                            JSONObject results = (JSONObject) response.getJSONObject("d").getJSONArray("results").get(0);
                            carrier = results.getString("CARRNAME");
                            price = results.getString("PRICE");
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                        callback.onFlightsData(new FlightsData(carrier, price));
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.e("onErrorResponse", error.getMessage());
            }
        }) {

            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                HashMap<String, String> headers = new HashMap<String, String>();
                String creds = String.format("%s:%s", "SYSTEM", "YourPassword");
                String auth = "Basic " + Base64.encodeToString(creds.getBytes(), Base64.DEFAULT);
                headers.put("Authorization", auth);
                return headers;
            }
        });
    }
}

Go the second build.gradle file (the one that says “Module.app”) and add this...

Module.app
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.mcxiaoke.volley:library:1.0.+'
    androidTestCompile 'org.hamcrest:hamcrest-all:1.3'
}

This will add the Volley library which is an HTTP library designed to make Android apps networking easier and faster.

Open the file “voice_trigger.xml” and copy the following code

voice_trigger.xml
<?xml version="1.0" encoding="utf-8"?>

<trigger keyword="Flight Report">
    <input prompt="Which Carrier?"/>
</trigger>

Open the file “strings.xml” and copy the following code

strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Flights Report</string>
    <string name="glass_voice_trigger">flights report</string>
    <string name="glass_voice_prompt">which carrier?</string>
</resources>

Open the file “AndroidManifest.xml” and copy the following code

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="glass.app.com.flightsreport">

    <uses-permission android:name="com.google.android.glass.permission.DEVELOPMENT"/>
    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="Flights Report">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="com.google.android.glass.action.VOICE_TRIGGER"/>
            </intent-filter>
            <meta-data
                android:name="com.google.android.glass.VoiceTrigger"
                android:resource="@xml/voice_trigger"/>
        </activity>

    </application>
</manifest>

We’re almost ready…we need to connect our Google Glass and make sure it’s on Debug On.

Navigate to “Settings” and enter it.


Navigate to “Device info” and click it


Navigate to “Turn on debug” and click it. It should become “Turn off debug”


With that we will be able to link our Google Glass to our Android Studio project. Simply run it and you will see this on the Glass.


Click to enter it and you will see the main screen


Say a carrier name like “American Airlines”


Wait and the application will connect to our SAP HANA OData and display the result


Now, to not make you believe that I’m just making this up…try another one…like “Lufthansa”


Wait to see the result…


Cool, huh? You could also ask for “Delta Airlines” or “United Airlines”…if you ask for something else, it might give you back the result for the most approximate name or just the default one.

I’m happy enough with this little project -;)

Greetings,

Blag.
Development Culture.