/*
* Copyright (C) 2016 Abhay Raj Singh Yadav
*
* 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.arsy.maps_library;
import android.animation.IntEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.os.Handler;
import android.view.animation.LinearInterpolator;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.GroundOverlay;
import com.google.android.gms.maps.model.GroundOverlayOptions;
import com.google.android.gms.maps.model.LatLng;
/**
* Created by abhay yadav on 09-Aug-16.
*/
public class MapRipple {
private GoogleMap googleMap;
private LatLng latLng, prevlatlng;
private Bitmap backgroundImage; //ripple image.
private float transparency = 0.5f; //transparency of image.
private volatile double distance = 2000; //distance to which ripple should be shown in metres
private int numberOfRipples = 1; //number of ripples to show, max = 4
private int fillColor = Color.TRANSPARENT; //fillcolor of circle
private int strokeColor = Color.BLACK; //border color of circle
private int strokewidth = 10; //border width of circle
private long durationBetweenTwoRipples = 4000; //in microseconds.
private long rippleDuration = 12000; //in microseconds
private ValueAnimator vAnimators[];
private Handler handlers[];
private GroundOverlay gOverlays[];
private GradientDrawable drawable;
private boolean isAnimationRunning = false;
public MapRipple(GoogleMap googleMap, LatLng latLng, Context context) {
this.googleMap = googleMap;
this.latLng = latLng;
this.prevlatlng = latLng;
drawable = (GradientDrawable) context.getResources().getDrawable(R.drawable.background);
vAnimators = new ValueAnimator[4];
handlers = new Handler[4];
gOverlays = new GroundOverlay[4];
}
public void withTransparency(float transparency) {
this.transparency = transparency;
}
public void withDistance(double distance) {
if (distance < 200)
distance = 200;
this.distance = distance;
}
public void withLatLng(LatLng latLng) {
prevlatlng = this.latLng;
this.latLng = latLng;
}
public void withNumberOfRipples(int numberOfRipples) {
if (numberOfRipples > 4 || numberOfRipples < 1)
numberOfRipples = 4;
this.numberOfRipples = numberOfRipples;
}
public void withFillColor(int fillColor) {
this.fillColor = fillColor;
}
public void withStrokeColor(int strokeColor) {
this.strokeColor = strokeColor;
}
public void withStrokewidth(int strokewidth) {
this.strokewidth = strokewidth;
}
public void withDurationBetweenTwoRipples(long durationBetweenTwoRipples) {
this.durationBetweenTwoRipples = durationBetweenTwoRipples;
}
public boolean isAnimationRunning() {
return isAnimationRunning;
}
public void withRippleDuration(long rippleDuration) {
this.rippleDuration = rippleDuration;
}
final Runnable runnable1 = new Runnable() {
@Override
public void run() {
gOverlays[0] = googleMap.addGroundOverlay(new
GroundOverlayOptions()
.position(latLng, (int) distance)
.transparency(transparency)
.image(BitmapDescriptorFactory.fromBitmap(backgroundImage)));
OverLay(0);
}
};
final Runnable runnable2 = new Runnable() {
@Override
public void run() {
gOverlays[1] = googleMap.addGroundOverlay(new
GroundOverlayOptions()
.position(latLng, (int) distance)
.transparency(transparency)
.image(BitmapDescriptorFactory.fromBitmap(backgroundImage)));
OverLay(1);
}
};
final Runnable runnable3 = new Runnable() {
@Override
public void run() {
gOverlays[2] = googleMap.addGroundOverlay(new
GroundOverlayOptions()
.position(latLng, (int) distance)
.transparency(transparency)
.image(BitmapDescriptorFactory.fromBitmap(backgroundImage)));
OverLay(2);
}
};
final Runnable runnable4 = new Runnable() {
@Override
public void run() {
gOverlays[3] = googleMap.addGroundOverlay(new
GroundOverlayOptions()
.position(latLng, (int) distance)
.transparency(transparency)
.image(BitmapDescriptorFactory.fromBitmap(backgroundImage)));
OverLay(3);
}
};
private void OverLay(final int i) {
vAnimators[i] = ValueAnimator.ofInt(0, (int) distance);
vAnimators[i].setRepeatCount(ValueAnimator.INFINITE);
vAnimators[i].setRepeatMode(ValueAnimator.RESTART);
vAnimators[i].setDuration(rippleDuration);
vAnimators[i].setEvaluator(new IntEvaluator());
vAnimators[i].setInterpolator(new LinearInterpolator());
vAnimators[i].addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
final Integer val = (Integer) valueAnimator.getAnimatedValue();
gOverlays[i].setDimensions(val);
if (distance - val <= 10) {
if (latLng != prevlatlng) {
gOverlays[i].setPosition(latLng);
}
}
}
});
vAnimators[i].start();
}
private void setDrawableAndBitmap() {
drawable.setColor(fillColor);
float d = Resources.getSystem().getDisplayMetrics().density;
int width = (int) (strokewidth * d); // margin in pixels
drawable.setStroke(width, strokeColor);
backgroundImage = drawableToBitmap(drawable);
}
private Bitmap drawableToBitmap(Drawable drawable) {
Bitmap bitmap = null;
if (drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
if (bitmapDrawable.getBitmap() != null) {
return bitmapDrawable.getBitmap();
}
}
if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); // Single color bitmap will be created of 1x1 pixel
} else {
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
public void stopRippleMapAnimation() {
if (isAnimationRunning) {
try {
for (int i = 0; i < numberOfRipples; i++) {
if (i == 0) {
handlers[i].removeCallbacks(runnable1);
vAnimators[i].cancel();
gOverlays[i].remove();
}
if (i == 1) {
handlers[i].removeCallbacks(runnable2);
vAnimators[i].cancel();
gOverlays[i].remove();
}
if (i == 2) {
handlers[i].removeCallbacks(runnable3);
vAnimators[i].cancel();
gOverlays[i].remove();
}
if (i == 3) {
handlers[i].removeCallbacks(runnable4);
vAnimators[i].cancel();
gOverlays[i].remove();
}
}
} catch (Exception e) {
}
}
isAnimationRunning = false;
}
public void startRippleMapAnimation() {
if (!isAnimationRunning) {
setDrawableAndBitmap();
for (int i = 0; i < numberOfRipples; i++) {
if (i == 0) {
handlers[i] = new Handler();
handlers[i].postDelayed(runnable1, durationBetweenTwoRipples * i);
}
if (i == 1) {
handlers[i] = new Handler();
handlers[i].postDelayed(runnable2, durationBetweenTwoRipples * i);
}
if (i == 2) {
handlers[i] = new Handler();
handlers[i].postDelayed(runnable3, durationBetweenTwoRipples * i);
}
if (i == 3) {
handlers[i] = new Handler();
handlers[i].postDelayed(runnable4, durationBetweenTwoRipples * i);
}
}
}
isAnimationRunning = true;
}
}