Embedded integration for Sygic2D

Introduction

In the following we will guide you through creating a simple Android application with embedding Sygic navigation engine through window in window style.
In this example we will develop a simple application, where you can type in an address string and through a custom button you can start navigation to that address.

Prerequisites

For being able to compile your application you need to have Eclipse ADT on your computer. If you don't have it please follow Download Eclipse ADT.
As the key point you need to obtain Sygic jar library files and Sygic resources. They are both contained within our Android Sygic SDK package, which you might obtain by downloading demo or contacting your sales representative.
Not necessarily but we recommend first to test the compilation flow using the IntegDemo2D project within the Android Sygic SDK package.

Create new project

Let's start Eclipse and create the new project through File -> New -> Android Application Project.
The project is automatically populated with several files, while these are relevant:

  • MainActivity.java, which is the placeholder for application coding (java code)
  • activity_main.xml, which will define graphical look of application (layout form)

The java code should be generated as follows:

package com.sygic.example.helloembed2d;
import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

The layout form should be generated as follows:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${packageName}.${activityClass}">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" ></TextView>

</RelativeLayout>

Include Sygic library

Including Sygic library consists of copying complete library subtree from your SDK package (\SDK\Lib\2d.integrated ) into the project's lib folder. Eclipse should automatically absorb it into project (might require a refresh operation on the libs folder).
The libs folder should look as follows:

  libs\
     armeabi\
        libApplicationAPI.so
        libLTTS7Allison.so
        ...
        libLTTS7XML.so
        libsygic.so
        libttsloquendoengine.so
     android-support-v4.jar
     Loqjapi.jar
     LTTS_wrapper.jar
     SygicDrive.jar

Finally the project setup should look as bellow.

 

Modify AndroidManifest.xml

The following modifications are needed:

  • add into section
  • add application permissions

The xml could look as follows:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.sygic.sdk.template"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="17"
        android:targetSdkVersion="21" ></uses>

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" ></uses>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" ></uses>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" ></uses>
    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ></uses>
    <uses-permission android:name="android.permission.INTERNET" ></uses>
    <uses-permission android:name="android.permission.READ_PHONE_STATE" ></uses>
    <uses-permission android:name="android.permission.CALL_PHONE" ></uses>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" ></uses>
    <uses-permission android:name="android.permission.READ_CONTACTS" ></uses>
    <uses-permission android:name="android.permission.VIBRATE" ></uses>
    <uses-permission android:name="android.permission.SEND_SMS" ></uses>
    <uses-permission android:name="android.permission.CAMERA" ></uses>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" ></uses>
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" ></uses>

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_sdk" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" ></action>
                <category android:name="android.intent.category.LAUNCHER" ></category>
            </intent-filter>
        </activity>

        <service android:name="com.sygic.drive.SygicService" >
        </service>
    </application>

</manifest>

Code basic Sygic application

The following two steps need to be applied to get a simple Sygic integration:

  • Update activity_main.xml layout by adding a SurfaceView element
  • Update MainActivity.java with a basic Sygic integration pattern

Layout update

In order to support embedding Sygic navigation on screen in a window-in-window look the layout must contain the placeholder. Thus we can modify the default generated layout to the following form:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <SurfaceView
        android:id="@+id/surface"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></SurfaceView>

</RelativeLayout>

Main Activity update

The following code is the typical framework pattern to pick when starting to write an application for the embedding integration.
The default generated MainActivity.java can fully be replaced with the bellow code. It contains the following mechanics:

  • binding Sygic service to the connection class mConnection providing onServiceConnected and onServiceDisconnect callbacks
  • augmenting Android onPause and onResume methods to bind and unbind with Sygic for performance efficiency
  • rendering Sygic navigation to the surface layout R.id.surface inside onServiceConnected
  • running Sygic navigation using mService.runNavi() inside onServiceConnected
  • registering Sygic onEvent callback using mService.registerCallback(...) inside onServiceConnected
  • definition of a callback mCallback to capture navigation events using OnEvent method
package com.sygic.example.helloembed2d;

import android.app.Activity;
import android.content.*;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.KeyEvent;
import android.view.SurfaceView;
import com.sygic.drive.LocalBinder;
import com.sygic.drive.SygicService;
import com.sygic.sdk.api.ApiCallback;
import com.sygic.sdk.api.events.ApiEvents;

public class MainActivity extends Activity {

    private static final String LOG_TAG = MainActivity.class.getCanonicalName();

    private SygicService mService;
    private ApiCallback mCallback;
    private Intent mBindIntent;
    private boolean mStarted = false;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // set up the screen
        setContentView(R.layout.activity_main);

        // create Api Callback
        mCallback = new ApiCallback() {

            // callback to handle events from Navigation
            @Override
            public void onEvent(final int event, final String data) {
                switch (event) {
                    case ApiEvents.EVENT_APP_STARTED:
                        mStarted = true;
                        break;
                    case ApiEvents.EVENT_APP_EXIT:
                        mStarted = false;
                        finish();
                        break;
                }
            }
        };

