Standalone integration using Android Studio

Introduction

In the following we will guide you through creating a simple Android application integrating Sygic navigation engine with a standalone integration (Sygic runs on a separate screen in the fullscreen mode).
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 remote aar library. 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 IpcDemo3D project within the Sygic SDK package. To get the demo 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. Hello3D, and name the package e.g. com.sygic.example.hello3d. 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:

package com.example.sygic.hello3d;

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:

<?xml version="1.0" encoding="utf-8"?>
<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="com.example.sygic.hello3d.MainActivity" >

    <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 three steps:

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

The project should look as below.

Define graphical look

We add three graphical elements into application, which is the EditText for inputting and address and two buttons. The first button (Map) for opening navigation application, and the second button (Navigate) for triggering the navigation functions to a given address.
Thus we may modify the default generated layout to the following form.

<?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:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.sygic.hello3d.MainActivity" >

    <Button
        android:id="@+id/btnMap"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Map" ></Button>

    <EditText
        android:id="@+id/editAddress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:ems="10" >
        <requestFocus ></requestFocus>
    </EditText>

    <Button
        android:id="@+id/btnNavigate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/editAddress"
        android:text="Navigate" ></Button>

</LinearLayout>

Code the application

The following coding steps are minimum to be applied:

  • define Navigation UI framework by extending MainActivity class
  • define API opening and closing operations
  • add the button for Opening Navigation
  • add the button for Invoking the navigate-to-address function

1. Define navigation UI framework

The following code is the typical framework to pick when starting to write an application using Sygic Remote API.
The activity class needs to contain the instance of Sygic Api (mApi) and the instance of ApiCallback (mApiCallback).
The ApiCallback as the interface defines 3 functions mandatory to fill (onServiceConnected, onServiceDisconnected, onEvent).
With the applicaton start (the application basically starts at OnCreate) we need to call the mApi.init function, which binds API with the defined callback and with Sygic navigation.

import android.support.v7.app.AppCompatActivity;
import com.sygic.sdk.remoteapi.Api;
import com.sygic.sdk.remoteapi.ApiCallback;

public class MainActivity extends AppCompatActivity {

    private Api mApi;
    private ApiCallback mApiCallback = new ApiCallback() {

        @Override
        public void onServiceDisconnected() {
        }

        @Override
        public void onServiceConnected() {
        }

        @Override
        public void onEvent(int event, String data) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mApi = Api.init(getApplicationContext(), "com.sygic.fleet", "com.sygic.fleet.SygicService", mApiCallback);
    }
}

2. Define API opening and closing operations

The opening of API interface is implemented with calling connect at the application start right after init as follows:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mApi = Api.init(getApplicationContext(), "com.sygic.fleet", "com.sygic.fleet.SygicService", mApiCallback);
        mApi.connect();
    }

and add registerCallback, which typically resides in onServiceConnected

