/**
* Copyright (C) 2013 Romain Guefveneu.
*
* This file is part of naonedbus.
*
* Naonedbus is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Naonedbus 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, see <http://www.gnu.org/licenses/>.
*/
package net.naonedbus.graphics.drawable;
import net.naonedbus.R;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.LightingColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.Drawable;
import com.google.android.maps.MapView;
/**
* A {@link Drawable} dedicated to {@link MapView}s. A {@link MapPinDrawable}
* displays a rounded pin with a dot in the middle. This class lets you easily
* change the color of the pin as well as the dot in the center of the pin.
*
* @author Cyril Mottier
*/
public class MapPinDrawable extends Drawable {
private static final int COLOR_MODE_UNKNOWN = -1;
private static final int COLOR_MODE_COLOR = 1;
private static final int COLOR_MODE_COLOR_STATE_LIST = 2;
private static final Paint sClearerPaint;
static {
sClearerPaint = new Paint();
sClearerPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
private int mColorMode = COLOR_MODE_UNKNOWN;
private int mPinColor;
private int mDotRes = R.drawable.gd_map_pin_dot;
private ColorStateList mPinColorStateList;
private ColorStateList mDotColorStateList;
private int mCurrentPinColor;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint mMapPinPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Bitmap mBase;
private Bitmap mPin;
private Bitmap mDot;
private Bitmap mMapPin;
private boolean mNeedRebuild;
private boolean mSelected = false;
/**
* Create a new {@link MapPinDrawable} that has a single color.
*
* @param res
* The application resources
* @param color
* The color of the pin/dot
*/
public MapPinDrawable(final Resources res, final int color) {
this(res, color, color);
}
/**
* Create a new {@link MapPinDrawable}.
*
* @param res
* The application resources
* @param pinClor
* The color of the pin
* @param dotColor
* The color of the dot
*/
public MapPinDrawable(final Resources res, final int pinColor, final int dotRes) {
mDotRes = dotRes;
initBitmaps(res);
setColors(res.getColor(pinColor), res.getColor(pinColor));
}
/**
* Create a new {@link MapPinDrawable} that may change color depending on
* its current state.
*
* @param res
* The application resources
* @param color
* A {@link ColorStateList} object giving a set of colors
* changing depending on the current {@link Drawable}'s state
*/
public MapPinDrawable(final Resources res, final ColorStateList color) {
this(res, color, color);
}
/**
* Create a new {@link MapPinDrawable} that may change color depending on
* its current state.
*
* @param res
* The application resources
* @param pinColor
* A {@link ColorStateList} object giving a set of colors for the
* pin changing depending on the current {@link Drawable} 's
* state
* @param dotColor
* A {@link ColorStateList} object giving a set of colors for the
* dot changing depending on the current {@link Drawable} 's
* state
*/
public MapPinDrawable(final Resources res, final ColorStateList pinColor, final ColorStateList dotColor) {
initBitmaps(res);
setColors(pinColor, dotColor);
}
/**
* Create a copy of a {@link MapPinDrawable}.
*
* @param mapPinDrawable
*/
public MapPinDrawable(final MapPinDrawable mapPinDrawable) {
mBase = mapPinDrawable.mBase;
mPin = mapPinDrawable.mPin;
mDot = mapPinDrawable.mDot;
mColorMode = COLOR_MODE_COLOR;
mPinColor = mCurrentPinColor = mapPinDrawable.mPinColor;
mNeedRebuild = true;
}
private void initBitmaps(final Resources res) {
// TODO Cyril: Share those Bitmaps between all instances of
// MapPinDrawable in order to save memory
mBase = BitmapFactory.decodeResource(res, R.drawable.gd_map_pin_base);
mPin = BitmapFactory.decodeResource(res, R.drawable.gd_map_pin_pin);
mDot = BitmapFactory.decodeResource(res, mDotRes);
}
/**
* Set the color for the pin/dot
*
* @param pinClor
* The color of the pin
* @param dotColor
* The color of the dot
*/
public void setColors(final int pinColor, final int dotColor) {
if (mColorMode != COLOR_MODE_COLOR || mPinColor != pinColor) {
mColorMode = COLOR_MODE_COLOR;
mPinColor = mCurrentPinColor = pinColor;
mNeedRebuild = true;
}
}
/**
* Set the color for the pin/dot
*
* @param pinClor
* The color of the pin
* @param dotColor
* The color of the dot
*/
public void setColors(final ColorStateList pinColor, final ColorStateList dotColor) {
if (mColorMode != COLOR_MODE_COLOR_STATE_LIST || mPinColorStateList != pinColor
|| mDotColorStateList != dotColor) {
mColorMode = COLOR_MODE_COLOR_STATE_LIST;
mPinColorStateList = pinColor;
mDotColorStateList = dotColor;
mNeedRebuild = true;
}
}
@Override
public boolean isStateful() {
return true;
}
@Override
public int getIntrinsicWidth() {
return (mBase != null) ? mBase.getWidth() : -1;
}
@Override
public int getIntrinsicHeight() {
return (mBase != null) ? mBase.getHeight() : -1;
}
@Override
protected boolean onStateChange(final int[] stateSet) {
if (mColorMode == COLOR_MODE_COLOR_STATE_LIST) {
final int pinColor = (mPinColorStateList != null) ? mPinColorStateList.getColorForState(stateSet, Color.BLACK)
: Color.BLACK;
if (mCurrentPinColor != pinColor) {
mCurrentPinColor = pinColor;
mNeedRebuild = true;
return true;
}
}
return false;
}
@Override
public void draw(final Canvas canvas) {
if (mNeedRebuild) {
if (mMapPin == null) {
mMapPin = Bitmap.createBitmap(mBase.getWidth(), mBase.getHeight(), Bitmap.Config.ARGB_8888);
}
final Canvas c = new Canvas(mMapPin);
c.drawRect(0, 0, mMapPin.getWidth(), mMapPin.getHeight(), sClearerPaint);
// 1 - Draw the base
c.drawBitmap(mBase, 0, 0, null);
// 2 - Draw the pin on top of it
mPaint.setColorFilter(new LightingColorFilter(Color.BLACK, mCurrentPinColor));
c.drawBitmap(mPin, 0, 0, mPaint);
// 3 - Draw the dot on top of everything
mPaint.setColorFilter(new LightingColorFilter(Color.WHITE, mCurrentPinColor));
c.drawBitmap(mDot, 0, 0, mPaint);
mNeedRebuild = false;
}
canvas.drawBitmap(mMapPin, null, getBounds(), mMapPinPaint);
}
@Override
public void setAlpha(final int alpha) {
mMapPinPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(final ColorFilter cf) {
mMapPinPaint.setColorFilter(cf);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
public boolean isSelected() {
return mSelected;
}
public void setSelected(final boolean selected) {
this.mSelected = selected;
}
}