org.navitproject.navit.Navit.java Source code

Java tutorial

Introduction

Here is the source code for org.navitproject.navit.Navit.java

Source

/**
 * Navit, a modular navigation system.
 * Copyright (C) 2005-2008 Navit Team
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 */

package org.navitproject.navit;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ActivityManager.TaskDescription;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Point;
import android.Manifest;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Message;
import android.os.PowerManager;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

public class Navit extends Activity {

    public NavitDialogs dialogs;
    private PowerManager.WakeLock wl;
    private NavitActivityResult ActivityResults[];
    public static InputMethodManager mgr = null;
    public static DisplayMetrics metrics = null;
    public static int status_bar_height = 0;
    public static int action_bar_default_height = 0;
    public static int navigation_bar_height = 0;
    public static int navigation_bar_height_landscape = 0;
    public static int navigation_bar_width = 0;
    public static Boolean show_soft_keyboard = false;
    public static Boolean show_soft_keyboard_now_showing = false;
    public static long last_pressed_menu_key = 0L;
    public static long time_pressed_menu_key = 0L;
    private static Intent startup_intent = null;
    private static long startup_intent_timestamp = 0L;
    public static String my_display_density = "mdpi";
    public static final int NavitDownloaderSelectMap_id = 967;
    public static final int MAP_NUM_PRIMARY = 11;
    public static final int NavitAddressSearch_id = 70;
    public static final int NavitSelectStorage_id = 43;
    public static String NavitLanguage;
    public static Resources NavitResources = null;

    public static final int MAP_NUM_SECONDARY = 12;
    static final String NAVIT_PACKAGE_NAME = "org.navitproject.navit";
    static final String TAG = "Navit";
    static String map_filename_path = null;
    static final String NAVIT_DATA_DIR = "/data/data/" + NAVIT_PACKAGE_NAME;
    static final String NAVIT_DATA_SHARE_DIR = NAVIT_DATA_DIR + "/share";
    static final String FIRST_STARTUP_FILE = NAVIT_DATA_SHARE_DIR + "/has_run_once.txt";
    public static final String NAVIT_PREFS = "NavitPrefs";
    Boolean isFullscreen = false;
    private static final int MY_PERMISSIONS_REQUEST_ALL = 101;

    /**
     * @brief A Runnable to restore soft input when the user returns to the activity.
     * 
     * An instance of this class can be passed to the main message queue in the Activity's
     * {@code onRestore()} method.
     */
    private class SoftInputRestorer implements Runnable {
        public void run() {
            Navit.this.showNativeKeyboard();
        }
    }

    public void removeFileIfExists(String source) {
        File file = new File(source);

        if (!file.exists())
            return;

        file.delete();
    }

    public void copyFileIfExists(String source, String destination) throws IOException {
        File file = new File(source);

        if (!file.exists())
            return;

        FileInputStream is = null;
        FileOutputStream os = null;

        try {
            is = new FileInputStream(source);
            os = new FileOutputStream(destination);

            int len;
            byte buffer[] = new byte[1024];

            while ((len = is.read(buffer)) != -1) {
                os.write(buffer, 0, len);
            }
        } finally {
            /* Close the FileStreams to prevent Resource leaks */
            if (is != null)
                is.close();

            if (os != null)
                os.close();
        }
        return;
    }

    public static String _(String in) {
        return NavitTextTranslations.get_text(in);
    }

    private boolean extractRes(String resname, String result) {
        boolean needs_update = false;
        Log.e(TAG, "Res Name " + resname + ", result " + result);
        int id = NavitResources.getIdentifier(resname, "raw", NAVIT_PACKAGE_NAME);
        Log.e(TAG, "Res ID " + id);
        if (id == 0)
            return false;

        File resultfile = new File(result);
        if (!resultfile.exists()) {
            needs_update = true;
            File path = resultfile.getParentFile();
            if (!path.exists() && !resultfile.getParentFile().mkdirs())
                return false;
        } else {
            PackageManager pm = getPackageManager();
            ApplicationInfo appInfo;
            long apkUpdateTime = 0;
            try {
                appInfo = pm.getApplicationInfo(NAVIT_PACKAGE_NAME, 0);
                apkUpdateTime = new File(appInfo.sourceDir).lastModified();
            } catch (NameNotFoundException e) {
                Log.e(TAG, "Could not read package infos");
                e.printStackTrace();
            }
            if (apkUpdateTime > resultfile.lastModified())
                needs_update = true;
        }

        if (needs_update) {
            Log.e(TAG, "Extracting resource");

            try {
                InputStream resourcestream = NavitResources.openRawResource(id);
                FileOutputStream resultfilestream = new FileOutputStream(resultfile);
                byte[] buf = new byte[1024];
                int i = 0;
                while ((i = resourcestream.read(buf)) != -1) {
                    resultfilestream.write(buf, 0, i);
                }
                resultfilestream.close();
            } catch (Exception e) {
                Log.e(TAG, "Exception " + e.getMessage());
                return false;
            }
        }
        return true;
    }

