01 First project
Overview
This tutorial will teach you the basics to create a project with the NavigationSDK in Android Studio. You will learn how to add the NavigationSDK as a project dependency, how to initialize the SDK correctly and which additional data you need to use the SDK.
NavigationSDK files
docs
(SDK documentation files)javadoc
(SDK Java wrapper API documentation)
maps
(prepackaged map data)sdcard
(necessary data to run the tutorials)sdk
(NavigationSDK library)tutorial
(tutorial sources)license.key
(license key for the SDK)
Attention
You get a test license.key
file with your copy of the NavigationSDK. This license file enables you to test the SDK until a specified date. Please contact PTV if your test license.key has expired.
Create your first NavigationSDK project
Please use com.ptvag.navigation.tutorial
as the package name to be in sync with the data directory, we later will introduce.
To create our first NavigationSDK project, you have to create a new Android Project with an empty Activity (Hint: Creating an Android Project).
Set the Target-SDK version to 22 to avoid problems in later tutorials with permission requests (Background: Android target versions > 22 will not grant permissions to any resources like GPS or network when the user not explicitly allows the permission. If you want to use a newer target version, please implement the user request in the corresponding tutorial for yourself).
To add the NavigationSDK to your project click on File > New > Import Module...
in the Android Studio an select the navigationSDK
folder from the sdk
folder.
Now add the NavigationSDK module as a dependency to your app Module-level Build File. Add an implementation dependency to the dependencies section.
dependencies { implementation project(':navigationSDK') }
Prepare the map data
Before we can use the SDK, your app needs some app data on the device. To get the directories where android allows us to put the map data, we edit the MainActivity.java
:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); File dir = getExternalFilesDir(null); Log.v("Installation Folder", dir.getAbsolutePath()); } }
Run your application and have a look at the logcat output to get our data installation folder. It should look something like this:
/<device dependent part>/Android/data/<yourpackage>/files
Usually your Android system has already created this path during the first start of your application. Create three directories in this path on the device and copy your data there.
maps
(copy your map data here)
The maps directory is mandatory. All used maps must be copied to this folder. Copy only the files of the map folders, the maps directory does not accept subfolders. Deleting single files of a map will cause the SDK to not initialize, so be sure to always have complete maps consisting of many files with the same prefix.addr
(copy your POI data here)
The addr directory contains the POI data and can be left empty if POIs are not used.-
data
(copy the rendering data and yourlicense.key
here)
This directory contains the subfoldersbmp
,renderer
andprofiles
for this tutorials.Generally, all files besides the
license.key
in the data directory are optional. The SDK will still work without the rendering data, but the map will look incomplete.In the top level, the
*.cmp
files are used for all text renderings on the map. If you delete them, no street or town names will be rendered anymore. All other files in the top level directory of data can be removed, they are only used for the tutorials.In the
data\bmp
directory you can find all icons that are directly rendered onto the map - like POI or restriction icons. If you don't need any of these icons, you can delete them. Please have in mind that a blue dot will be rendered for each POI as a fallback incase of POI data is present but icons are missing.In the
data\renderer
directory reside the map design files. You can remove this directory if you don't want to change the map design.The
date\profiles
directory contains files with predefined route options for various vehicles (see tutorial 5 for loading such files).
Shortcut
Your SDK delivery contains a sample sdcard
folder which holds all necessary data for all tutorials. Just replace the files inside the maps
folder with your favored maps and copy the Android
folder to your sdcard. You need about 4GB of free space on your sdcard. Copying can be done for example with the adb call: adb push Android /mnt/sdcard/
.
Attention
Please check the presence of the license.key
key file as mentioned above.
Initialize the NavigationSDK
To initialize the NavigationSDK, we edit the MainActivity again and add a NavigationSDK.initialize()
call. Initialization needs three paths and a map name. The three paths are the maps, addr and data directories that you just have created. The map name is the file name without the file extension (e.g. deu).
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); File dir = getExternalFilesDir(null); try { NavigationSDK.initialize( new File(dir, "maps"), "deu", new File(dir, "addr"), new File(dir, "data") ); } catch (NavigationException e) { e.printStackTrace(); } } }
That's it. You can now use the NavigationSDK.
Attention
If initialization throws an NavigationException with code: -53
you either forgot to copy the license.key
file to your data directory or your test license.key has expired.
Improvements
To avoid reinitializing the NavigationSDK every time the MainActivity is created, you should move the initialization code to a new class maintaining the global application state. Create a new class called Application extending android.app.Application:
public class Application extends android.app.Application { @Override public void onCreate() { super.onCreate(); File dir = getExternalFilesDir(null); try { NavigationSDK.initialize( new File(dir, "maps"), "deu", new File(dir, "addr"), new File(dir, "data") ); } catch (NavigationException e) { e.printStackTrace(); } } }
AndroidManifest.xml
through setting the android:name
attribute (Hint: Application class documentation).
... <application android:name=".Application" ... /> ...
Now the SDK will be initialized only once the application is created.
As a helper for further tutorials we also save the paths to static members to easily access these directories later.
public class Application extends android.app.Application { private static File basePath; private static File mapsPath; private static File addrPath; private static File dataPath; @Override public void onCreate() { super.onCreate(); basePath = getExternalFilesDir(null); mapsPath = new File(basePath, "maps"); addrPath = new File(basePath, "addr"); dataPath = new File(basePath, "data"); try { NavigationSDK.initialize( mapsPath, "deu", addrPath, dataPath ); } catch (NavigationException e) { e.printStackTrace(); } } public static File getBasePath() { return basePath; } public static File getMapsPath() { return mapsPath; } public static File getAddrPath() { return addrPath; } public static File getDataPath() { return dataPath; }
Error Handling
For the sake of this tutorial we implement a minimal error handling for the initialization of the SDK, so the user gets a failure message if he is missing some of the necessary SDK files. Because the initialization happens in Application::onCreate()
where we have no GUI yet, we save possible errors in a static member so we can evaluate it later in the MainActivity
.
public class Application extends android.app.Application { ... private static Throwable startupException; public static boolean hadStartupException() { return startupException != null; } public static Throwable getStartupException() { return startupException; } @Override public void onCreate() { super.onCreate(); ... try { NavigationSDK.initialize( mapsPath, "DEU", addrPath, dataPath ); } catch (NavigationException e) { startupException = e; return; } } ... }
Our approach is to show the startup error by replacing the MainActivity
layout with an error layout called activity_startup_error.xml
.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorPrimaryDark"> <ImageView android:id="@+id/imageView2" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:src="@drawable/error" /> <TextView android:id="@+id/errorMessageTextView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:inputType="textMultiLine" android:maxLines="10" android:singleLine="false" android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium" tools:text="ErrorMessage" /> </LinearLayout>
We replace the layout in the activities onCreate()
an use the exceptions message as an error text for the user. We also return early from all livecycle methods to prevent any logic from executing when an startup error occured.
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (Application.hadStartupException()) { showStartupError(); return; } setContentView(R.layout.activity_main); } private void showStartupError() { setContentView(R.layout.activity_startup_error); TextView errorMessage = findViewById(R.id.errorMessageTextView); Throwable e = Application.getStartupException(); errorMessage.setText(e.getMessage()); } @Override protected void onResume() { super.onResume(); if (Application.hadStartupException()) return; } @Override protected void onPause() { super.onPause(); if (Application.hadStartupException()) return; } @Override protected void onDestroy() { super.onDestroy(); if (Application.hadStartupException()) return; } }