plugin.google.maps.CordovaGoogleMaps.java Source code

Java tutorial

Introduction

Here is the source code for plugin.google.maps.CordovaGoogleMaps.java

Source

package plugin.google.maps;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Color;
import android.location.Location;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.Toast;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.LocationSettingsResult;
import com.google.android.gms.location.LocationSettingsStatusCodes;
import com.google.android.gms.maps.MapsInitializer;

import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.PluginEntry;
import org.apache.cordova.PluginManager;
import org.apache.cordova.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@SuppressWarnings("deprecation")
public class CordovaGoogleMaps extends CordovaPlugin implements ViewTreeObserver.OnScrollChangedListener {
    private final String TAG = "GoogleMapsPlugin";
    private HashMap<String, Bundle> bufferForLocationDialog = new HashMap<String, Bundle>();

    private final int ACTIVITY_LOCATION_DIALOG = 0x7f999900; // Invite the location dialog using Google Play Services
    private final int ACTIVITY_LOCATION_PAGE = 0x7f999901; // Open the location settings page

    private Activity activity;
    public ViewGroup root;
    public MyPluginLayout mPluginLayout = null;
    private GoogleApiClient googleApiClient = null;
    private JSONArray _saveArgs = null;
    private CallbackContext _saveCallbackContext = null;
    public final HashMap<String, Integer> methods = new HashMap<String, Integer>();
    public boolean initialized = false;
    public PluginManager pluginManager;
    private static ExecutorService executorService = Executors.newFixedThreadPool(1);
    private String CURRENT_URL;

    @SuppressLint("NewApi")
    @Override
    public void initialize(final CordovaInterface cordova, final CordovaWebView webView) {
        super.initialize(cordova, webView);
        if (root != null) {
            return;
        }
        activity = cordova.getActivity();
        final View view = webView.getView();
        view.getViewTreeObserver().addOnScrollChangedListener(CordovaGoogleMaps.this);
        root = (ViewGroup) view.getParent();

        Method[] classMethods = this.getClass().getMethods();
        for (int i = 0; i < classMethods.length; i++) {
            String methodName = classMethods[i].getName();
            methods.put(methodName, methodName.hashCode());
        }

        pluginManager = webView.getPluginManager();

        cordova.getActivity().runOnUiThread(new Runnable() {
            @SuppressLint("NewApi")
            public void run() {

                // Enable this, webView makes draw cache on the Android action bar issue.
                //View view = webView.getView();
                //if (Build.VERSION.SDK_INT >= 21 || "org.xwalk.core.XWalkView".equals(view.getClass().getName())){
                //  view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
                //  Log.d("Layout", "--> view =" + view.isHardwareAccelerated()); //always false
                //}

                // ------------------------------
                // Check of Google Play Services
                // ------------------------------
                int checkGooglePlayServices = GooglePlayServicesUtil.isGooglePlayServicesAvailable(activity);

                Log.d(TAG,
                        "----> checkGooglePlayServices = " + (ConnectionResult.SUCCESS == checkGooglePlayServices));

                if (checkGooglePlayServices != ConnectionResult.SUCCESS) {
                    // google play services is missing!!!!
                    /*
                     * Returns status code indicating whether there was an error. Can be one
                     * of following in ConnectionResult: SUCCESS, SERVICE_MISSING,
                     * SERVICE_VERSION_UPDATE_REQUIRED, SERVICE_DISABLED, SERVICE_INVALID.
                     */
                    Log.e(TAG, "---Google Play Services is not available: "
                            + GooglePlayServicesUtil.getErrorString(checkGooglePlayServices));

                    boolean isNeedToUpdate = false;

                    String errorMsg = "Google Maps Android API v2 is not available for some reason on this device. Do you install the latest Google Play Services from Google Play Store?";
                    switch (checkGooglePlayServices) {
                    case ConnectionResult.DEVELOPER_ERROR:
                        errorMsg = "The application is misconfigured. This error is not recoverable and will be treated as fatal. The developer should look at the logs after this to determine more actionable information.";
                        break;
                    case ConnectionResult.INTERNAL_ERROR:
                        errorMsg = "An internal error of Google Play Services occurred. Please retry, and it should resolve the problem.";
                        break;
                    case ConnectionResult.INVALID_ACCOUNT:
                        errorMsg = "You attempted to connect to the service with an invalid account name specified.";
                        break;
                    case ConnectionResult.LICENSE_CHECK_FAILED:
                        errorMsg = "The application is not licensed to the user. This error is not recoverable and will be treated as fatal.";
                        break;
                    case ConnectionResult.NETWORK_ERROR:
                        errorMsg = "A network error occurred. Please retry, and it should resolve the problem.";
                        break;
                    case ConnectionResult.SERVICE_DISABLED:
                        errorMsg = "The installed version of Google Play services has been disabled on this device. Please turn on Google Play Services.";
                        break;
                    case ConnectionResult.SERVICE_INVALID:
                        errorMsg = "The version of the Google Play services installed on this device is not authentic. Please update the Google Play Services from Google Play Store.";
                        isNeedToUpdate = true;
                        break;
                    case ConnectionResult.SERVICE_MISSING:
                        errorMsg = "Google Play services is missing on this device. Please install the Google Play Services.";
                        isNeedToUpdate = true;
                        break;
                    case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED:
                        errorMsg = "The installed version of Google Play services is out of date. Please update the Google Play Services from Google Play Store.";
                        isNeedToUpdate = true;
                        break;
                    case ConnectionResult.SIGN_IN_REQUIRED:
                        errorMsg = "You attempted to connect to the service but you are not signed in. Please check the Google Play Services configuration";
                        break;
                    default:
                        isNeedToUpdate = true;
                        break;
                    }

                    final boolean finalIsNeedToUpdate = isNeedToUpdate;
                    AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity);
                    alertDialogBuilder.setMessage(errorMsg).setCancelable(false).setPositiveButton("Close",
                            new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int id) {
                                    dialog.dismiss();
                                    if (finalIsNeedToUpdate) {
                                        try {
                                            activity.startActivity(new Intent(Intent.ACTION_VIEW,
                                                    Uri.parse("market://details?id=com.google.android.gms")));
                                        } catch (android.content.ActivityNotFoundException anfe) {
                                            activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(
                                                    "http://play.google.com/store/apps/details?id=appPackageName")));
                                        }
                                    }
                                }
                            });
                    AlertDialog alertDialog = alertDialogBuilder.create();

