/* * Copyright 2013-2016 microG Project Team * * 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 com.google.android.maps; import android.graphics.Canvas; import android.graphics.Point; import android.graphics.drawable.Drawable; import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; import org.microg.annotation.OriginalApi; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; @OriginalApi public abstract class ItemizedOverlay<Item extends OverlayItem> extends Overlay implements Overlay.Snappable { private static final String TAG = ItemizedOverlay.class.getName(); @OriginalApi protected int mLastFocusedIndex; private final Drawable defaultMarker; private final ArrayList<Item> internalList = new ArrayList<Item>(); private int latSpanE6; private int lonSpanE6; private Item focusedItem; private GeoPoint center; private OnFocusChangeListener focusChangeListener; private boolean pendingFocusChangeEvent = false; private boolean drawFocusedItem = true; private Comparator<Item> latitudeComparator = new Comparator<Item>() { @Override public int compare(Item lhs, Item rhs) { return lhs.getPoint().getLatitudeE6() - rhs.getPoint().getLatitudeE6(); } }; private Comparator<Item> drawComparator = new Comparator<Item>() { @Override public int compare(Item lhs, Item rhs) { return getIndexToDraw(lhs.index) - getIndexToDraw(rhs.index); } }; @OriginalApi public ItemizedOverlay(Drawable defaultMarker) { this.defaultMarker = defaultMarker; } @OriginalApi protected static Drawable boundCenter(Drawable ballon) { int width = ballon.getIntrinsicWidth(); int halfWidth = width / 2; int height = ballon.getIntrinsicHeight(); int halfHeight = height / 2; ballon.setBounds(-halfWidth, -halfHeight, width - halfWidth, height - halfHeight); return ballon; } @OriginalApi protected static Drawable boundCenterBottom(Drawable ballon) { int width = ballon.getIntrinsicWidth(); int halfWidth = width / 2; int height = ballon.getIntrinsicHeight(); ballon.setBounds(-halfWidth, 1 - height, width - halfWidth, 1); return ballon; } @OriginalApi protected abstract Item createItem(int i); @OriginalApi public void draw(Canvas canvas, MapView mapView, boolean shadow) { if (pendingFocusChangeEvent && focusChangeListener != null) { focusChangeListener.onFocusChanged(this, focusedItem); } pendingFocusChangeEvent = false; List<Item> sortedList = new ArrayList<Item>(); for (int i = 0; i < internalList.size(); i++) { sortedList.add(getItem(i)); } Collections.sort(sortedList, drawComparator); Point point = new Point(); for (Item item : sortedList) { if (item != null) { int state = (drawFocusedItem && (focusedItem == item) ? OverlayItem.ITEM_STATE_FOCUSED_MASK : 0); Drawable marker = item.getMarker(state); if (marker == null) { marker = defaultMarker; OverlayItem.setState(marker, state); } mapView.getProjection().toPixels(item.getPoint(), point); Overlay.drawAt(canvas, marker, point.x, point.y, shadow); } } } @OriginalApi public GeoPoint getCenter() { if (!internalList.isEmpty()) { return center; } else { return null; } } @OriginalApi public Item getFocus() { return focusedItem; } @OriginalApi public void setFocus(Item item) { pendingFocusChangeEvent = item != focusedItem; focusedItem = item; } @OriginalApi protected int getIndexToDraw(int drawingOrder) { return internalList.get(drawingOrder).sortedIndex; } @OriginalApi public Item getItem(int position) { synchronized (internalList) { if (internalList.size() > position) { return internalList.get(position); } else { return null; } } } @OriginalApi public int getLastFocusedIndex() { return mLastFocusedIndex; } @OriginalApi protected void setLastFocusedIndex(int lastFocusedIndex) { mLastFocusedIndex = lastFocusedIndex; } @OriginalApi public int getLatSpanE6() { return latSpanE6; } @OriginalApi public int getLonSpanE6() { return lonSpanE6; } @OriginalApi protected boolean hitTest(Item item, Drawable marker, int hitX, int hitY) { return marker.getBounds().contains(hitX, hitY); } @OriginalApi public Item nextFocus(boolean forwards) { if (forwards) { return mLastFocusedIndex >= internalList.size() - 1 ? null : internalList.get(mLastFocusedIndex + 1); } else { return mLastFocusedIndex <= 0 ? null : internalList.get(mLastFocusedIndex - 1); } } @OriginalApi @Override public boolean onKeyUp(int keyCode, KeyEvent event, MapView mapView) { Log.w(TAG, "Incomplete implementation of onKeyUp()"); return super.onKeyUp(keyCode, event, mapView); // TODO } @OriginalApi @Override public boolean onSnapToItem(int x, int y, Point snapPoint, MapView mapView) { Log.w(TAG, "Incomplete implementation of onSnapToItem()"); return false; // TODO } @OriginalApi @Override public boolean onTap(GeoPoint p, MapView mapView) { Point touchPoint = mapView.getProjection().toPixels(p, null); Point point = new Point(); List<Item> hitItems = new ArrayList<Item>(); for (int i = internalList.size() - 1; i >= 0; i--) { final Item item = getItem(i); final int state = (drawFocusedItem && (focusedItem == item) ? OverlayItem.ITEM_STATE_FOCUSED_MASK : 0); Drawable marker = item.getMarker(state); if (marker == null) { marker = defaultMarker; OverlayItem.setState(marker, state); } mapView.getProjection().toPixels(item.getPoint(), point); if (hitTest(item, marker, touchPoint.x - point.x, touchPoint.y - point.y)) { hitItems.add(item); } } if (!hitItems.isEmpty()) { Item item = hitItems.get(0); for (Item hitItem : hitItems) { if (hitItem.getPoint().getLatitude() > item.getPoint().getLatitude()) item = hitItem; } Log.d(TAG, "Hit " + hitItems.size() + " items, most likely " + item); boolean result = onTap(item.index); if (focusedItem != item) { focusedItem = item; mLastFocusedIndex = item.index; if (focusChangeListener != null) { focusChangeListener.onFocusChanged(this, item); } } return result; } else { focusedItem = null; return false; } } @OriginalApi protected boolean onTap(int index) { return false; } @OriginalApi @Override public boolean onTouchEvent(MotionEvent e, MapView mapView) { Log.w(TAG, "Incomplete implementation of onTouchEvent()"); return super.onTouchEvent(e, mapView); // TODO } @OriginalApi @Override public boolean onTrackballEvent(MotionEvent event, MapView mapView) { Log.w(TAG, "Incomplete implementation of onTrackballEvent()"); return super.onTrackballEvent(event, mapView); // TODO } @OriginalApi protected void populate() { synchronized (internalList) { int size = size(); internalList.clear(); internalList.ensureCapacity(size); int minLatE6 = 90000000, maxLatE6 = -90000000, minLonE6 = 180000000, maxLonE6 = -180000000; for (int i = 0; i < size; i++) { Item item = createItem(i); item.index = i; minLatE6 = Math.min(minLatE6, item.getPoint().getLatitudeE6()); maxLatE6 = Math.max(maxLatE6, item.getPoint().getLatitudeE6()); minLonE6 = Math.min(minLonE6, item.getPoint().getLongitudeE6()); minLonE6 = Math.min(minLonE6, item.getPoint().getLongitudeE6()); internalList.add(item); } List<Item> sortedList = new ArrayList<Item>(internalList); Collections.sort(sortedList, latitudeComparator); for (int i = 0; i < sortedList.size(); i++) { sortedList.get(i).sortedIndex = i; } latSpanE6 = maxLatE6 - minLatE6; lonSpanE6 = maxLonE6 - minLonE6; center = new GeoPoint(minLatE6 + latSpanE6 / 2, minLonE6 + lonSpanE6 / 2); } if (assignedMapView != null && assignedMapView.getHandler() != null) { assignedMapView.getHandler().post(new Runnable() { @Override public void run() { assignedMapView.invalidate(); } }); } } @OriginalApi public void setDrawFocusedItem(boolean drawFocusedItem) { this.drawFocusedItem = drawFocusedItem; } @OriginalApi public void setOnFocusChangeListener(OnFocusChangeListener l) { this.focusChangeListener = l; } @OriginalApi public abstract int size(); @OriginalApi public interface OnFocusChangeListener { @OriginalApi void onFocusChanged(ItemizedOverlay overlay, OverlayItem newFocus); } }