Embedded integration using Android Studio

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.

Prepare your working environment

For being able to compile your application you need to have Android Studio on your computer. If you don't have it please follow Download Android Studio.
As the key point you need to obtain Sygic library aar. You may retrieve it from the demo or relase Android package or contact your sales representative.
Not necessarily but we recommend first to test the compilation flow using the IntegDemo3D project within the Sygic SDK package. To get the demo of Embedded SDK version please contact us.

Create new project

Let's start Android Studio and start the new project through Start a new Android Studio project, name the application e.g. MyApp, and name the package e.g. com.sygic.example.hello3dwiw. Let's define the minimum SDK version API 19, and let's select the template Empty Activity.
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:

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @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" />

</RelativeLayout>

Include Sygic library

Including Sygic library consists of three steps:

  • import SygicLib.aar into the project from your SDK package
    Go through File -> New -> New Module -> Import jar -> Browse for SygicLib.aar
  • add project's library dependency to SygicLib.aar
    Right click on app, then Open Module Settings, then Dependencies, then SygicLib
  • add project's dependency to Google play services
    Right click on app, then Open Module Settings, then Dependencies, then com.google.android.gms.play-service:8.4.0 or newer version

The project should look as below.

Define graphical look

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 segments,
one, which defines the UI interface for address input (Editext edit1) and the button, which triggers the navigation(Button button1)
second, which prepares placeholder for Sygic navigation pannel (FrameLayout sygicmap).
Thus we can modify the default generated layout to the following form

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="com.example.rcmar.mysygicwiw.MainActivity"
    android:weightSum="1">

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

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

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

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

    </LinearLayout>

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

       <FrameLayout
           android:id="@+id/sygicmap"
           android:layout_width="match_parent"
           android:layout_height="match_parent">

           <fragment
               android:layout_width="match_parent"
               android:layout_height="match_parent"
               android:name="com.sygic.example.wiwdemo3d.SygicNaviFragment"
               android:layout_gravity="center_horizontal" />

       </FrameLayout>

    </LinearLayout>

</LinearLayout>

Modify AndroidManifest.xml

The following 3 modifications are needed:

  • add tools:replace="icon, label, theme" into application section
  • add android:configChanges="orientation|keyboardHidden|screenSize" into activity section
  • optionally add application permissions

The xml could look as follows:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.sygic.example.wiwdemo3d">

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.CALL_PHONE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:replace="icon, label, theme">

        <activity android:name="com.sygic.example.hello3dwiw.MainActivity"
            android:configChanges="orientation|keyboardHidden|screenSize"
            >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Optional modification

If you have your own application class it is required to update the manifest tools:replace setting as follows:

tools:replace="icon, label, theme, android:name"
        android:name="android.support.multidex.MultiDexApplication"&gt;

Adapt proguard rules

In order to avoid compilation errors or application crashes sometimes it is necessary to adapt the proguard-rules.pro file.
The recommended setup as as follows:

-dontobfuscate
-dontwarn java.awt.**
-dontwarn io.fabric.**
-dontwarn com.crashlytics.android.**
-dontwarn com.facebook.**
-dontwarn loquendo.tts.**
-dontwarn com.google.android.**
-dontwarn com.sygic.aura.downloader.**
-dontwarn com.flurry.**

-keep class com.sygic.aura.SygicMain {*;}
-keep class com.sygic.aura.SygicProject {*;}
-keep class com.sygic.aura.clazz.* {*;}
-keep class com.sygic.aura.sidebar.* {*;}
-keep class com.sygic.aura.feature.http.* {*;}
-keep class com.sygic.sdk.api.** {*;}
-keep class com.google.android.gms.**
-keep class com.flurry.** {*;}

-keepattributes *Annotation*,EnclosingMethod,Signature
-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class com.sygic.aura.WebActivity$WebViewJsInterface {
    public *;
}

Adapt build.gradle

In order to avoid application crashes sometimes it is necessary to adapt the build.gradle file.
Please make sure the dependencies sections looks similar:

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.2.1'
    compile 'com.android.support:design:23.2.1'
    compile 'com.google.android.gms:play-services:8.4.0'
    compile 'com.flurry.android:analytics:6.3.1'
    compile project(':SygicLib')
}

Code the application

The following four modifications steps are minimum to be applied:

  • define Sygic navigation UI framework by extending MainActivity class
  • add SygicNaviFragment class into applicatiom
  • add SygicNaviCallback class into application
  • add Sygic API call to allow for navigation operation
  • optionally add application permissions (required for API version 23 and higher)
    Please check Permissions management

1. Define navigation UI framework

The following code is the typical framework to pick when starting to write an application using Sygic Window in Window mode.
The function initUI() instantiates Sygic navigation fragment and passes it into Fragment manager.
The Sygic navigation then requires that the methods (onCreateDialog, onPrepareDialog, onActivityResult) are overriden with the code bellow.

public class MainActivity extends AppCompatActivity
{
    private SygicNaviFragment fgm;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initUI();
    }

    private void initUI() {
        setContentView(R.layout.activity_main);
        fgm = new SygicNaviFragment();
        getSupportFragmentManager().beginTransaction().replace(R.id.sygicmap, fgm).commit();
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        Dialog dlg = fgm.onCreateDialog(id);
        if (dlg == null)
            return super.onCreateDialog(id);
        return dlg;
    }

    @Override
    protected void onPrepareDialog(int id, Dialog dialog) {
        super.onPrepareDialog(id, dialog);
        fgm.onPrepareDialog(id, dialog);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        fgm.onActivityResult(requestCode, resultCode, data);
    }

}