                    // show it
                    alertDialog.show();

                    Log.e(TAG, "Google Play Services is not available.");
                    return;
                }

                webView.getView().setBackgroundColor(Color.TRANSPARENT);
                webView.getView().setOverScrollMode(View.OVER_SCROLL_NEVER);
                mPluginLayout = new MyPluginLayout(webView, activity);

                // Check the API key
                ApplicationInfo appliInfo = null;
                try {
                    appliInfo = activity.getPackageManager().getApplicationInfo(activity.getPackageName(),
                            PackageManager.GET_META_DATA);
                } catch (NameNotFoundException e) {
                }

                String API_KEY = appliInfo.metaData.getString("com.google.android.maps.v2.API_KEY");
                if ("API_KEY_FOR_ANDROID".equals(API_KEY)) {

                    AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity);

                    alertDialogBuilder.setMessage(
                            "Please replace 'API_KEY_FOR_ANDROID' in the platforms/android/AndroidManifest.xml with your API Key!")
                            .setCancelable(false).setPositiveButton("Close", new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int id) {
                                    dialog.dismiss();
                                }
                            });
                    AlertDialog alertDialog = alertDialogBuilder.create();

                    // show it
                    alertDialog.show();
                }

                CURRENT_URL = webView.getUrl();

                //------------------------------
                // Initialize Google Maps SDK
                //------------------------------
                if (!initialized) {
                    try {
                        MapsInitializer.initialize(cordova.getActivity());
                        initialized = true;
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

            }
        });

    }

    @Override
    public void onScrollChanged() {
        if (mPluginLayout == null) {
            return;
        }
        View view = webView.getView();
        int scrollX = view.getScrollX();
        int scrollY = view.getScrollY();
        mPluginLayout.scrollTo(scrollX, scrollY);

    }

    @Override
    public boolean execute(final String action, final JSONArray args, final CallbackContext callbackContext)
            throws JSONException {

        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    if (action.equals("putHtmlElements")) {
                        CordovaGoogleMaps.this.putHtmlElements(args, callbackContext);
                    } else if ("getMyLocation".equals(action)) {
                        CordovaGoogleMaps.this.getMyLocation(args, callbackContext);
                    } else if ("getMap".equals(action)) {
                        CordovaGoogleMaps.this.getMap(args, callbackContext);
                    } else if ("removeMap".equals(action)) {
                        CordovaGoogleMaps.this.removeMap(args, callbackContext);
                    }

                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        });
        /*
        if (mPluginLayout != null && mPluginLayout.isDebug) {
          if (args != null && args.length() > 0) {
            Log.d(TAG, "(debug)action=" + action + " args[0]=" + args.getString(0));
          } else {
            Log.d(TAG, "(debug)action=" + action);
          }
        }
            
        if (methods.containsKey(action)) {
          if (mPluginLayout != null && mPluginLayout.isDebug) {
            if (args != null && args.length() > 0) {
              Log.d(TAG, "(debug)action=" + action + " args[0]=" + args.getString(0));
            } else {
              Log.d(TAG, "(debug)action=" + action);
            }
          }
          cordova.getThreadPool().submit(new Runnable() {
            @Override
            public void run() {
              Method method = methods.get(action);
              try {
        method.invoke(CordovaGoogleMaps.this, args, callbackContext);
              } catch (Exception e) {
        e.printStackTrace();
        callbackContext.error("" + e.getMessage());
              }
            }
          });
          return true;
        } else {
          return false;
        }*/
        return true;

    }

    public void requestPermissions(CordovaPlugin plugin, int requestCode, String[] permissions) {

        try {
            Method requestPermission = CordovaInterface.class.getDeclaredMethod("requestPermissions",
                    CordovaPlugin.class, int.class, String[].class);

            // If there is no exception, then this is cordova-android 5.0.0+
            requestPermission.invoke(plugin.cordova, plugin, requestCode, permissions);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults)
            throws JSONException {
        PluginResult result;
        for (int r : grantResults) {
            if (r == PackageManager.PERMISSION_DENIED) {
                result = new PluginResult(PluginResult.Status.ERROR, "Geolocation permission request was denied.");
                _saveCallbackContext.sendPluginResult(result);
                return;
            }
        }
        CordovaGoogleMaps.this.getMyLocation(_saveArgs, _saveCallbackContext);
    }

    public void putHtmlElements(final JSONArray args, final CallbackContext callbackContext) throws JSONException {

        final JSONObject elements = args.getJSONObject(0);
        if (mPluginLayout == null) {
            callbackContext.success();
            return;
        }

        if (!mPluginLayout.stopFlag || mPluginLayout.needUpdatePosition) {
            mPluginLayout.putHTMLElements(elements);
        }

        /*
        if (mPluginLayout.needUpdatePosition) {
          mPluginLayout.needUpdatePosition = false;
            
          for (String s : mPluginLayout.pluginMaps.keySet()) {
            mPluginLayout.updateViewPosition(s);
          }
        }
        */

        callbackContext.success();
    }

    @SuppressWarnings("unused")
    public void getMyLocation(final JSONArray args, final CallbackContext callbackContext) throws JSONException {

        // enableHighAccuracy = true -> PRIORITY_HIGH_ACCURACY
        // enableHighAccuracy = false -> PRIORITY_BALANCED_POWER_ACCURACY

        JSONObject params = args.getJSONObject(0);
        boolean isHighLocal = false;
        if (params.has("enableHighAccuracy")) {
            isHighLocal = params.getBoolean("enableHighAccuracy");
        }
        final boolean isHigh = isHighLocal;

        // Request geolocation permission.
        boolean locationPermission;
        try {
            Method hasPermission = CordovaInterface.class.getDeclaredMethod("hasPermission", String.class);

            String permission = "android.permission.ACCESS_COARSE_LOCATION";
            locationPermission = (Boolean) hasPermission.invoke(cordova, permission);
        } catch (Exception e) {
            PluginResult result;
            result = new PluginResult(PluginResult.Status.ILLEGAL_ACCESS_EXCEPTION);
            callbackContext.sendPluginResult(result);
            return;
        }

        if (!locationPermission) {
            _saveArgs = args;
            _saveCallbackContext = callbackContext;
            requestPermissions(CordovaGoogleMaps.this, 0, new String[] { "android.permission.ACCESS_FINE_LOCATION",
                    "android.permission.ACCESS_COARSE_LOCATION" });
            return;
        }

        if (googleApiClient == null) {
            googleApiClient = new GoogleApiClient.Builder(activity).addApi(LocationServices.API)
                    .addConnectionCallbacks(
                            new com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks() {

                                @Override
                                public void onConnected(Bundle connectionHint) {
                                    Log.e(TAG, "===> onConnected");
                                    CordovaGoogleMaps.this.sendNoResult(callbackContext);

                                    _checkLocationSettings(isHigh, callbackContext);
                                }

                                @Override
                                public void onConnectionSuspended(int cause) {
                                    Log.e(TAG, "===> onConnectionSuspended");
                                }

                            })
                    .addOnConnectionFailedListener(
                            new com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener() {

                                @Override
                                public void onConnectionFailed(@NonNull ConnectionResult result) {
                                    Log.e(TAG, "===> onConnectionFailed");

                                    PluginResult tmpResult = new PluginResult(PluginResult.Status.ERROR,
                                            result.toString());
                                    tmpResult.setKeepCallback(false);
                                    callbackContext.sendPluginResult(tmpResult);

                                    googleApiClient.disconnect();
                                }

                            })
                    .build();
            googleApiClient.connect();
        } else if (googleApiClient.isConnected()) {
            _checkLocationSettings(isHigh, callbackContext);
        }
    }

    private void _checkLocationSettings(final boolean enableHighAccuracy, final CallbackContext callbackContext) {

        LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().setAlwaysShow(true);

        LocationRequest locationRequest;
        locationRequest = LocationRequest.create().setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        builder.addLocationRequest(locationRequest);

        if (enableHighAccuracy) {
            locationRequest = LocationRequest.create().setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
            builder.addLocationRequest(locationRequest);
        }

        PendingResult<LocationSettingsResult> locationSettingsResult = LocationServices.SettingsApi
                .checkLocationSettings(googleApiClient, builder.build());

        locationSettingsResult.setResultCallback(new ResultCallback<LocationSettingsResult>() {

            @Override
            public void onResult(@NonNull LocationSettingsResult result) {
                final Status status = result.getStatus();
                switch (status.getStatusCode()) {
                case LocationSettingsStatusCodes.SUCCESS:
                    _requestLocationUpdate(false, enableHighAccuracy, callbackContext);
                    break;

                case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
                    // Location settings are not satisfied. But could be fixed by showing the user
                    // a dialog.
                    try {
                        //Keep the callback id
                        Bundle bundle = new Bundle();
                        bundle.putInt("type", ACTIVITY_LOCATION_DIALOG);
                        bundle.putString("callbackId", callbackContext.getCallbackId());
                        bundle.putBoolean("enableHighAccuracy", enableHighAccuracy);
                        int hashCode = bundle.hashCode();

                        bufferForLocationDialog.put("bundle_" + hashCode, bundle);
                        CordovaGoogleMaps.this.sendNoResult(callbackContext);

                        // Show the dialog by calling startResolutionForResult(),
                        // and check the result in onActivityResult().
                        cordova.setActivityResultCallback(CordovaGoogleMaps.this);
                        status.startResolutionForResult(cordova.getActivity(), hashCode);
                    } catch (SendIntentException e) {
                        // Show the dialog that is original version of this plugin.
                        _showLocationSettingsPage(enableHighAccuracy, callbackContext);
                    }
                    break;

                case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                    // Location settings are not satisfied. However, we have no way to fix the
                    // settings so we won't show the dialog.

                    JSONObject jsResult = new JSONObject();
                    try {
                        jsResult.put("status", false);
                        jsResult.put("error_code", "service_not_available");
                        jsResult.put("error_message", "This app has been rejected to use Location Services.");
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                    callbackContext.error(jsResult);
                    break;
                }
            }

        });
    }

    private void _showLocationSettingsPage(final boolean enableHighAccuracy,
            final CallbackContext callbackContext) {
        //Ask the user to turn on the location services.
        AlertDialog.Builder builder = new AlertDialog.Builder(this.activity);
        builder.setTitle("Improve location accuracy");
        builder.setMessage("To enhance your Maps experience:\n\n" + " - Enable Google apps location access\n\n"
                + " - Turn on GPS and mobile network location");
        builder.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                //Keep the callback id
                Bundle bundle = new Bundle();
                bundle.putInt("type", ACTIVITY_LOCATION_PAGE);
                bundle.putString("callbackId", callbackContext.getCallbackId());
                bundle.putBoolean("enableHighAccuracy", enableHighAccuracy);
                int hashCode = bundle.hashCode();

                bufferForLocationDialog.put("bundle_" + hashCode, bundle);
                CordovaGoogleMaps.this.sendNoResult(callbackContext);

                //Launch settings, allowing user to make a change
                cordova.setActivityResultCallback(CordovaGoogleMaps.this);
                Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                activity.startActivityForResult(intent, hashCode);
            }
        });
        builder.setNegativeButton("Skip", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                //No location service, no Activity
                dialog.dismiss();

                JSONObject result = new JSONObject();
                try {
                    result.put("status", false);
                    result.put("error_code", "service_denied");
                    result.put("error_message", "This app has been rejected to use Location Services.");
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                callbackContext.error(result);
            }
        });
        builder.create().show();
    }

    @SuppressWarnings("MissingPermission")
    private void _requestLocationUpdate(final boolean isRetry, final boolean enableHighAccuracy,
            final CallbackContext callbackContext) {

        int priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY;
        if (enableHighAccuracy) {
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY;
        }

        LocationRequest locationRequest = LocationRequest.create().setExpirationTime(5000).setNumUpdates(2)
                .setSmallestDisplacement(0).setPriority(priority).setInterval(5000);

        final PendingResult<Status> result = LocationServices.FusedLocationApi
                .requestLocationUpdates(googleApiClient, locationRequest, new LocationListener() {

                    @Override
                    public void onLocationChanged(Location location) {
                        /*
                        if (callbackContext.isFinished()) {
                          return;
                        }
                        */
                        JSONObject result;
                        try {
                            result = PluginUtil.location2Json(location);
                            result.put("status", true);
                            callbackContext.success(result);
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }

                        googleApiClient.disconnect();
                    }

                });

        result.setResultCallback(new ResultCallback<Status>() {

            public void onResult(Status status) {
                if (!status.isSuccess()) {
                    String errorMsg = status.getStatusMessage();
                    PluginResult result = new PluginResult(PluginResult.Status.ERROR, errorMsg);
                    callbackContext.sendPluginResult(result);
                } else {
                    // no update location
                    Location location = LocationServices.FusedLocationApi.getLastLocation(googleApiClient);
                    if (location != null) {
                        try {
                            JSONObject result = PluginUtil.location2Json(location);
                            result.put("status", true);
                            callbackContext.success(result);
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                    } else {
                        if (!isRetry) {
                            Toast.makeText(activity, "Waiting for location...", Toast.LENGTH_SHORT).show();

                            CordovaGoogleMaps.this.sendNoResult(callbackContext);

                            // Retry
                            Handler handler = new Handler();
                            handler.postDelayed(new Runnable() {
                                @Override
                                public void run() {
                                    _requestLocationUpdate(true, enableHighAccuracy, callbackContext);
                                }
                            }, 3000);
                        } else {
                            // Send back the error result
                            JSONObject result = new JSONObject();
                            try {
                                result.put("status", false);
                                result.put("error_code", "cannot_detect");
                                result.put("error_message", "Can not detect your location. Try again.");
                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                            callbackContext.error(result);
                        }
                    }
                }
            }
        });
    }

    @Override
    public void onReset() {
        super.onReset();
        if (mPluginLayout == null || mPluginLayout.pluginMaps == null) {
            return;
        }

        cordova.getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                CURRENT_URL = webView.getUrl();

                mPluginLayout.setBackgroundColor(Color.WHITE);

                Set<String> mapIds = mPluginLayout.pluginMaps.keySet();
                PluginMap pluginMap;

                // prevent the ConcurrentModificationException error.
                String[] mapIdArray = mapIds.toArray(new String[mapIds.size()]);
                for (String mapId : mapIdArray) {
                    if (mPluginLayout.pluginMaps.containsKey(mapId)) {
                        pluginMap = mPluginLayout.removePluginMap(mapId);
                        pluginMap.remove(null, null);
                        pluginMap.onDestroy();
                        mPluginLayout.HTMLNodes.remove(mapId);
                    }
                }
                mPluginLayout.HTMLNodes.clear();
                mPluginLayout.pluginMaps.clear();

                System.gc();
            }
        });

    }

    public void removeMap(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
        String mapId = args.getString(0);
        if (mPluginLayout.pluginMaps.containsKey(mapId)) {
            PluginMap pluginMap = mPluginLayout.removePluginMap(mapId);
            pluginMap.remove(null, null);
            pluginMap.onDestroy();
            mPluginLayout.HTMLNodes.remove(mapId);
        }
        callbackContext.success();
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public void getMap(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
        //------------------------------------------
        // Create an instance of PluginMap class.
        //------------------------------------------
        String mapId = args.getString(0);
        PluginMap pluginMap = new PluginMap();
        pluginMap.privateInitialize(mapId, cordova, webView, null);
        pluginMap.initialize(cordova, webView);
        pluginMap.mapCtrl = CordovaGoogleMaps.this;
        pluginMap.self = pluginMap;
        ((MyPlugin) pluginMap).CURRENT_PAGE_URL = CURRENT_URL;

        PluginEntry pluginEntry = new PluginEntry(mapId, pluginMap);
        pluginManager.addService(pluginEntry);

        pluginMap.getMap(args, callbackContext);
    }

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

        if (!bufferForLocationDialog.containsKey("bundle_" + requestCode)) {
            Log.e(TAG, "no key");
            return;
        }
        Bundle query = bufferForLocationDialog.get("bundle_" + requestCode);
        Log.d(TAG, "====> onActivityResult (" + resultCode + ")");

        switch (query.getInt("type")) {
        case ACTIVITY_LOCATION_DIALOG:
            // User was asked to enable the location setting.
            switch (resultCode) {
            case Activity.RESULT_OK:
                // All required changes were successfully made
                _inviteLocationUpdateAfterActivityResult(query);
                break;
            case Activity.RESULT_CANCELED:
                // The user was asked to change settings, but chose not to
                _userRefusedToUseLocationAfterActivityResult(query);
                break;
            default:
                break;
            }
            break;
        case ACTIVITY_LOCATION_PAGE:
            _onActivityResultLocationPage(query);
            break;
        }
    }

    private void _onActivityResultLocationPage(Bundle bundle) {
        String callbackId = bundle.getString("callbackId");
        CallbackContext callbackContext = new CallbackContext(callbackId, this.webView);

        LocationManager locationManager = (LocationManager) this.activity
                .getSystemService(Context.LOCATION_SERVICE);
        List<String> providers = locationManager.getAllProviders();
        int availableProviders = 0;
        if (mPluginLayout != null && mPluginLayout.isDebug) {
            Log.d(TAG, "---debug at getMyLocation(available providers)--");
        }
        Iterator<String> iterator = providers.iterator();
        String provider;
        boolean isAvailable;
        while (iterator.hasNext()) {
            provider = iterator.next();
            isAvailable = locationManager.isProviderEnabled(provider);
            if (isAvailable) {
                availableProviders++;
            }
            if (mPluginLayout != null && mPluginLayout.isDebug) {
                Log.d(TAG, "   " + provider + " = " + (isAvailable ? "" : "not ") + "available");
            }
        }
        if (availableProviders == 0) {
            JSONObject result = new JSONObject();
            try {
                result.put("status", false);
                result.put("error_code", "not_available");
                result.put("error_message",
                        "Since this device does not have any location provider, this app can not detect your location.");
            } catch (JSONException e) {
                e.printStackTrace();
            }
            callbackContext.error(result);
            return;
        }

        _inviteLocationUpdateAfterActivityResult(bundle);
    }

    private void _inviteLocationUpdateAfterActivityResult(Bundle bundle) {
        boolean enableHighAccuracy = bundle.getBoolean("enableHighAccuracy");
        String callbackId = bundle.getString("callbackId");
        CallbackContext callbackContext = new CallbackContext(callbackId, this.webView);
        this._requestLocationUpdate(false, enableHighAccuracy, callbackContext);
    }

    private void _userRefusedToUseLocationAfterActivityResult(Bundle bundle) {
        String callbackId = bundle.getString("callbackId");
        CallbackContext callbackContext = new CallbackContext(callbackId, this.webView);
        JSONObject result = new JSONObject();
        try {
            result.put("status", false);
            result.put("error_code", "service_denied");
            result.put("error_message", "This app has been rejected to use Location Services.");
        } catch (JSONException e) {
            e.printStackTrace();
        }
        callbackContext.error(result);
    }

    @Override
    public void onPause(boolean multitasking) {
        super.onPause(multitasking);
        cordova.getThreadPool().submit(new Runnable() {
            @Override
            public void run() {

                Set<String> mapIds = mPluginLayout.pluginMaps.keySet();
                PluginMap pluginMap;

                // prevent the ConcurrentModificationException error.
                String[] mapIdArray = mapIds.toArray(new String[mapIds.size()]);
                for (String mapId : mapIdArray) {
                    if (mPluginLayout.pluginMaps.containsKey(mapId)) {
                        pluginMap = mPluginLayout.pluginMaps.get(mapId);
                        pluginMap.mapView.onPause();
                    }
                }
            }
        });
    }

    @Override
    public void onResume(boolean multitasking) {
        super.onResume(multitasking);

        cordova.getThreadPool().submit(new Runnable() {
            @Override
            public void run() {

                Set<String> mapIds = mPluginLayout.pluginMaps.keySet();
                PluginMap pluginMap;

                // prevent the ConcurrentModificationException error.
                String[] mapIdArray = mapIds.toArray(new String[mapIds.size()]);
                for (String mapId : mapIdArray) {
                    if (mPluginLayout.pluginMaps.containsKey(mapId)) {
                        pluginMap = mPluginLayout.pluginMaps.get(mapId);
                        pluginMap.mapView.onResume();
                    }
                }
            }
        });
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        cordova.getThreadPool().submit(new Runnable() {
            @Override
            public void run() {

                Set<String> mapIds = mPluginLayout.pluginMaps.keySet();
                PluginMap pluginMap;

                // prevent the ConcurrentModificationException error.
                String[] mapIdArray = mapIds.toArray(new String[mapIds.size()]);
                for (String mapId : mapIdArray) {
                    if (mPluginLayout.pluginMaps.containsKey(mapId)) {
                        pluginMap = mPluginLayout.pluginMaps.get(mapId);
                        pluginMap.mapView.onDestroy();
                    }
                }
            }
        });
    }

    protected void sendNoResult(CallbackContext callbackContext) {
        PluginResult pluginResult = new PluginResult(PluginResult.Status.NO_RESULT);
        pluginResult.setKeepCallback(true);
        callbackContext.sendPluginResult(pluginResult);
    }

    /**
     * Called by the system when the device configuration changes while your activity is running.
     *
     * @param newConfig      The new device configuration
     */
    /*
    public void onConfigurationChanged(Configuration newConfig) {
      super.onConfigurationChanged(newConfig);
    // Checks the orientation of the screen
      if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        Toast.makeText(activity, "landscape", Toast.LENGTH_SHORT).show();
      } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
        Toast.makeText(activity, "portrait", Toast.LENGTH_SHORT).show();
      }
    }
    */

}