/*
* Copyright 2014 Thomas Hoffmann
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.j4velin.mapsmeasure;
import android.Manifest;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.location.Location;
import android.net.Uri;
import android.os.BadParcelableException;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v4.app.FragmentActivity;
import android.support.v4.content.PermissionChecker;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.util.DisplayMetrics;
import android.util.Pair;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.widget.AdapterView;
import android.widget.FrameLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.vending.billing.IInAppBillingService;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapClickListener;
import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polygon;
import com.google.android.gms.maps.model.PolygonOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;
import com.google.maps.android.SphericalUtil;
import org.json.JSONObject;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Stack;
import de.j4velin.mapsmeasure.wrapper.API17Wrapper;
public class Map extends FragmentActivity implements OnMapReadyCallback {
enum MeasureType {
DISTANCE, AREA, ELEVATION
}
// the map to draw to
private GoogleMap mMap;
private DrawerLayout mDrawerLayout;
// the stacks - everytime the user touches the map, an entry is pushed
private final Stack<LatLng> trace = new Stack<>();
private final Stack<Polyline> lines = new Stack<>();
private final Stack<Marker> points = new Stack<>();
private Polygon areaOverlay;
private Pair<Float, Float> altitude;
private float distance; // in meters
private MeasureType type; // the currently selected measure type
private TextView valueTv; // the view displaying the distance/area & unit
static boolean metric; // display in metric units
private static BitmapDescriptor marker;
private IInAppBillingService mService;
private static boolean PRO_VERSION = false;
private DrawerListAdapter drawerListAdapert;
private GoogleApiClient mGoogleApiClient;
private ElevationView elevationView;
private boolean navBarOnRight;
private int drawerSize, statusbar, navBarHeight;
// store last location callback in case we dont have location permission yet and need to execute it later
private LocationCallback lastLocationCallback;
// Constants
private final static int COLOR_LINE = Color.argb(128, 0, 0, 0), COLOR_POINT =
Color.argb(128, 255, 0, 0);
private final static float LINE_WIDTH = 5f;
final static NumberFormat formatter_two_dec = NumberFormat.getInstance(Locale.getDefault());
private final static NumberFormat formatter_no_dec =
NumberFormat.getInstance(Locale.getDefault());
private final static int REQUEST_LOCATION_PERMISSION = 0;
final static String SKU = "de.j4velin.mapsmeasure.billing.pro";
private final ServiceConnection mServiceConn = new ServiceConnection() {
@Override
public void onServiceDisconnected(final ComponentName name) {
mService = null;
}
@Override
public void onServiceConnected(final ComponentName name, final IBinder service) {
mService = IInAppBillingService.Stub.asInterface(service);
try {
Bundle ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null);
if (ownedItems.getInt("RESPONSE_CODE") == 0) {
PRO_VERSION =
ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST") != null &&
ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST")
.contains(SKU);
getSharedPreferences("settings", Context.MODE_PRIVATE).edit()
.putBoolean("pro", PRO_VERSION).commit();
}
} catch (RemoteException e) {
Toast.makeText(Map.this, e.getClass().getName() + ": " + e.getMessage(),
Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
};
public GoogleMap getMap() {
return mMap;
}
public void closeDrawer() {
if (mDrawerLayout != null) mDrawerLayout.closeDrawers();
}
/**
* Get the formatted string for the valueTextView.
* <p/>
* Depending on whether 'showArea' is set, the returned string shows the
* distance of the trace or the area between them. If 'showArea' is set,
* this call might be expensive as the area is computed here and not cached.
*
* @return the formatted text for the valueTextView
*/
private String getFormattedString() {
if (type == MeasureType.DISTANCE) {
elevationView.setVisibility(View.GONE);
if (metric) {
if (distance > 1000) return formatter_two_dec.format(distance / 1000) + " km";
else return formatter_two_dec.format(Math.max(0, distance)) + " m";
} else {
if (distance > 1609) return formatter_two_dec.format(distance / 1609.344f) + " mi";
else if (distance > 30)
return formatter_two_dec.format(distance / 1609.344f) + " mi\n" +
formatter_two_dec.format(Math.max(0, distance / 0.3048f)) + " ft";
else return formatter_two_dec.format(Math.max(0, distance / 0.3048f)) + " ft";
}
} else if (type == MeasureType.AREA) {
elevationView.setVisibility(View.GONE);
double area;
if (areaOverlay != null) areaOverlay.remove();
if (trace.size() >= 3) {
area = SphericalUtil.computeArea(trace);
areaOverlay = mMap.addPolygon(
new PolygonOptions().addAll(trace).strokeWidth(0).fillColor(COLOR_POINT));
} else {
area = 0;
}
if (metric) {
if (area > 1000000)
return formatter_two_dec.format(Math.max(0, area / 1000000d)) + " km²";
else return formatter_no_dec.format(Math.max(0, area)) + " m²";
} else {
if (area >= 2589989)
return formatter_two_dec.format(Math.max(0, area / 2589988.110336d)) + " mi²";
else return formatter_no_dec.format(Math.max(0, area / 0.09290304d)) + " ft²";
}
} else if (type == MeasureType.ELEVATION) {
if (altitude == null) {
final Handler h = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
altitude = Util.updateElevationView(elevationView, trace);
h.post(new Runnable() {
@Override
public void run() {
if (isFinishing()) return;
if (altitude == null) {
Dialogs.getElevationErrorDialog(Map.this).show();
changeType(MeasureType.DISTANCE);
} else {
updateValueText();
elevationView.invalidate();
}
}
});
}
}).start();
return "Loading...";
} else {
String re = metric ? formatter_two_dec.format(altitude.first) + " m\u2B06, " +
formatter_two_dec.format(-1 * altitude.second) + " m\u2B07" :
formatter_two_dec.format(altitude.first / 0.3048f) + " ft\u2B06" +
formatter_two_dec.format(-1 * altitude.second / 0.3048f) +
" ft\u2B07";
if (!trace.isEmpty()) {
try {
float lastPoint = Util.lastElevation;
if (lastPoint > -Float.MAX_VALUE) {
re += "\n" + (metric ? formatter_two_dec.format(lastPoint) + " m" :
formatter_two_dec.format(lastPoint / 0.3048f) + " ft");
}
} catch (Exception e) {
e.printStackTrace();
}
}
elevationView.setVisibility(trace.size() > 1 ? View.VISIBLE : View.GONE);
altitude = null;
return re;
}
} else {
return "not yet supported";
}
}
@Override
protected void onRestoreInstanceState(final Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
try {
metric = savedInstanceState.getBoolean("metric");
@SuppressWarnings("unchecked")
// Casting to Stack<LatLng> apparently results in
// "java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.util.Stack"
// on some devices
List<LatLng> tmp = (List<LatLng>) savedInstanceState.getSerializable("trace");
Iterator<LatLng> it = tmp.iterator();
while (it.hasNext()) {
addPoint(it.next());
}
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(
new LatLng(savedInstanceState.getDouble("position-lat"),
savedInstanceState.getDouble("position-lon")),
savedInstanceState.getFloat("position-zoom")));
} catch (Exception e) {
if (BuildConfig.DEBUG) Logger.log(e);
e.printStackTrace();
}
}
@Override
protected void onSaveInstanceState(final Bundle outState) {
outState.putSerializable("trace", trace);
outState.putBoolean("metric", metric);
if (mMap != null) { // might be null if there is an issue with Google
// Play Services
outState.putDouble("position-lon", mMap.getCameraPosition().target.longitude);
outState.putDouble("position-lat", mMap.getCameraPosition().target.latitude);
outState.putFloat("position-zoom", mMap.getCameraPosition().zoom);
}
super.onSaveInstanceState(outState);
}
/**
* Adds a new point, calculates the new distance and draws the point and a
* line to it
*
* @param p the new point
*/
void addPoint(final LatLng p) {
if (!trace.isEmpty()) {
lines.push(mMap.addPolyline(
new PolylineOptions().color(COLOR_LINE).width(LINE_WIDTH).add(trace.peek())
.add(p)));
distance += SphericalUtil.computeDistanceBetween(p, trace.peek());
}
points.push(drawMarker(p));
trace.push(p);
updateValueText();
}
/**
* Resets the map by removing all points, lines and setting the text to 0
*/
void clear() {
mMap.clear();
trace.clear();
lines.clear();
points.clear();
distance = 0;
updateValueText();
}
/**
* Removes the last added point, the line to it and updates the distance
*/
private void removeLast() {
if (trace.isEmpty()) return;
points.pop().remove();
LatLng remove = trace.pop();
if (!trace.isEmpty())
distance -= SphericalUtil.computeDistanceBetween(remove, trace.peek());
if (!lines.isEmpty()) lines.pop().remove();
updateValueText();
}
@SuppressLint("NewApi")
@Override
public void onCreate(final Bundle savedInstanceState) {
if (BuildConfig.DEBUG && Build.VERSION.SDK_INT >= 23 && PermissionChecker
.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
PermissionChecker.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
}
try {
super.onCreate(savedInstanceState);
} catch (final BadParcelableException bpe) {
if (BuildConfig.DEBUG) Logger.log(bpe);
}
init();
}
/**
* Initializes everything
*/
private void init() {
setContentView(R.layout.activity_map);
elevationView = (ElevationView) findViewById(R.id.elevationsview);
formatter_no_dec.setMaximumFractionDigits(0);
formatter_two_dec.setMaximumFractionDigits(2);
final SharedPreferences prefs = getSharedPreferences("settings", Context.MODE_PRIVATE);
// use metric a the default everywhere, except in the US
metric = prefs.getBoolean("metric", !Locale.getDefault().equals(Locale.US));
final View topCenterOverlay = findViewById(R.id.topCenterOverlay);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
final View menuButton = findViewById(R.id.menu);
if (menuButton != null) {
menuButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
mDrawerLayout.openDrawer(GravityCompat.START);
}
});
}
if (mDrawerLayout != null) {
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
mDrawerLayout.setDrawerListener(new DrawerLayout.DrawerListener() {
private boolean menuButtonVisible = true;
@Override
public void onDrawerStateChanged(int newState) {
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public void onDrawerSlide(final View drawerView, final float slideOffset) {
if (android.os.Build.VERSION.SDK_INT >=
android.os.Build.VERSION_CODES.HONEYCOMB)
topCenterOverlay.setAlpha(1 - slideOffset);
if (menuButtonVisible && menuButton != null && slideOffset > 0) {
menuButton.setVisibility(View.INVISIBLE);
menuButtonVisible = false;
}
}
@Override
public void onDrawerOpened(final View drawerView) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
topCenterOverlay.setVisibility(View.INVISIBLE);
}
@Override
public void onDrawerClosed(final View drawerView) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
topCenterOverlay.setVisibility(View.VISIBLE);
if (menuButton != null) {
menuButton.setVisibility(View.VISIBLE);
menuButtonVisible = true;
}
}
});
}
((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
.getMapAsync(this);
valueTv = (TextView) findViewById(R.id.distance);
updateValueText();
valueTv.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
if (type == MeasureType.DISTANCE) {
changeType(MeasureType.AREA);
}
// only switch to elevation mode is an internet connection is
// available and user has access to this feature
else if (type == MeasureType.AREA && Util.checkInternetConnection(Map.this) &&
PRO_VERSION) {
changeType(MeasureType.ELEVATION);
} else {
if (BuildConfig.DEBUG) Logger.log("internet connection available: " +
Util.checkInternetConnection(Map.this));
changeType(MeasureType.DISTANCE);
}
}
});
View delete = findViewById(R.id.delete);
delete.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
removeLast();
}
});
delete.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(final View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(Map.this);
builder.setMessage(getString(R.string.delete_all, trace.size()));
builder.setPositiveButton(android.R.string.yes,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
clear();
dialog.dismiss();
}
});
builder.setNegativeButton(android.R.string.no,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builder.create().show();
return true;
}
});
// Drawer stuff
ListView drawerList = (ListView) findViewById(R.id.left_drawer);
drawerListAdapert = new DrawerListAdapter(this);
drawerList.setAdapter(drawerListAdapert);
drawerList.setDivider(null);
drawerList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(final AdapterView<?> parent, final View view, int position,
long id) {
switch (position) {
case 0: // Search before Android 5.0
Dialogs.getSearchDialog(Map.this).show();
closeDrawer();
break;
case 2: // Units
Dialogs.getUnits(Map.this, distance, SphericalUtil.computeArea(trace))
.show();
closeDrawer();
break;
case 3: // distance
changeType(MeasureType.DISTANCE);
break;
case 4: // area
changeType(MeasureType.AREA);
break;
case 5: // elevation
if (PRO_VERSION) {
changeType(MeasureType.ELEVATION);
} else {
Dialogs.getElevationAccessDialog(Map.this, mService).show();
}
break;
case 7: // map
changeView(GoogleMap.MAP_TYPE_NORMAL);
break;
case 8: // satellite
changeView(GoogleMap.MAP_TYPE_HYBRID);
break;
case 9: // terrain
changeView(GoogleMap.MAP_TYPE_TERRAIN);
break;
case 11: // save
Dialogs.getSaveNShare(Map.this, trace).show();
closeDrawer();
break;
case 12: // more apps
try {
startActivity(new Intent(Intent.ACTION_VIEW,
Uri.parse("market://search?q=pub:j4velin"))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
} catch (ActivityNotFoundException anf) {
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(
"https://play.google.com/store/apps/developer?id=j4velin"))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
break;
case 13: // about
Dialogs.getAbout(Map.this).show();
closeDrawer();
break;
default:
break;
}
}
});
changeType(MeasureType.DISTANCE);
// KitKat translucent decor enabled? -> Add some margin/padding to the
// drawer
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
statusbar = Util.getStatusBarHeight(this);
FrameLayout.LayoutParams lp =
(FrameLayout.LayoutParams) topCenterOverlay.getLayoutParams();
lp.setMargins(0, statusbar + 10, 0, 0);
topCenterOverlay.setLayoutParams(lp);
// on most devices and in most orientations, the navigation bar
// should be at the bottom and therefore reduces the available
// display height
navBarHeight = Util.getNavigationBarHeight(this);
DisplayMetrics total, available;
total = new DisplayMetrics();
available = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(available);
API17Wrapper.getRealMetrics(getWindowManager().getDefaultDisplay(), total);
navBarOnRight = getResources().getConfiguration().orientation ==
android.content.res.Configuration.ORIENTATION_LANDSCAPE &&
(total.widthPixels - available.widthPixels > 0);
FrameLayout.LayoutParams elevationParams =
(FrameLayout.LayoutParams) elevationView.getLayoutParams();
drawerSize = mDrawerLayout == null ? Util.dpToPx(this, 200) : 0;
if (navBarOnRight) {
// in landscape on phones, the navigation bar might be at the
// right side, reducing the available display width
drawerList.setPadding(0, statusbar + 10, 0, 0);
if (menuButton != null) menuButton.setPadding(0, 0, 0, 0);
elevationParams.setMargins(drawerSize, 0, navBarHeight, 0);
} else {
drawerList.setPadding(0, statusbar + 10, 0, 0);
drawerListAdapert.setMarginBottom(navBarHeight);
if (menuButton != null) menuButton.setPadding(0, 0, 0, navBarHeight);
elevationParams.setMargins(Math.max(drawerSize, Util.dpToPx(this, 25)), 0, 0,
navBarHeight);
}
elevationView.setLayoutParams(elevationParams);
}
PRO_VERSION |= prefs.getBoolean("pro", false);
if (!PRO_VERSION) {
bindService(new Intent("com.android.vending.billing.InAppBillingService.BIND")
.setPackage("com.android.vending"), mServiceConn, Context.BIND_AUTO_CREATE);
}
}
@Override
public void onMapReady(final GoogleMap googleMap) {
mMap = googleMap;
marker = BitmapDescriptorFactory.fromResource(R.drawable.marker);
changeView(getSharedPreferences("settings", Context.MODE_PRIVATE)
.getInt("mapView", GoogleMap.MAP_TYPE_NORMAL));
mMap.setOnMarkerClickListener(new OnMarkerClickListener() {
@Override
public boolean onMarkerClick(final Marker click) {
addPoint(click.getPosition());
return true;
}
});
mMap.setOnMyLocationButtonClickListener(new GoogleMap.OnMyLocationButtonClickListener() {
@Override
public boolean onMyLocationButtonClick() {
getCurrentLocation(new LocationCallback() {
@Override
public void gotLocation(final Location location) {
if (location != null) {
LatLng myLocation =
new LatLng(location.getLatitude(), location.getLongitude());
double distance = SphericalUtil.computeDistanceBetween(myLocation,
mMap.getCameraPosition().target);
// Only if the distance is less than 50cm we are on our location, add the marker
if (distance < 0.5) {
Toast.makeText(Map.this, R.string.marker_on_current_location,
Toast.LENGTH_SHORT).show();
addPoint(myLocation);
} else {
if (BuildConfig.DEBUG)
Logger.log("location accuracy too bad to add point");
moveCamera(myLocation);
}
}
}
});
return true;
}
});
mMap.setOnMapClickListener(new OnMapClickListener() {
@Override
public void onMapClick(final LatLng center) {
addPoint(center);
}
});
if (hasLocationPermission()) {
//noinspection MissingPermission
mMap.setMyLocationEnabled(true);
}
// KitKat translucent decor enabled? -> Add some margin/padding to the map
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
if (navBarOnRight) {
// in landscape on phones, the navigation bar might be at the
// right side, reducing the available display width
mMap.setPadding(drawerSize, statusbar, navBarHeight, 0);
} else {
mMap.setPadding(0, statusbar, 0, navBarHeight);
}
}
// check if open with csv file
if (Intent.ACTION_VIEW.equals(getIntent().getAction())) {
try {
Util.loadFromFile(getIntent().getData(), this);
} catch (IOException e) {
if (BuildConfig.DEBUG) Logger.log(e);
Toast.makeText(this, getString(R.string.error,
e.getClass().getSimpleName() + "\n" + e.getMessage()), Toast.LENGTH_LONG)
.show();
e.printStackTrace();
}
} else {
// dont move to current position if started with a csv file
getCurrentLocation(new LocationCallback() {
@Override
public void gotLocation(final Location location) {
if (location != null && mMap.getCameraPosition().zoom <= 2) {
moveCamera(new LatLng(location.getLatitude(), location.getLongitude()));
}
}
});
}
}
/**
* Tries to get the users current position
*
* @param callback the callback which should be called when we got a location
*/
private void getCurrentLocation(final LocationCallback callback) {
if (hasLocationPermission()) {
if (callback == null) return;
mGoogleApiClient = new GoogleApiClient.Builder(this).addApi(LocationServices.API)
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
@Override
public void onConnected(final Bundle bundle) {
//noinspection ResourceType
Location l = LocationServices.FusedLocationApi
.getLastLocation(mGoogleApiClient);
mGoogleApiClient.disconnect();
callback.gotLocation(l);
}
@Override
public void onConnectionSuspended(int cause) {
if (BuildConfig.DEBUG) Logger.log("connection suspended: " + cause);
}
}).build();
mGoogleApiClient.connect();
} else { // no permission
if (Build.VERSION.SDK_INT >= 23) {
lastLocationCallback = callback;
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION_PERMISSION);
} else if (BuildConfig.DEBUG) Logger.log("no permission and no way to request them");
}
}
/**
* Moves the map view to the given position
*
* @param pos the position to move to
*/
public void moveCamera(final LatLng pos) {
moveCamera(pos, 16f);
}
/**
* Moves the map view to the given position
*
* @param pos the position to move to
* @param zoom the zoom to apply
*/
private void moveCamera(final LatLng pos, float zoom) {
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(pos, zoom));
}
/**
* Change the "type" of measuring: Distance, Area or Altitude
*
* @param newType the type to change to
*/
private void changeType(final MeasureType newType) {
type = newType;
drawerListAdapert.changeType(newType);
updateValueText();
if (mDrawerLayout != null) mDrawerLayout.closeDrawers();
if (newType != MeasureType.AREA) {
if (areaOverlay != null) areaOverlay.remove();
}
}
/**
* Change between normal map, satellite hybrid and terrain view
*
* @param newView the new view, should be one of GoogleMap.MAP_TYPE_NORMAL,
* GoogleMap.MAP_TYPE_HYBRID or GoogleMap.MAP_TYPE_TERRAIN
*/
private void changeView(int newView) {
if (mMap != null) mMap.setMapType(newView);
drawerListAdapert.changeView(newView);
if (mDrawerLayout != null) mDrawerLayout.closeDrawers();
getSharedPreferences("settings", Context.MODE_PRIVATE).edit().putInt("mapView", newView)
.commit();
}
/**
* Draws a marker at the given point.
* <p/>
* Should be called when the users touches the map and adds an entry to the
* stacks
*
* @param center the point where the user clicked
* @return the drawn Polygon
*/
private Marker drawMarker(final LatLng center) {
return mMap.addMarker(
new MarkerOptions().position(center).flat(true).anchor(0.5f, 0.5f).icon(marker));
}
/**
* Updates the valueTextView at the top of the screen
*/
void updateValueText() {
if (valueTv != null) valueTv.setText(getFormattedString());
}
@Override
public void onRequestPermissionsResult(int requestCode, final String[] permissions,
final int[] grantResults) {
switch (requestCode) {
case REQUEST_LOCATION_PERMISSION:
if (grantResults.length > 0 &&
grantResults[0] == PermissionChecker.PERMISSION_GRANTED &&
grantResults[1] == PermissionChecker.PERMISSION_GRANTED) {
getCurrentLocation(lastLocationCallback);
//noinspection ResourceType
mMap.setMyLocationEnabled(true);
} else {
String savedLocation = getSharedPreferences("settings", Context.MODE_PRIVATE)
.getString("lastLocation", null);
if (savedLocation != null && savedLocation.contains("#")) {
String[] data = savedLocation.split("#");
try {
if (data.length == 3) {
moveCamera(new LatLng(Double.parseDouble(data[0]),
Double.parseDouble(data[1])), Float.parseFloat(data[2]));
}
} catch (NumberFormatException nfe) {
nfe.printStackTrace();
}
}
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
@Override
public boolean onPrepareOptionsMenu(final Menu menu) {
if (mDrawerLayout == null) return true;
if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) mDrawerLayout.closeDrawers();
else mDrawerLayout.openDrawer(GravityCompat.START);
return false;
}
@Override
public void onDestroy() {
super.onDestroy();
if (mMap != null) {
CameraPosition lastPosition = mMap.getCameraPosition();
if (lastPosition != null) {
getSharedPreferences("settings", Context.MODE_PRIVATE).edit()
.putString("lastLocation",
lastPosition.target.latitude + "#" + lastPosition.target.longitude +
"#" + lastPosition.zoom).commit();
}
}
if (mService != null) {
unbindService(mServiceConn);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 42 && resultCode == RESULT_OK) {
if (data.getIntExtra("RESPONSE_CODE", 0) == 0) {
try {
JSONObject jo = new JSONObject(data.getStringExtra("INAPP_PURCHASE_DATA"));
PRO_VERSION = jo.getString("productId").equals(SKU) &&
jo.getString("developerPayload").equals(getPackageName());
getSharedPreferences("settings", Context.MODE_PRIVATE).edit()
.putBoolean("pro", PRO_VERSION).commit();
changeType(MeasureType.ELEVATION);
} catch (Exception e) {
if (BuildConfig.DEBUG) Logger.log(e);
Toast.makeText(this, e.getClass().getName() + ": " + e.getMessage(),
Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
}
}
private boolean hasLocationPermission() {
return PermissionChecker
.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) ==
PermissionChecker.PERMISSION_GRANTED && PermissionChecker
.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) ==
PermissionChecker.PERMISSION_GRANTED;
}
}