    private void showInfos() {
        SharedPreferences settings = getSharedPreferences(NAVIT_PREFS, MODE_PRIVATE);
        boolean firstStart = settings.getBoolean("firstStart", true);

        if (firstStart) {
            AlertDialog.Builder infobox = new AlertDialog.Builder(this);
            infobox.setTitle(getString(R.string.initial_info_box_title)); // TRANS
            infobox.setCancelable(false);
            final TextView message = new TextView(this);
            message.setFadingEdgeLength(20);
            message.setVerticalFadingEdgeEnabled(true);
            // message.setVerticalScrollBarEnabled(true);
            RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(
                    RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.FILL_PARENT);

            message.setLayoutParams(rlp);
            final SpannableString s = new SpannableString(getString(R.string.initial_info_box_message)); // TRANS
            Linkify.addLinks(s, Linkify.WEB_URLS);
            message.setText(s);
            message.setMovementMethod(LinkMovementMethod.getInstance());
            infobox.setView(message);

            // TRANS
            infobox.setPositiveButton(getString(R.string.initial_info_box_OK),
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface arg0, int arg1) {
                            Log.e("Navit", "Ok, user saw the infobox");
                        }
                    });

            // TRANS
            infobox.setNeutralButton(getString(R.string.initial_info_box_more_info),
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface arg0, int arg1) {
                            Log.e("Navit", "user wants more info, show the website");
                            String url = "http://wiki.navit-project.org/index.php/Navit_on_Android";
                            Intent i = new Intent(Intent.ACTION_VIEW);
                            i.setData(Uri.parse(url));
                            startActivity(i);
                        }
                    });
            infobox.show();
            SharedPreferences.Editor edit_settings = settings.edit();
            edit_settings.putBoolean("firstStart", false);
            edit_settings.commit();
        }
    }

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
            this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        else
            this.getActionBar().hide();

        dialogs = new NavitDialogs(this);

        NavitResources = getResources();

        // only take arguments here, onResume gets called all the time (e.g. when screenblanks, etc.)
        Navit.startup_intent = this.getIntent();
        // hack! Remember time stamps, and only allow 4 secs. later in onResume to set target!
        Navit.startup_intent_timestamp = System.currentTimeMillis();
        Log.e("Navit", "**1**A " + startup_intent.getAction());
        Log.e("Navit", "**1**D " + startup_intent.getDataString());

        // init translated text
        NavitTextTranslations.init();

        // NOTIFICATION
        // Setup the status bar notification      
        // This notification is removed in the exit() function
        NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); // Grab a handle to the NotificationManager
        Notification NavitNotification = new Notification(R.drawable.ic_notify,
                getString(R.string.notification_ticker), System.currentTimeMillis()); // Create a new notification, with the text string to show when the notification first appears
        PendingIntent appIntent = PendingIntent.getActivity(getApplicationContext(), 0, getIntent(), 0);
        //      FIXME : needs a fix for sdk 23
        //      NavitNotification.setLatestEventInfo(getApplicationContext(), "Navit", getString(R.string.notification_event_default), appIntent);   // Set the text in the notification
        //      NavitNotification.flags|=Notification.FLAG_ONGOING_EVENT;   // Ensure that the notification appears in Ongoing
        //      nm.notify(R.string.app_name, NavitNotification);   // Set the notification

        // Status and navigation bar sizes
        // These are platform defaults and do not change with rotation, but we have to figure out which ones apply
        // (is the navigation bar visible? on the side or at the bottom?)
        Resources resources = getResources();
        int shid = resources.getIdentifier("status_bar_height", "dimen", "android");
        int adhid = resources.getIdentifier("action_bar_default_height", "dimen", "android");
        int nhid = resources.getIdentifier("navigation_bar_height", "dimen", "android");
        int nhlid = resources.getIdentifier("navigation_bar_height_landscape", "dimen", "android");
        int nwid = resources.getIdentifier("navigation_bar_width", "dimen", "android");
        status_bar_height = (shid > 0) ? resources.getDimensionPixelSize(shid) : 0;
        action_bar_default_height = (adhid > 0) ? resources.getDimensionPixelSize(adhid) : 0;
        navigation_bar_height = (nhid > 0) ? resources.getDimensionPixelSize(nhid) : 0;
        navigation_bar_height_landscape = (nhlid > 0) ? resources.getDimensionPixelSize(nhlid) : 0;
        navigation_bar_width = (nwid > 0) ? resources.getDimensionPixelSize(nwid) : 0;
        Log.d(TAG, String.format(
                "status_bar_height=%d, action_bar_default_height=%d, navigation_bar_height=%d, navigation_bar_height_landscape=%d, navigation_bar_width=%d",
                status_bar_height, action_bar_default_height, navigation_bar_height,
                navigation_bar_height_landscape, navigation_bar_width));
        if ((ContextCompat.checkSelfPermission(this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
                || (ContextCompat.checkSelfPermission(this,
                        Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
            Log.d(TAG, "ask for permission(s)");
            ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    Manifest.permission.ACCESS_FINE_LOCATION }, MY_PERMISSIONS_REQUEST_ALL);
        }
        // get the local language -------------
        Locale locale = java.util.Locale.getDefault();
        String lang = locale.getLanguage();
        String langu = lang;
        String langc = lang;
        Log.e("Navit", "lang=" + lang);
        int pos = langu.indexOf('_');
        if (pos != -1) {
            langc = langu.substring(0, pos);
            NavitLanguage = langc + langu.substring(pos).toUpperCase(locale);
            Log.e("Navit", "substring lang " + NavitLanguage.substring(pos).toUpperCase(locale));
            // set lang. for translation
            NavitTextTranslations.main_language = langc;
            NavitTextTranslations.sub_language = NavitLanguage.substring(pos).toUpperCase(locale);
        } else {
            String country = locale.getCountry();
            Log.e("Navit", "Country1 " + country);
            Log.e("Navit", "Country2 " + country.toUpperCase(locale));
            NavitLanguage = langc + "_" + country.toUpperCase(locale);
            // set lang. for translation
            NavitTextTranslations.main_language = langc;
            NavitTextTranslations.sub_language = country.toUpperCase(locale);
        }
        Log.e("Navit", "Language " + lang);

        SharedPreferences prefs = getSharedPreferences(NAVIT_PREFS, MODE_PRIVATE);
        map_filename_path = prefs.getString("filenamePath",
                Environment.getExternalStorageDirectory().getPath() + "/navit/");

        // make sure the new path for the navitmap.bin file(s) exist!!
        File navit_maps_dir = new File(map_filename_path);
        navit_maps_dir.mkdirs();

        // make sure the share dir exists
        File navit_data_share_dir = new File(NAVIT_DATA_SHARE_DIR);
        navit_data_share_dir.mkdirs();

        Display display_ = getWindowManager().getDefaultDisplay();
        int width_ = display_.getWidth();
        int height_ = display_.getHeight();
        metrics = new DisplayMetrics();
        display_.getMetrics(Navit.metrics);
        int densityDpi = (int) ((Navit.metrics.density * 160) - .5f);
        Log.e("Navit", "Navit -> pixels x=" + width_ + " pixels y=" + height_);
        Log.e("Navit", "Navit -> dpi=" + densityDpi);
        Log.e("Navit", "Navit -> density=" + Navit.metrics.density);
        Log.e("Navit", "Navit -> scaledDensity=" + Navit.metrics.scaledDensity);

        ActivityResults = new NavitActivityResult[16];
        setVolumeControlStream(AudioManager.STREAM_MUSIC);
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, "NavitDoNotDimScreen");

        if (!extractRes(langc, NAVIT_DATA_DIR + "/locale/" + langc + "/LC_MESSAGES/navit.mo")) {
            Log.e("Navit", "Failed to extract language resource " + langc);
        }

        if (densityDpi <= 120) {
            my_display_density = "ldpi";
        } else if (densityDpi <= 160) {
            my_display_density = "mdpi";
        } else if (densityDpi < 240) {
            my_display_density = "hdpi";
        } else if (densityDpi < 320) {
            my_display_density = "xhdpi";
        } else if (densityDpi < 480) {
            my_display_density = "xxhdpi";
        } else if (densityDpi < 640) {
            my_display_density = "xxxhdpi";
        } else {
            Log.e("Navit", "found device of very high density (" + densityDpi + ")");
            Log.e("Navit", "using xxxhdpi values");
            my_display_density = "xxxhdpi";
        }

        if (!extractRes("navit" + my_display_density, NAVIT_DATA_DIR + "/share/navit.xml")) {
            Log.e("Navit", "Failed to extract navit.xml for " + my_display_density);
        }

        // --> dont use android.os.Build.VERSION.SDK_INT, needs API >= 4
        Log.e("Navit", "android.os.Build.VERSION.SDK_INT=" + Integer.valueOf(android.os.Build.VERSION.SDK));
        NavitMain(this, NavitLanguage, Integer.valueOf(android.os.Build.VERSION.SDK), my_display_density,
                NAVIT_DATA_DIR + "/bin/navit", map_filename_path);

        showInfos();

        Navit.mgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.d("Navit", "OnResume");
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            /* Required to make system bars fully transparent */
            getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
        }
        //InputMethodManager mgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        // DEBUG
        // intent_data = "google.navigation:q=Wien Burggasse 27";
        // intent_data = "google.navigation:q=48.25676,16.643";
        // intent_data = "google.navigation:ll=48.25676,16.643&q=blabla-strasse";
        // intent_data = "google.navigation:ll=48.25676,16.643";
        if (startup_intent != null) {
            if (System.currentTimeMillis() <= Navit.startup_intent_timestamp + 4000L) {
                Log.e("Navit", "**2**A " + startup_intent.getAction());
                Log.e("Navit", "**2**D " + startup_intent.getDataString());
                String navi_scheme = startup_intent.getScheme();
                if (navi_scheme != null && navi_scheme.equals("google.navigation")) {
                    parseNavigationURI(startup_intent.getData().getSchemeSpecificPart());
                }
            } else {
                Log.e("Navit", "timestamp for navigate_to expired! not using data");
            }
        }
        Log.d(TAG, "onResume");
        if (show_soft_keyboard_now_showing) {
            /* Calling showNativeKeyboard() directly won't work here, we need to use the message queue */
            View cf = getCurrentFocus();
            if (cf == null)
                Log.e(TAG, "no view in focus, can't get a handler");
            else
                cf.getHandler().post(new SoftInputRestorer());
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.d(TAG, "onPause");
        if (show_soft_keyboard_now_showing) {
            Log.d(TAG, "onPause:hiding soft input");
            this.hideNativeKeyboard();
            show_soft_keyboard_now_showing = true;
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_ALL: {
            if (grantResults.length > 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED
                    && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
                // ok, we got permissions
            } else {
                AlertDialog.Builder infobox = new AlertDialog.Builder(this);
                infobox.setTitle(getString(R.string.permissions_info_box_title)); // TRANS
                infobox.setCancelable(false);
                final TextView message = new TextView(this);
                message.setFadingEdgeLength(20);
                message.setVerticalFadingEdgeEnabled(true);
                RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(
                        RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.FILL_PARENT);
                message.setLayoutParams(rlp);
                final SpannableString s = new SpannableString(getString(R.string.permissions_not_granted)); // TRANS
                message.setText(s);
                message.setMovementMethod(LinkMovementMethod.getInstance());
                infobox.setView(message);
                // TRANS
                infobox.setPositiveButton(getString(R.string.initial_info_box_OK),
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface arg0, int arg1) {
                                exit();
                            }
                        });
                infobox.show();
            }
            return;
        }
        }
    }

    private void parseNavigationURI(String schemeSpecificPart) {
        String naviData[] = schemeSpecificPart.split("&");
        Pattern p = Pattern.compile("(.*)=(.*)");
        Map<String, String> params = new HashMap<String, String>();
        for (int count = 0; count < naviData.length; count++) {
            Matcher m = p.matcher(naviData[count]);

            if (m.matches()) {
                params.put(m.group(1), m.group(2));
            }
        }

        // d: google.navigation:q=blabla-strasse # (this happens when you are offline, or from contacts)
        // a: google.navigation:ll=48.25676,16.643&q=blabla-strasse
        // c: google.navigation:ll=48.25676,16.643
        // b: google.navigation:q=48.25676,16.643

        Float lat;
        Float lon;
        Bundle b = new Bundle();

        String geoString = params.get("ll");
        if (geoString != null) {
            String address = params.get("q");
            if (address != null)
                b.putString("q", address);
        } else {
            geoString = params.get("q");
        }

        if (geoString != null) {
            if (geoString.matches("^[+-]{0,1}\\d+(|\\.\\d*),[+-]{0,1}\\d+(|\\.\\d*)$")) {
                String geo[] = geoString.split(",");
                if (geo.length == 2) {
                    try {
                        lat = Float.valueOf(geo[0]);
                        lon = Float.valueOf(geo[1]);
                        b.putFloat("lat", lat);
                        b.putFloat("lon", lon);
                        Message msg = Message.obtain(N_NavitGraphics.callback_handler,
                                NavitGraphics.msg_type.CLB_SET_DESTINATION.ordinal());

                        msg.setData(b);
                        msg.sendToTarget();
                        Log.e("Navit", "target found (b): " + geoString);
                    } catch (NumberFormatException e) {
                    } // nothing to do here
                }
            } else {
                start_targetsearch_from_intent(geoString);
            }
        }
    }

    public void setActivityResult(int requestCode, NavitActivityResult ActivityResult) {
        //Log.e("Navit", "setActivityResult " + requestCode);
        ActivityResults[requestCode] = ActivityResult;
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        super.onPrepareOptionsMenu(menu);
        //Log.e("Navit","onPrepareOptionsMenu");
        // this gets called every time the menu is opened!!
        // change menu items here!
        menu.clear();

        // group-id,item-id,sort order number
        //menu.add(1, 1, 100, getString(R.string.optionsmenu_zoom_in)); //TRANS
        //menu.add(1, 2, 200, getString(R.string.optionsmenu_zoom_out)); //TRANS

        menu.add(1, 3, 300, getString(R.string.optionsmenu_download_maps)); //TRANS
        menu.add(1, 5, 400, getString(R.string.optionsmenu_toggle_poi)); //TRANS

        menu.add(1, 6, 500, getString(R.string.optionsmenu_address_search)); //TRANS
        menu.add(1, 10, 600, getString(R.string.optionsmenu_set_map_location));

        menu.add(1, 99, 900, getString(R.string.optionsmenu_exit_navit)); //TRANS

        /* Only show the Backup to SD-Card Option if we really have one */
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
            menu.add(1, 7, 700, getString(R.string.optionsmenu_backup_restore)); //TRANS

        return true;
    }

    // define callback id here
    public static NavitGraphics N_NavitGraphics = null;

    // callback id gets set here when called from NavitGraphics
    public static void setKeypressCallback(int kp_cb_id, NavitGraphics ng) {
        //Log.e("Navit", "setKeypressCallback -> id1=" + kp_cb_id);
        //Log.e("Navit", "setKeypressCallback -> ng=" + String.valueOf(ng));
        //N_KeypressCallbackID = kp_cb_id;
        N_NavitGraphics = ng;
    }

    public static void setMotionCallback(int mo_cb_id, NavitGraphics ng) {
        //Log.e("Navit", "setKeypressCallback -> id2=" + mo_cb_id);
        //Log.e("Navit", "setKeypressCallback -> ng=" + String.valueOf(ng));
        //N_MotionCallbackID = mo_cb_id;
        N_NavitGraphics = ng;
    }

    public void start_targetsearch_from_intent(String target_address) {
        if (target_address == null || target_address.equals("")) {
            // empty search string entered
            Toast.makeText(getApplicationContext(), getString(R.string.address_search_not_found), Toast.LENGTH_LONG)
                    .show(); //TRANS
        } else {
            Intent search_intent = new Intent(this, NavitAddressSearchActivity.class);
            search_intent.putExtra("search_string", target_address);
            this.startActivityForResult(search_intent, NavitAddressSearch_id);
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        runOptionsItem(item.getItemId());
        return true;
    }

    public void runOptionsItem(int id) {
        // Handle item selection
        switch (id) {
        case 1:
            // zoom in
            Message.obtain(N_NavitGraphics.callback_handler, NavitGraphics.msg_type.CLB_ZOOM_IN.ordinal())
                    .sendToTarget();
            // if we zoom, hide the bubble
            Log.e("Navit", "onOptionsItemSelected -> zoom in");
            break;
        case 2:
            // zoom out
            Message.obtain(N_NavitGraphics.callback_handler, NavitGraphics.msg_type.CLB_ZOOM_OUT.ordinal())
                    .sendToTarget();
            // if we zoom, hide the bubble
            Log.e("Navit", "onOptionsItemSelected -> zoom out");
            break;
        case 3:
            // map download menu
            Intent map_download_list_activity = new Intent(this, NavitDownloadSelectMapActivity.class);
            startActivityForResult(map_download_list_activity, Navit.NavitDownloaderSelectMap_id);
            break;
        case 5:
            // toggle the normal POI layers and labels (to avoid double POIs)
            Message msg = Message.obtain(N_NavitGraphics.callback_handler,
                    NavitGraphics.msg_type.CLB_CALL_CMD.ordinal());
            Bundle b = new Bundle();
            b.putString("cmd", "toggle_layer(\"POI Symbols\");");
            msg.setData(b);
            msg.sendToTarget();

            msg = Message.obtain(N_NavitGraphics.callback_handler, NavitGraphics.msg_type.CLB_CALL_CMD.ordinal());
            b = new Bundle();
            b.putString("cmd", "toggle_layer(\"POI Labels\");");
            msg.setData(b);
            msg.sendToTarget();

            // toggle full POI icons on/off
            msg = Message.obtain(N_NavitGraphics.callback_handler, NavitGraphics.msg_type.CLB_CALL_CMD.ordinal());
            b = new Bundle();
            b.putString("cmd", "toggle_layer(\"Android-POI-Icons-full\");");
            msg.setData(b);
            msg.sendToTarget();

            break;
        case 6:
            // ok startup address search activity
            Intent search_intent = new Intent(this, NavitAddressSearchActivity.class);
            this.startActivityForResult(search_intent, NavitAddressSearch_id);
            break;
        case 7:
            /* Backup / Restore */
            showDialog(NavitDialogs.DIALOG_BACKUP_RESTORE);
            break;
        case 10:
            setMapLocation();
            break;
        case 99:
            // exit
            this.onStop();
            this.exit();
            break;
        }
    }

    /**
     * @brief Shows the Options menu.
     * 
     * Calling this method has the same effect as pressing the hardware Menu button, where present, or touching
     * the overflow button in the Action bar.
     */
    public void showMenu() {
        openOptionsMenu();
    }

    /**
     * @brief Shows the native keyboard or other input method.
     * 
     * @return {@code true} if an input method is going to be displayed, {@code false} if not
     */
    public int showNativeKeyboard() {
        /*
         * Apologies for the huge mess that this function is, but Android's soft input API is a big
         * nightmare. Its devs have mercifully given us an option to show or hide the keyboard, but
         * there is no reliable way to figure out if it is actually showing, let alone how much of the
         * screen it occupies, so our best bet is guesswork.
         */
        Configuration config = getResources().getConfiguration();
        if ((config.keyboard == Configuration.KEYBOARD_QWERTY)
                && (config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO))
            /* physical keyboard present, exit */
            return 0;

        /* Use SHOW_FORCED here, else keyboard won't show in landscape mode */
        mgr.showSoftInput(getCurrentFocus(), InputMethodManager.SHOW_FORCED);
        show_soft_keyboard_now_showing = true;

        /* 
         * Crude way to estimate the height occupied by the keyboard: for AOSP on KitKat and Lollipop it
         * is about 62-63% of available screen width (in portrait mode) but no more than slightly above
         * 46% of height (in landscape mode).
         */
        Display display_ = getWindowManager().getDefaultDisplay();
        int width_ = display_.getWidth();
        int height_ = display_.getHeight();
        int maxHeight = height_ * 47 / 100;
        int inputHeight = width_ * 63 / 100;
        if (inputHeight > (maxHeight))
            inputHeight = maxHeight;

        /* the receiver isn't going to fire before the UI thread becomes idle, well after this method returns */
        Log.d(TAG, "showNativeKeyboard:return (assuming true)");
        return inputHeight;
    }

    /**
     * @brief Hides the native keyboard or other input method.
     */
    public void hideNativeKeyboard() {
        mgr.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
        show_soft_keyboard_now_showing = false;
    }

    void setDestination(float latitude, float longitude, String address) {
        Toast.makeText(getApplicationContext(), getString(R.string.address_search_set_destination) + "\n" + address,
                Toast.LENGTH_LONG).show(); //TRANS

        Message msg = Message.obtain(N_NavitGraphics.callback_handler,
                NavitGraphics.msg_type.CLB_SET_DESTINATION.ordinal());
        Bundle b = new Bundle();
        b.putFloat("lat", latitude);
        b.putFloat("lon", longitude);
        b.putString("q", address);
        msg.setData(b);
        msg.sendToTarget();
    }

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
        case Navit.NavitDownloaderSelectMap_id:
            if (resultCode == Activity.RESULT_OK) {
                Message msg = dialogs.obtainMessage(NavitDialogs.MSG_START_MAP_DOWNLOAD,
                        data.getIntExtra("map_index", -1), 0);
                msg.sendToTarget();
            }
            break;
        case NavitAddressSearch_id:
            if (resultCode == Activity.RESULT_OK) {
                Bundle destination = data.getExtras();
                Toast.makeText(getApplicationContext(),
                        getString(R.string.address_search_set_destination) + "\n" + destination.getString(("q")),
                        Toast.LENGTH_LONG).show(); //TRANS

                Message msg = Message.obtain(N_NavitGraphics.callback_handler,
                        NavitGraphics.msg_type.CLB_SET_DESTINATION.ordinal());
                msg.setData(destination);
                msg.sendToTarget();
            }
            break;
        case NavitSelectStorage_id:
            if (resultCode == RESULT_OK) {
                String newDir = data.getStringExtra(FileBrowserActivity.returnDirectoryParameter);
                Log.d(TAG, "selected path= " + newDir);
                if (!newDir.contains("/navit"))
                    newDir = newDir + "/navit/";
                else
                    newDir = newDir + "/";
                SharedPreferences prefs = this.getSharedPreferences(NAVIT_PREFS, MODE_PRIVATE);
                SharedPreferences.Editor prefs_editor = prefs.edit();
                prefs_editor.putString("filenamePath", newDir);
                prefs_editor.commit();
                Toast.makeText(this, String
                        .format(Navit._("New location set to %s\nRestart Navit to apply the changes."), newDir),
                        Toast.LENGTH_LONG).show();
            } else
                Log.w(TAG, "select path failed");
            break;
        default:
            //Log.e("Navit", "onActivityResult " + requestCode + " " + resultCode);
            ActivityResults[requestCode].onActivityResult(requestCode, resultCode, data);
            break;
        }
    }

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

    protected Dialog onCreateDialog(int id) {
        return dialogs.createDialog(id);
    }

    @Override
    public boolean onSearchRequested() {
        /* Launch the internal Search Activity */
        Intent search_intent = new Intent(this, NavitAddressSearchActivity.class);
        this.startActivityForResult(search_intent, NavitAddressSearch_id);

        return true;
    }

    public boolean setMapLocation() {
        Intent fileExploreIntent = new Intent(this, FileBrowserActivity.class);
        fileExploreIntent.putExtra(FileBrowserActivity.startDirectoryParameter, "/mnt")
                .setAction(FileBrowserActivity.INTENT_ACTION_SELECT_DIR);
        startActivityForResult(fileExploreIntent, NavitSelectStorage_id);

        return true;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("Navit", "OnDestroy");
        // TODO next call will kill our app the hard way. This should not be necessary, but ensures navit is
        // properly restarted and no resources are wasted with navit in background. Remove this call after 
        // code review
        NavitDestroy();
    }

    public void fullscreen(int fullscreen) {
        int w, h;

        isFullscreen = (fullscreen != 0);
        if (isFullscreen) {
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
        } else {
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        }

        Display display_ = getWindowManager().getDefaultDisplay();
        if (Build.VERSION.SDK_INT < 17) {
            w = display_.getWidth();
            h = display_.getHeight();
        } else {
            Point size = new Point();
            display_.getRealSize(size);
            w = size.x;
            h = size.y;
        }
        Log.d(TAG, String.format("Toggle fullscreen, w=%d, h=%d", w, h));
        N_NavitGraphics.handleResize(w, h);
    }

    public void disableSuspend() {
        wl.acquire();
        wl.release();
    }

    public void exit() {
        //      NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        //      nm.cancel(R.string.app_name);
        NavitVehicle.removeListener();
        NavitDestroy();
    }

    public native void NavitMain(Navit x, String lang, int version, String display_density_string, String path,
            String path2);

    public native void NavitDestroy();

    /*
     * this is used to load the 'navit' native library on
     * application startup. The library has already been unpacked at
     * installation time by the package manager.
     */
    static {
        System.loadLibrary("navit");
    }
}