/* * Copyright (C) 2016 Google Inc. All Rights Reserved. * * 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.apps.santatracker.map; import android.graphics.Point; import android.os.Handler; import com.google.android.apps.santatracker.data.SantaPreferences; import com.google.android.apps.santatracker.util.SantaLog; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.Projection; import com.google.android.gms.maps.model.BitmapDescriptorFactory; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.Marker; import com.google.android.gms.maps.model.MarkerOptions; class PresentMarker { private static final int ANIMATION_FRAMES_FADEOUT = 4; // per marker private static final int ANIMATION_FRAMES_MOVING_MAX = 275; private static final int ANIMATION_FRAMES_MOVING_MIN = 175; private static final int ANIMATION_FRAMES_WAIT = 500; private static final double MAXIMUM_ZOOM_LEVEL = 8.7f; static final String MARKER_TITLE = "PresentMarker"; private Marker[] mAnimationMarkers; private Marker mMovementMarker; private int mIndex = 0; private GoogleMap mMap; private SantaMarker mSantaMarker; private int mSizeX, mSizeY; private LatLng mDestination = null; private int mFrame = 0; private double mDirectionLat, mDirectionLng; private int mTotalAnimationLength; private LatLng mLocation; private int mAnimationDuration; private Projection mProjection; private boolean mWaitingForProjection = false; private LatLng mSantaPosition; private Handler mHandler; private static boolean VALID_CAMERA; PresentMarker(GoogleMap map, SantaMarker santa, Handler handler, int[] animIcons, int screenWidth, int screenHeight) { this.mMap = map; this.mSantaMarker = santa; this.mHandler = handler; // setup markers, one per icon mAnimationMarkers = new Marker[animIcons.length - 1]; LatLng position = new LatLng(0f, 0f); for (int i = 1; i < animIcons.length; i++) { mAnimationMarkers[i - 1] = mMap.addMarker(new MarkerOptions() .title(MARKER_TITLE) .icon(BitmapDescriptorFactory.fromResource(animIcons[i])) .position(position).visible(false)); mAnimationMarkers[i - 1].setVisible(false); } mMovementMarker = mMap.addMarker(new MarkerOptions() .title(MARKER_TITLE) .icon(BitmapDescriptorFactory.fromResource(animIcons[0])) .position(position).visible(false)); mMovementMarker.setVisible(false); mSizeX = screenWidth; mSizeY = screenHeight; // Wait before start mFrame = SantaPreferences.getRandom(-ANIMATION_FRAMES_WAIT, 0); reset(); } private void setProjection(Projection p, LatLng santaPosition) { this.mProjection = p; this.mSantaPosition = santaPosition; this.mWaitingForProjection = false; } static void setViewParameters(double zoom, boolean inSantaCam) { VALID_CAMERA = zoom > MAXIMUM_ZOOM_LEVEL || inSantaCam; } void draw() { // 5 States: waiting for valid camera for new present location, waiting // for start, // New present, moving, animating/disappearing if (!VALID_CAMERA && (mDestination == null && mProjection == null)) { } else if (mAnimationDuration < 0 || mWaitingForProjection) { // wait to start and until projection has been set // need to initialise the projection } else if (VALID_CAMERA && mDestination == null && mProjection == null) { // Log.d(TAG,"getting projection - zoom: "+ZOOM_LEVEL); mWaitingForProjection = true; mHandler.post(mGetProjectionRunnable); } else if (mDestination == null && mProjection != null) { // pick a new destination from screen coordinates int y = SantaPreferences.getRandom(0, mSizeY); int x = SantaPreferences.getRandom(0, mSizeX); mDestination = mProjection.fromScreenLocation(new Point(x, y)); if (mDestination == null) { SantaLog.d("SantaPresents", "Point = " + new Point(x, y)); } mAnimationDuration = SantaPreferences.getRandom( ANIMATION_FRAMES_MOVING_MIN, ANIMATION_FRAMES_MOVING_MAX); mTotalAnimationLength = mAnimationDuration + (ANIMATION_FRAMES_FADEOUT * mAnimationMarkers.length); // calculate speed mDirectionLat = (mDestination.latitude - mSantaPosition.latitude) / mAnimationDuration; mDirectionLng = (mDestination.longitude - mSantaPosition.longitude) / mAnimationDuration; mLocation = mSantaPosition; mHandler.post(mSetVisibleLocationRunnable); mFrame = 0; // Log.d(TAG, // "New present Marker position: "+mLocation+" movement: "+mDirectionLat+", "+mDirectionLng); mProjection = null; } else if (mFrame < mAnimationDuration) { // Moving animation mLocation = new LatLng(mLocation.latitude + mDirectionLat, mLocation.longitude + mDirectionLng); mHandler.post(mSetLocationRunnable); // animate out if frames left for all animation markers } else if (mFrame >= mAnimationDuration && mFrame <= mTotalAnimationLength) { if ((mFrame - mAnimationDuration) % ANIMATION_FRAMES_FADEOUT == 0) { // switch to the next marker mHandler.post(mSwapIconRunnable); } } else if (mFrame > mTotalAnimationLength) { // animation finished, reset and start again after wait mDestination = null; mFrame = SantaPreferences .getRandom(-ANIMATION_FRAMES_MOVING_MAX, 0); } // Wait if (!mWaitingForProjection) { mFrame++; } } /** * Hides the previous animation marker and marks the given marker visible. * If this is is the first marker, only it will be set visible. If this is * not a marker, nothing will be done. */ private void showAnimationMarker(int i) { if (i >= 0 && i < mAnimationMarkers.length) { mAnimationMarkers[i].setPosition(mLocation); mAnimationMarkers[i].setVisible(true); } // hide the previous marker if (i - 1 < 0) { mMovementMarker.setVisible(false); } else if (i - 1 < mAnimationMarkers.length) { mAnimationMarkers[i - 1].setVisible(false); } } public void reset() { mAnimationMarkers[mIndex].setVisible(false); mIndex = 0; } public void hide() { mAnimationMarkers[mIndex].setVisible(false); } private Runnable mGetProjectionRunnable = new Runnable() { public void run() { setProjection(mMap.getProjection(), mSantaMarker.getPosition()); } }; private Runnable mSwapIconRunnable = new Runnable() { public void run() { showAnimationMarker((mFrame - mAnimationDuration) / ANIMATION_FRAMES_FADEOUT); } }; private Runnable mSetVisibleLocationRunnable = new Runnable() { public void run() { mMovementMarker.setPosition(mLocation); mMovementMarker.setVisible(true); } }; private Runnable mSetLocationRunnable = new Runnable() { public void run() { mMovementMarker.setPosition(mLocation); } }; }