        // start the service,
        // we want to keep the reference to the intent so we can reuse it later
        // to bind and unbind with service
        mBindIntent = new Intent(this, SygicService.class);
        startService(mBindIntent);
    }

    /**
     * unbind the service
     */
    @Override
    protected void onPause() {
        super.onPause();
        if (mService != null) {
            unbindService(mConnection);
        }
    }

    /**
     * bind the service, will start onServiceConnected
     */
    @Override
    protected void onResume() {
        super.onResume();
        bindService(mBindIntent, mConnection, BIND_AUTO_CREATE);
    }

    // we implement a service connection which is used when binding to the service
    private ServiceConnection mConnection = new ServiceConnection() {

        // this is called asynchronously when a successful connection has been established
        public void onServiceConnected(ComponentName className, IBinder service) {
            Log.d(LOG_TAG, "Service connected...");
            mService = ((LocalBinder) service).getService();
            if (mService != null) {
                // set the surface to draw onto
                mService.setSurface((SurfaceView) findViewById(R.id.surface));

                if (!mStarted) {
                    // register the callback to receive the events and run the Navigation
                    mService.registerCallback(mCallback);
                    mService.runNavi();
                }
            }
        }

        // this is called when we get disconnected, e.g. the service gets closed unexpectedly
        public void onServiceDisconnected(ComponentName className) {
            Log.d(LOG_TAG, "Service disconnected...");
            mService = null;
        }
    };

    @Override
    protected void onDestroy() {
        mService = null;
        super.onDestroy();
    }
}

Navigation resources

Finally we also need to copy Sygic Navigation resources into device file system, i.e. typically internal or external SD card. The resources are part of the Android Sygic SDK package you obtain and are contained inside the Navigation folder. The resources, i.e. 3 folders (Drive, Res, Maps) need to be copied into the root folder on your device.
Example:

sdcard\
    Drive\
    Res\
    Maps\

Build and deploy application

Building the application is simple as clicking on Run button in Eclipse.
The result of the build process is generation of HelloEmbed2D.apk, which is normally automatically deployed on your Android device and run.
Running the application should yield the following appearance.

 

Code UI extension for address input and navigate button

In this section we will add the GUI support for address input to navigate to. This will be done by augmenting current layout and adding API call to Sygic from a button click.

Layout update

As an example of the window-in-window look we can prepare the layout in such a way, that it splits the screen into 2 visual segments,
one, which defines the UI interface for address input (Editext edit1) and the button, which triggers the navigation(Button button1),
and second, the navigation panel represented by SurfaceView placeholder.
Thus we can modify the default generated layout to the following form:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:orientation="vertical"
        android:layout_weight="0.25">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:textSize="20sp"
            android:paddingBottom="10sp"
            android:text="Hello World!" ></TextView>

        <EditText
            android:id="@+id/edit1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="E.g. deu,berlin,alexanderplatz,10" ></EditText>

        <Button
            android:id="@+id/button1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Navigate to Address" ></Button>

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.75">

        <SurfaceView
            android:id="@+id/surface"
            android:layout_width="match_parent"
            android:layout_height="match_parent"></SurfaceView>

    </LinearLayout>

</LinearLayout>

Code update

We will add a button on click method, which reads address from the edittext and triggers Sygic navigation API function NavigateToAddress. All functionality can be wrapped inside InitUI() method called from within OnCreate().

private void initUI()
    {
        final EditText address = (EditText)findViewById(R.id.edit1);

        Button btn = (Button) findViewById(R.id.button1);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread() {
                    public void run() {
                        try {
                            ApiNavigation.navigateToAddress(address.getText().toString(), false, 0, 5000);
                        } catch (GeneralException e) {
                            e.printStackTrace();
                        }
                    }
                }.start();
            }
        });

    }

Build and run

The new run should result in the following appearance.

 

Code extension for external key support

If you want the Sygic navigation to react to external key buttons (like volume up and down, back, etc) the following two methods onKeyDown and onKeyUp need to be overriden and thus added to the code as follows:

@Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
            AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
            if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)
                am.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, AudioManager.FLAG_SHOW_UI);
            else am.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI);
            return true;
        }

        mService.keyMessage(keyCode, event);
        // search is consumed by the navigation
        if (keyCode == KeyEvent.KEYCODE_SEARCH)
            return true;

        if(keyCode == KeyEvent.KEYCODE_BACK) {
            boolean onBack = mService.onBackPressed();
            // disabled in settings.ini; do what you want
            if (!onBack)
                ;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP)
            return true;

        mService.keyMessage(keyCode, event);
        // search is consumed by the navigation
        if (keyCode == KeyEvent.KEYCODE_SEARCH)
            return true;
        return super.onKeyDown(keyCode, event);
    }

Full sample reference

tbd