@Override
    public void onServiceConnected() {
        try {
            mApi.registerCallback();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

Next, we want to close API with an application exit, and the typical choice is to place unregisterCallback and disconnect inside the application's onDestroy method as follows:

@Override
    protected void onDestroy() {
        try {
            mApi.unregisterCallback();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        mApi.disconnect();
        super.onDestroy();
    }

3. Open navigation (Map button)

We will define the Map button, which will serve two purposes. When clicked it will open up Sygic navigation if yet not started, or else if started, it will put it into foreground. This definition belongs to the OnCreate method too.

Button btnMap = (Button)findViewById(R.id.btnMap);
btnMap.setOnClickListener(new View.OnClickListener() {
   public void onClick(View v)
   {
      try {
        mApi.show(false);
      } catch (RemoteException e) {
        e.printStackTrace();
      }
   }
});

4. Invoke navigation function (Navigate button)

We will call the navigation function from the button Navigate through its event function OnClick.
Inside OnClick we will read the user typed address and use it in the API function call NavigateToAddress.
The whole functionality can be defined within default activity OnCreate method as follows:


Button btnNavigate = null;

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

        btnNavigate = (Button)findViewById(R.id.btnNavigate);
        btnNavigate.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                String address = ((EditText)findViewById(R.id.editAddress)).getText().toString();
                try {
                    mApi.show(false);
                    ApiNavigation.navigateToAddress(address, false, 0, 0);
                } catch (NavigationException e) {
                    e.printStackTrace();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

        });

        mApi = Api.init(getApplicationContext(), "com.sygic.fleet", "com.sygic.fleet.SygicService", mApiCallback);
        mApi.connect();
    }

5. Enable/disable Navigate button

For better consistency of the demo application we will enable or disable the Navigate button based on the condition whether navigation is started or not. This is to avoid excessive error management with calling navigation API function when Sygic navigation is not started yet.
For this we need to manipulate the button at four places:

  1. We define globally accessible class variable:

    Button btnNavigate = null;
  2. We set it to disabled with application start within the OnCreate function.

    btnNavigate = (Button)findViewById(R.id.btnNavigate);        
    btnNavigate.setEnabled(false);
  3. We activate or deactivate the button when receiving callback from service connection. Here we check the Sygic application is running or not using yhe API function isApplicationRunning.

@Override
    public void onServiceConnected() {
       ...
       try {
         boolean isRun = Api.isApplicationRunning(1000);
         if (isRun)
         {
             btnNavigate.setEnabled(true);
         }
         else
         {
             btnNavigate.setEnabled(false);
         }
       } catch (GeneralException e) {
          e.printStackTrace();
       }

    }
  1. We enable or disable the button when receiving callback to the OnEvent method. Here we check the API events EVENT_APP_STARTED and EVENT_APP_EXIT, which signal the start or the exit of the Sygic navigation.
    Here we use the pattern to handle events within the UI thread as it is prohibited to handle UI functions outside UI threads.
    This pattern is generally safe pattern for performing any operations within the OnEvent method.
   public void onEvent(int event, String data) {
       eventHandler.obtainMessage(event, data).sendToTarget();
   }

   Handler eventHandler = new Handler(new Handler.Callback() {

        @Override
        public boolean handleMessage(Message msg) {
            int event = msg.what;
            String data = (String)msg.obj;
            if (event == ApiEvents.EVENT_APP_STARTED)
            {
                btnNavigate.setEnabled(true);
            }
            if (event == ApiEvents.EVENT_APP_EXIT)
            {
                btnNavigate.setEnabled(false);
            }

            return false;
        }
    });

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 Hello3D.apk, which is normally automatically deployed on your Android device and run.

You also need to copy and install Sygic Navigation to device. Please note in this example it should be Sygic Professional Navigation.
If you have not done that yet please follow Installation of navigation.

Running the application should yield the following outcome.

Full sample reference

package com.example.sygic.hello3d;

import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import com.sygic.sdk.remoteapi.Api;
import com.sygic.sdk.remoteapi.ApiCallback;
import com.sygic.sdk.remoteapi.ApiNavigation;
import com.sygic.sdk.remoteapi.events.ApiEvents;
import com.sygic.sdk.remoteapi.exception.GeneralException;
import com.sygic.sdk.remoteapi.exception.NavigationException;

public class MainActivity extends AppCompatActivity {

    private Api mApi;
    Button btnNavigate = null;
    private ApiCallback mApiCallback = new ApiCallback() {

        @Override
        public void onServiceDisconnected() {
        }

        @Override
        public void onServiceConnected() {
            try {
                mApi.registerCallback();
            } catch (RemoteException e) {
                e.printStackTrace();
            }

            try {
                boolean isRun = Api.isApplicationRunning(1000);
                if (isRun)
                {
                    btnNavigate.setEnabled(true);
                }
                else
                {
                    btnNavigate.setEnabled(false);
                }
            } catch (GeneralException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onEvent(int event, String data) {
            eventHandler.obtainMessage(event, data).sendToTarget();
        }
    };

    Handler eventHandler = new Handler(new Handler.Callback() {

        @Override
        public boolean handleMessage(Message msg) {
            int event = msg.what;
            String data = (String)msg.obj;
            if (event == ApiEvents.EVENT_APP_STARTED)
            {
                btnNavigate.setEnabled(true);
            }
            if (event == ApiEvents.EVENT_APP_EXIT)
            {
                btnNavigate.setEnabled(false);
            }

            return false;
        }
    });

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

        Button btnMap = (Button)findViewById(R.id.btnMap);
        btnMap.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v)
            {
                try {
                    mApi.show(false);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });

        btnNavigate = (Button)findViewById(R.id.btnNavigate);
        btnNavigate.setEnabled(false);
        btnNavigate.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                String address = ((EditText)findViewById(R.id.editAddress)).getText().toString();
                try {
                    mApi.show(false);
                    ApiNavigation.navigateToAddress(address, false, 0, 0);
                } catch (NavigationException e) {
                    e.printStackTrace();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

        });

        mApi = Api.init(getApplicationContext(), "com.sygic.fleet", "com.sygic.fleet.SygicService", mApiCallback);
        mApi.connect();
    }

    @Override
    protected void onDestroy() {
        try {
            mApi.unregisterCallback();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        mApi.disconnect();
        super.onDestroy();
    }
}