2. Add Sygic Navigation Fragment class

The bare minimum required implementation of the SygicNaviFragment class should be as follows, i.e. defining start of navigation and setting callback reference within the onResume() method.

import com.sygic.aura.embedded.SygicFragmentSupportV4;

public class SygicNaviFragment extends SygicFragmentSupportV4 {

    @Override
    public void onResume() {
        startNavi();
        setCallback(new SygicNaviCallback(getActivity()));
        super.onResume();
    }
}

3. Add Sygic navigation callback class

The implementation of the callback class is neccessary if we want to react on some navigation events.
The callback class must be inherited from the Sygic IAPiCallback class. The basic implementation defines the constructor, through which the main activity reference is passed.
And interestingly we need to fill the mandatory onEvent method, through which various events are coming from navigation and can be processed as desired.
The example here shows the navigation exit event is captured, on which we close the whole application.

import com.sygic.aura.embedded.IApiCallback;
import com.sygic.sdk.api.events.ApiEvents;

public class SygicNaviCallback implements IApiCallback {

    private Activity mActivity;

    public SygicNaviCallback(Activity activity) {
        mActivity = activity;
    }

    @Override
    public void onEvent(final int event, final String data) {
        if(event == ApiEvents.EVENT_APP_EXIT) {
            mActivity.finish();
        }
    }

    @Override
    public void onServiceConnected() {
    }

    @Override
    public void onServiceDisconnected() {
    }
}

4. Add navigation operation through API call

We will call the navigation function from the button click on our designed button through its OnClick method.
Inside OnClick we read the user typed address and use it in the API call navigateToAddress. Here we use the API call using the thread mechanics.
Thus the whole functionality can be defined by extending the initUI() function as follows.

private void initUI() {
        setContentView(R.layout.activity_main);
        fgm = new SygicNaviFragment();
        getSupportFragmentManager().beginTransaction().replace(R.id.sygicmap, fgm).commit();

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

    }

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 package you obtain and are contained inside the Navigation/Sygic3D/SygicLib folder. The resources need to be copied into SygicLib folder on the device.
Example:

sdcard\
    SygicLib\
      Android\
      Res\
      Maps\

Sygic navigation will search for the SygicLib folder to read resources.

Attention

it is important that the resources are aligned with the library version. Therefore it is strongly recommended that for an application development the resources and library are taken from the same release package.

Build and deploy application

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

Full sample reference

package com.sygic.example.hello3dwiw;

import android.app.Dialog;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.sygic.sdk.api.ApiNavigation;

import com.sygic.aura.utils.PermissionsUtils;
import com.sygic.sdk.api.exception.GeneralException;

public class MainActivity extends AppCompatActivity
{
    public static final String LOG_TAG = "hello3dwiw";

    private SygicNaviFragment fgm;

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

        if(PermissionsUtils.requestAllPermissions(this) == PackageManager.PERMISSION_GRANTED)
        {
            initUI();
        }
    }

    private void initUI() {
        setContentView(R.layout.activity_main);
        fgm = new SygicNaviFragment();
        getSupportFragmentManager().beginTransaction().replace(R.id.sygicmap, fgm).commit();

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

    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        for(int res : grantResults) {
            if(res != PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "You have to allow all permissions", Toast.LENGTH_LONG).show();
                finish();
                return;
            }
        }

        // all permissions are granted
        initUI();
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        Dialog dlg = fgm.onCreateDialog(id);
        if (dlg == null)
            return super.onCreateDialog(id);
        return dlg;
    }

    @Override
    protected void onPrepareDialog(int id, Dialog dialog) {
        super.onPrepareDialog(id, dialog);
        fgm.onPrepareDialog(id, dialog);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        fgm.onActivityResult(requestCode, resultCode, data);
    }
}

// ------ class SygicNaviFragment ------

package com.sygic.example.hello3dwiw;

import com.sygic.aura.embedded.SygicFragmentSupportV4;

public class SygicNaviFragment extends SygicFragmentSupportV4 {

    @Override
    public void onResume() {
        startNavi();
        setCallback(new SygicNaviCallback(getActivity()));
        super.onResume();
    }
}

// ------ class SygicNaviCallback ------

package com.sygic.example.hello3dwiw;

import android.app.Activity;
import android.util.Log;
import android.widget.Toast;

import com.sygic.aura.embedded.IApiCallback;
import com.sygic.sdk.api.events.ApiEvents;

public class SygicNaviCallback implements IApiCallback {

    private Activity mActivity;

    public SygicNaviCallback(Activity activity) {
        mActivity = activity;
    }

    @Override
    public void onEvent(final int event, final String data) {
        if(event == ApiEvents.EVENT_APP_EXIT) {
            mActivity.finish();
        }

        mActivity.runOnUiThread(
                new Runnable() {
                    @Override
                    public void run() {
                        String str = "Hello Event";
                        Toast.makeText(mActivity, str, Toast.LENGTH_SHORT).show();
                    }
                }
        );

    }

    @Override
    public void onServiceConnected() {
        Log.d(MainActivity.LOG_TAG, "service connected");
    }

    @Override
    public void onServiceDisconnected() {
        Log.d(MainActivity.LOG_TAG, "service disconnected");
    }
}