└── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
├── androidTest
└── java
│ └── com
│ └── spartons
│ └── animatingmarkercurrentlocation
│ └── ExampleInstrumentedTest.java
├── main
├── AndroidManifest.xml
├── java
│ └── com
│ │ └── spartons
│ │ └── animatingmarkercurrentlocation
│ │ ├── LatLngInterpolator.java
│ │ ├── MainActivity.java
│ │ └── MarkerAnimation.java
└── res
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable
│ ├── current_location_vector_icon.xml
│ └── ic_launcher_background.xml
│ ├── layout
│ └── activity_main.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ └── values
│ ├── colors.xml
│ ├── google_map_api.xml
│ ├── strings.xml
│ └── styles.xml
└── test
└── java
└── com
└── spartons
└── animatingmarkercurrentlocation
└── ExampleUnitTest.java
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 27
5 | defaultConfig {
6 | applicationId "com.spartons.animatingmarkercurrentlocation"
7 | minSdkVersion 20
8 | targetSdkVersion 27
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | compileOptions {
20 | sourceCompatibility JavaVersion.VERSION_1_8
21 | targetCompatibility JavaVersion.VERSION_1_8
22 | }
23 | }
24 |
25 | dependencies {
26 | implementation fileTree(include: ['*.jar'], dir: 'libs')
27 | implementation 'com.android.support:appcompat-v7:27.1.1'
28 | implementation 'com.android.support.constraint:constraint-layout:1.1.0'
29 | implementation 'com.google.android.gms:play-services-location:15.0.1'
30 | implementation 'com.google.android.gms:play-services-maps:15.0.1'
31 | testImplementation 'junit:junit:4.12'
32 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
33 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
34 | }
35 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/spartons/animatingmarkercurrentlocation/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.spartons.animatingmarkercurrentlocation;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.spartons.animatingmarkercurrentlocation", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
27 |
28 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/spartons/animatingmarkercurrentlocation/LatLngInterpolator.java:
--------------------------------------------------------------------------------
1 | import com.google.android.gms.maps.model.LatLng;
2 |
3 | import static java.lang.Math.asin;
4 | import static java.lang.Math.atan2;
5 | import static java.lang.Math.cos;
6 | import static java.lang.Math.pow;
7 | import static java.lang.Math.sin;
8 | import static java.lang.Math.sqrt;
9 | import static java.lang.Math.toDegrees;
10 | import static java.lang.Math.toRadians;
11 |
12 | public interface LatLngInterpolator {
13 |
14 | LatLng interpolate(float fraction, LatLng a, LatLng b);
15 |
16 | class Spherical implements LatLngInterpolator {
17 |
18 | /* From github.com/googlemaps/android-maps-utils */
19 | @Override
20 | public LatLng interpolate(float fraction, LatLng from, LatLng to) {
21 | // http://en.wikipedia.org/wiki/Slerp
22 | double fromLat = toRadians(from.latitude);
23 | double fromLng = toRadians(from.longitude);
24 | double toLat = toRadians(to.latitude);
25 | double toLng = toRadians(to.longitude);
26 | double cosFromLat = cos(fromLat);
27 | double cosToLat = cos(toLat);
28 |
29 | // Computes Spherical interpolation coefficients.
30 | double angle = computeAngleBetween(fromLat, fromLng, toLat, toLng);
31 | double sinAngle = sin(angle);
32 | if (sinAngle < 1E-6) {
33 | return from;
34 | }
35 | double a = sin((1 - fraction) * angle) / sinAngle;
36 | double b = sin(fraction * angle) / sinAngle;
37 |
38 | // Converts from polar to vector and interpolate.
39 | double x = a * cosFromLat * cos(fromLng) + b * cosToLat * cos(toLng);
40 | double y = a * cosFromLat * sin(fromLng) + b * cosToLat * sin(toLng);
41 | double z = a * sin(fromLat) + b * sin(toLat);
42 |
43 | // Converts interpolated vector back to polar.
44 | double lat = atan2(z, sqrt(x * x + y * y));
45 | double lng = atan2(y, x);
46 | return new LatLng(toDegrees(lat), toDegrees(lng));
47 | }
48 |
49 | private double computeAngleBetween(double fromLat, double fromLng, double toLat, double toLng) {
50 | // Haversine's formula
51 | double dLat = fromLat - toLat;
52 | double dLng = fromLng - toLng;
53 | return 2 * asin(sqrt(pow(sin(dLat / 2), 2) +
54 | cos(fromLat) * cos(toLat) * pow(sin(dLng / 2), 2)));
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/java/com/spartons/animatingmarkercurrentlocation/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.spartons.animatingmarkercurrentlocation;
2 |
3 | import android.Manifest;
4 | import android.content.pm.PackageManager;
5 | import android.location.Location;
6 | import android.os.Build;
7 | import android.os.Looper;
8 | import android.support.annotation.NonNull;
9 | import android.support.v4.app.ActivityCompat;
10 | import android.support.v7.app.AppCompatActivity;
11 | import android.os.Bundle;
12 | import android.util.Log;
13 | import android.view.View;
14 | import android.widget.Toast;
15 |
16 | import com.google.android.gms.common.ConnectionResult;
17 | import com.google.android.gms.common.GoogleApiAvailability;
18 | import com.google.android.gms.location.FusedLocationProviderClient;
19 | import com.google.android.gms.location.LocationCallback;
20 | import com.google.android.gms.location.LocationRequest;
21 | import com.google.android.gms.location.LocationResult;
22 | import com.google.android.gms.location.LocationServices;
23 | import com.google.android.gms.maps.CameraUpdateFactory;
24 | import com.google.android.gms.maps.GoogleMap;
25 | import com.google.android.gms.maps.OnMapReadyCallback;
26 | import com.google.android.gms.maps.SupportMapFragment;
27 | import com.google.android.gms.maps.model.BitmapDescriptorFactory;
28 | import com.google.android.gms.maps.model.CameraPosition;
29 | import com.google.android.gms.maps.model.LatLng;
30 | import com.google.android.gms.maps.model.Marker;
31 | import com.google.android.gms.maps.model.MarkerOptions;
32 |
33 |
34 | public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {
35 |
36 | private static final int MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 5445;
37 |
38 | private GoogleMap googleMap;
39 | private FusedLocationProviderClient fusedLocationProviderClient;
40 | private Marker currentLocationMarker;
41 | private Location currentLocation;
42 | private boolean firstTimeFlag = true;
43 |
44 | private final View.OnClickListener clickListener = view -> {
45 | if (view.getId() == R.id.currentLocationImageButton && googleMap != null && currentLocation != null)
46 | animateCamera(currentLocation);
47 | };
48 |
49 | private final LocationCallback mLocationCallback = new LocationCallback() {
50 |
51 | @Override
52 | public void onLocationResult(LocationResult locationResult) {
53 | super.onLocationResult(locationResult);
54 | if (locationResult.getLastLocation() == null)
55 | return;
56 | currentLocation = locationResult.getLastLocation();
57 | if (firstTimeFlag && googleMap != null) {
58 | animateCamera(currentLocation);
59 | firstTimeFlag = false;
60 | }
61 | showMarker(currentLocation);
62 | }
63 | };
64 |
65 | @Override
66 | protected void onCreate(Bundle savedInstanceState) {
67 | super.onCreate(savedInstanceState);
68 | setContentView(R.layout.activity_main);
69 | SupportMapFragment supportMapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapFragment);
70 | supportMapFragment.getMapAsync(this);
71 | findViewById(R.id.currentLocationImageButton).setOnClickListener(clickListener);
72 | }
73 |
74 | @Override
75 | public void onMapReady(GoogleMap googleMap) {
76 | this.googleMap = googleMap;
77 | }
78 |
79 | private void startCurrentLocationUpdates() {
80 | LocationRequest locationRequest = LocationRequest.create();
81 | locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
82 | locationRequest.setInterval(3000);
83 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
84 | if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
85 | ActivityCompat.requestPermissions(MainActivity.this,
86 | new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
87 | MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
88 | return;
89 | }
90 | }
91 | fusedLocationProviderClient.requestLocationUpdates(locationRequest, mLocationCallback, Looper.myLooper());
92 | }
93 |
94 | private boolean isGooglePlayServicesAvailable() {
95 | GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance();
96 | int status = googleApiAvailability.isGooglePlayServicesAvailable(this);
97 | if (ConnectionResult.SUCCESS == status)
98 | return true;
99 | else {
100 | if (googleApiAvailability.isUserResolvableError(status))
101 | Toast.makeText(this, "Please Install google play services to use this application", Toast.LENGTH_LONG).show();
102 | }
103 | return false;
104 | }
105 |
106 | @Override
107 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
108 | super.onRequestPermissionsResult(requestCode, permissions, grantResults);
109 | if (requestCode == MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION) {
110 | if (grantResults[0] == PackageManager.PERMISSION_DENIED)
111 | Toast.makeText(this, "Permission denied by uses", Toast.LENGTH_SHORT).show();
112 | else if (grantResults[0] == PackageManager.PERMISSION_GRANTED)
113 | startCurrentLocationUpdates();
114 | }
115 | }
116 |
117 | private void animateCamera(@NonNull Location location) {
118 | LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
119 | googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(getCameraPositionWithBearing(latLng)));
120 | }
121 |
122 | @NonNull
123 | private CameraPosition getCameraPositionWithBearing(LatLng latLng) {
124 | return new CameraPosition.Builder().target(latLng).zoom(16).build();
125 | }
126 |
127 | private void showMarker(@NonNull Location currentLocation) {
128 | LatLng latLng = new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude());
129 | if (currentLocationMarker == null)
130 | currentLocationMarker = googleMap.addMarker(new MarkerOptions().icon(BitmapDescriptorFactory.defaultMarker()).position(latLng));
131 | else
132 | MarkerAnimation.animateMarkerToGB(currentLocationMarker, latLng, LatLngInterpolator.Spherical());
133 | }
134 |
135 | @Override
136 | protected void onStop() {
137 | super.onStop();
138 | if (fusedLocationProviderClient != null)
139 | fusedLocationProviderClient.removeLocationUpdates(mLocationCallback);
140 | }
141 |
142 | @Override
143 | protected void onResume() {
144 | super.onResume();
145 | if (isGooglePlayServicesAvailable()) {
146 | fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
147 | startCurrentLocationUpdates();
148 | }
149 | }
150 |
151 | @Override
152 | protected void onDestroy() {
153 | super.onDestroy();
154 | fusedLocationProviderClient = null;
155 | googleMap = null;
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/app/src/main/java/com/spartons/animatingmarkercurrentlocation/MarkerAnimation.java:
--------------------------------------------------------------------------------
1 | import android.os.Handler;
2 | import android.os.SystemClock;
3 | import android.view.animation.AccelerateDecelerateInterpolator;
4 | import android.view.animation.Interpolator;
5 |
6 | import com.google.android.gms.maps.model.LatLng;
7 | import com.google.android.gms.maps.model.Marker;
8 |
9 | public class MarkerAnimation {
10 |
11 | public static void animateMarkerToGB(final Marker marker, final LatLng finalPosition, final LatLngInterpolator latLngInterpolator) {
12 | final LatLng startPosition = marker.getPosition();
13 | final Handler handler = new Handler();
14 | final long start = SystemClock.uptimeMillis();
15 | final Interpolator interpolator = new AccelerateDecelerateInterpolator();
16 | final float durationInMs = 2000;
17 |
18 | handler.post(new Runnable() {
19 | long elapsed;
20 | float t;
21 | float v;
22 |
23 | @Override
24 | public void run() {
25 | // Calculate progress using interpolator
26 | elapsed = SystemClock.uptimeMillis() - start;
27 | t = elapsed / durationInMs;
28 | v = interpolator.getInterpolation(t);
29 |
30 | marker.setPosition(latLngInterpolator.interpolate(v, startPosition, finalPosition));
31 |
32 | // Repeat till progress is complete.
33 | if (t < 1) {
34 | // Post again 16ms later.
35 | handler.postDelayed(this, 16);
36 | }
37 | }
38 | });
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/current_location_vector_icon.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodingInfinite/animateMarkerWithCurrentLocation/4f92597150106abcf6b667c399b3b2cfc8a0af9b/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodingInfinite/animateMarkerWithCurrentLocation/4f92597150106abcf6b667c399b3b2cfc8a0af9b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodingInfinite/animateMarkerWithCurrentLocation/4f92597150106abcf6b667c399b3b2cfc8a0af9b/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodingInfinite/animateMarkerWithCurrentLocation/4f92597150106abcf6b667c399b3b2cfc8a0af9b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodingInfinite/animateMarkerWithCurrentLocation/4f92597150106abcf6b667c399b3b2cfc8a0af9b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodingInfinite/animateMarkerWithCurrentLocation/4f92597150106abcf6b667c399b3b2cfc8a0af9b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodingInfinite/animateMarkerWithCurrentLocation/4f92597150106abcf6b667c399b3b2cfc8a0af9b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodingInfinite/animateMarkerWithCurrentLocation/4f92597150106abcf6b667c399b3b2cfc8a0af9b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodingInfinite/animateMarkerWithCurrentLocation/4f92597150106abcf6b667c399b3b2cfc8a0af9b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodingInfinite/animateMarkerWithCurrentLocation/4f92597150106abcf6b667c399b3b2cfc8a0af9b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/google_map_api.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | AIzaSyD2gPrI2eAR*******1YAyYgWXxTeshoyI
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AnimatingMarkerCurrentLocation
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/spartons/animatingmarkercurrentlocation/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.spartons.animatingmarkercurrentlocation;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------