Friday, September 11, 2015

SAP HANA a través del Glass con que se mira

Este post fué posteado originalmente en SAP HANA through the looking Glass.



Si eres un empleado de SAP, por favor siguenos en Jam.


Aún cuando Google Glass ya no va más...al menos no para el consumo masivo…Pensé que conectarlo a SAP HANA serviría para poder escribir un buen blog...puesto que después de todo, tenemos uno en el d-shop…así que aquí vamos -;)


Primero, debemos crear un objeto Join y asociar la tabla por MANDT y CARRID. De ahí, seleccionar los siguientes campos como output MANDT, CARRID, CARRNAME, PRICE y CURRENCY.

Luego crear un objeto Aggregation seleccionándo los campos CARRNAME, PRICE (Como columnas agregadas) y CURRENCY. Debemos filtrar el campo CURRENCY por ‘USD’.

Luego, debemos crear un objeto Projection y seleccionar solo PRICE y CARRNAME.

En el objeto Semantics asegúrense de marcar  “CROSS CLIENT” como cliente por defecto.


Ahora, cambiemos a la vista SAP HANA Development y creemos un nuevo repositorio. Lo llamaremos“Flights”.

Creamos un proyecto “XS Engine” y también lo llamamos “Flights”. Lo enlazamos con el repositorio “Flights”.

Creamos un archivo vacio llamado “.xsapp”.

Creamos un archivo llamado “.xsaccess” con el siguente código.

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

Finalmente creamos un archivo llamado “flights.xsodata” con el siguiente código

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

Activamos el proyecto y lanzamos nuestro browser...deberíamos ver algo como esto…


La parte de SAP HANA está lista…así que ahora podemos movernos a la parte de Google Glass…

Primer, debemos descargar Android Studio y actualizar el SDK Manager para incluir lo siguiente…API 19 incluyéndo Google Glass Kit Preview.


Creamos un nuevo proyecto y lo llamamo “GLASS_HANA” o cualquier cosa que se les ocurra…


En la siguiente pantalla, quiten el check “Phone and Tablet” y marquen “Glass”. Asegurense de que “Glass Development Kit Preview (API 19) está seleccionado.


Escogemos “Immersion Activity”.


Dejamos el nombre “MainActiviy” y pueden cambiar el Activity Title is quieren....


En el archivo “MainActivity” copien el siguiente código...

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();
    }
}

Creamos un nuevo archivo y lo llamamos “HANAFlightsAPI” y copiamos el siguiente código

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

Vamos al segundo archivo build.gradle (el que dice “Module.app”) y agregamos esto...

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

Esto va a agregar la Volley library la cual es una librería HTTP diseñana para hacer que las aplicaciones de networking con Android sean más sencillas y más veloces.

Abrimos el archivo “voice_trigger.xml” y copiamos el siguiente código...

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

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

Abrimos el archivo “strings.xml” y copiamos el siguiente código...

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>

Abrimos el archivo “AndroidManifest.xml” y copiamos el siguiente código

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>

Estamos casi listos…necesitamos conectar nuestro Google Glass y asegurárnos de que está en Debug On.

Navegamos hasta “Settings” e ingresamos.


Navegamos hasta “Device info” y lo seleccionamos


Navegamos hasta “Turn on debug” y lo seleccionamos. Debería convertirse en “Turn off debug”


Con eso podremos ser capacer de enlazar nuestro Google Glass con nuestro proyecto en Android Studio. Simplimente lo ejecutamos y lo veremos en Google Glass.


Hacemos click para entrar y veremos la pantalla principal


Di el nombre de una aerolínea como “American Airlines”


Espera y la aplicación se conectará con nuestro ODATA en SAP HANA y mostrará el resultado


Ahora, para no hacerlos pensar que me estoy inventándo esto…intentemos con otra…como “Lufthansa”


Esperen a ver el resultado…


Bueno, no? También pueden preguntar por “Delta Airlines” o “United Airlines”…si preguntamos por algo más, nos dará el resultado del nombre más aproximado o el del valor por defecto.

Estoy lo suficientemente feliz con este pequeño proyecto -;)

Saludos,

Blag.
Development Culture.

No comments: