/**
* Filename: LocationMarker.java (in org.repin.android.ui.mapview)
* This file is part of the Redpin project.
*
* Redpin is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Redpin 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Redpin. If not, see <http://www.gnu.org/licenses/>.
*
* (c) Copyright ETH Zurich, Pascal Brogle, Philipp Bolliger, 2010, ALL RIGHTS RESERVED.
*
* www.redpin.org
*/
package org.redpin.android.ui.mapview;
import org.redpin.android.R;
import org.redpin.android.core.Location;
import org.redpin.android.net.home.LocationRemoteHome;
import android.content.Context;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.FrameLayout;
/**
* {@link LocationMarker} is the graphical representation of an {@link Location}
* on the {@link MapView}
*
* @author Pascal Brogle (broglep@student.ethz.ch)
*
*/
public class LocationMarker extends Button implements OnClickListener {
private Location location;
int markerX, markerY;
static int size = 44;
static boolean densityCalculated = false;
private FrameLayout.LayoutParams layout;
boolean clicked = false;
boolean positionChanged = false;
private float mLastMotionX;
private float mLastMotionY;
private int unscaledX;
private int unscaledY;
private float scale;
private ViewGroup container;
private LocationMarkerAnnotation annotation;
private CurrentLocationCircle circle;
private boolean isCurrentLocation = false;
private float density;
/**
*
* @param context
* {@link Context}
* @param l
* {@link Location} that the marker respresent
* @param container
* The {@link ViewGroup} the marker will be added to
*/
public LocationMarker(Context context, Location l, ViewGroup container) {
super(context);
this.container = container;
initView(context);
setLocation(l);
}
/**
* Initiates the {@link LocationMarker}
*
* @param context
* {@link Context}
*/
private void initView(Context context) {
setVisibility(INVISIBLE);
setIsCurrentLocation(false);
updateBackground();
setEnabled(false);
setOnClickListener(this);
if (!densityCalculated) {
density = getContext().getResources().getDisplayMetrics().density;
size = (int) (size * density);
LocationMarker.densityCalculated = true;
}
setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT, Gravity.NO_GRAVITY));
setWidth(size);
setHeight(size);
}
/**
* Sets the markers location
*
* @param l
* {@link Location}
*/
private void setLocation(Location l) {
this.location = l;
markerX = location.getMapXcord() - size / 2;
markerY = location.getMapYcord() - size / 2;
unscaledX = markerX;
unscaledY = markerY;
System.out.println("Unscaled Marker: " + unscaledX + "," + unscaledY);
positionChanged();
}
/**
*
* @return {@link Location} represented by the {@link LocationMarker}
*/
public Location getLocation() {
return location;
}
/**
* Updates the coordinates of the locations and saves the change on the
* server
*/
private void updateLocation() {
updateLocationCords();
LocationRemoteHome.updateLocation(location);
}
/**
* Updates the coordinates
*/
private void updateLocationCords() {
location.setMapXcord(unscaledX + size / 2);
location.setMapYcord(unscaledY + size / 2);
}
/**
* Changes the look of the marker depending if it represent an current
* {@link Location}
*
* @param b
* Whether the {@link Location} represented by the
* {@link Location} is the current {@link Location}
*/
public void setIsCurrentLocation(boolean b) {
isCurrentLocation = b;
updateBackground();
if (isCurrentLocation) {
showAnnotation();
showCircle();
} else {
hideAnnotation();
hideCircle();
}
}
private boolean enabled = false;
/**
* {@inheritDoc}
*/
@Override
public void setEnabled(boolean b) {
if (isCurrentLocation) {
enabled = false;
showAnnotation();
} else {
enabled = b;
}
if (annotation != null) {
annotation.setEnabled(b);
}
}
/**
* {@inheritDoc}
*/
@Override
public void onClick(View v) {
clicked = !clicked;
updateBackground();
if (clicked) {
showAnnotation();
} else {
hideAnnotation();
}
}
/**
* Updates the background depending on the state
*/
protected void updateBackground() {
if (isCurrentLocation) {
setBackgroundResource(R.drawable.current_location);
return;
}
if (clicked && enabled) {
setBackgroundResource(R.drawable.purple_map_pin_big);
} else {
setBackgroundResource(R.drawable.red_map_pin_big);
}
return;
}
/**
* Shows the {@link LocationMarkerAnnotation}
*/
void showAnnotation() {
if (annotation == null) {
annotation = new LocationMarkerAnnotation(this);
container.addView(annotation);
}
annotation.setEnabled(enabled);
annotation.setVisibility(getVisibility());
}
/**
* Starts editing of the {@link LocationMarkerAnnotation}
*/
void beginEdit() {
if (annotation == null || !enabled)
return;
annotation.requestFocus();
InputMethodManager imm = (InputMethodManager) getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED,
InputMethodManager.HIDE_IMPLICIT_ONLY);
}
/**
* Hides the {@link LocationMarkerAnnotation}
*/
void hideAnnotation() {
if (annotation == null)
return;
annotation.setVisibility(GONE);
}
/**
* Shows a {@link CurrentLocationCircle} around the {@link LocationMarker}
*/
private void showCircle() {
if (circle == null) {
circle = new CurrentLocationCircle(this, 25);
container.addView(circle);
}
circle.setVisibility(getVisibility());
}
/**
* Hides {@link CurrentLocationCircle}
*/
private void hideCircle() {
if (circle == null)
return;
circle.setVisibility(GONE);
}
boolean isDragging = false;
/**
* Handles the dragging of the {@link LocationMarker}
*
* @param ev
* The motion event
* @return <code>True</code> if the event was handled, <code>false</code>
* otherwise.
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (!enabled || (!clicked && !isDragging)) {
return super.onTouchEvent(ev);
}
boolean handled = false;
final int action = ev.getAction();
final float y = ev.getY();
final float x = ev.getX();
switch (action) {
case MotionEvent.ACTION_DOWN:
mLastMotionX = x;
mLastMotionY = y;
System.out.println("start dragging " + mLastMotionX + ","
+ mLastMotionY);
break;
case MotionEvent.ACTION_MOVE:
isDragging = true;
final float scrollX = mLastMotionX - x;
final float scrollY = mLastMotionY - y;
System.out.println("move by " + scrollX + "," + scrollY);
moveMarkerBy((int) scrollX, (int) scrollY);
break;
case MotionEvent.ACTION_UP:
// dragging is finished, update location
if (isDragging) {
handled = true;
updateLocation();
isDragging = false;
}
break;
case MotionEvent.ACTION_CANCEL:
isDragging = false;
break;
}
if (handled) {
return true;
}
return super.onTouchEvent(ev);
}
/**
* Notify {@link LocationMarkerAnnotation} and {@link CurrentLocationCircle}
* that the {@link LocationMarker} position changed
*/
public void positionChanged() {
layout = (android.widget.FrameLayout.LayoutParams) getLayoutParams();
layout.setMargins(markerX, markerY, 0, 0);
setLayoutParams(layout);
if (annotation != null) {
annotation.markerPositionChanged = true;
}
if (isCurrentLocation && circle != null) {
circle.markerPositionChanged = true;
}
}
/**
* Moves the marker by specified distance
*
* @param distanceX
* Distance in X direction
* @param distanceY
* Distance in Y direction
*/
private void moveMarkerBy(int distanceX, int distanceY) {
setMarkerXY(markerX - distanceX, markerY - distanceY);
unscaledX = (int) (markerX / scale);
unscaledY = (int) (markerY / scale);
System.out.println("Marker: " + markerX + "," + markerY);
System.out.println("Unscaled Marker: " + unscaledX + "," + unscaledY);
}
public float[] getPosition() {
return new float[] { markerX, markerY };
}
public void onScaleChanged(float newScale) {
if (newScale == 0)
return;
scale = newScale;
int x, y;
x = (int) ((((float) (unscaledX + size / 2)) * scale) - size / 2);
y = (int) ((((float) (unscaledY + size / 2)) * scale) - size / 2);
setMarkerXY(x, y);
}
int lastMarkerX = -1;
int lastMarkerY = -1;
private void setMarkerXY(int x, int y) {
int oldX = markerX;
int oldY = markerY;
markerX = x;
markerY = y;
positionChanged();
requestLayout();
lastMarkerX = oldX;
lastMarkerY = oldY;
}
/**
* Removes the {@link LocationMarker} from the container and if applicable
* the {@link LocationMarkerAnnotation} and {@link CurrentLocationCircle}
*/
protected void removeFromContainer() {
if (annotation != null) {
container.removeView(annotation);
}
if (circle != null) {
container.removeView(circle);
}
container.removeView(this);
}
/**
* {@inheritDoc}
*/
@Override
public void setVisibility(int visibility) {
if (annotation != null) {
annotation.setVisibility(visibility);
}
if (circle != null) {
circle.setVisibility(visibility);
}
super.setVisibility(visibility);
}
}