// Copyright 2010 Google Inc. // // 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.stardroid.control; import static com.google.android.stardroid.base.TimeConstants.MILLISECONDS_PER_DAY; import static com.google.android.stardroid.base.TimeConstants.SECONDS_PER_HOUR; import static com.google.android.stardroid.base.TimeConstants.SECONDS_PER_MINUTE; import static com.google.android.stardroid.base.TimeConstants.SECONDS_PER_10MINUTE; import static com.google.android.stardroid.base.TimeConstants.SECONDS_PER_SECOND; import static com.google.android.stardroid.base.TimeConstants.SECONDS_PER_DAY; import static com.google.android.stardroid.base.TimeConstants.SECONDS_PER_WEEK; import com.google.android.stardroid.R; import com.google.android.stardroid.util.MiscUtil; import android.util.Log; import java.util.Date; /** * Controls time as selected / created by the user in Time Travel mode. * Includes control for "playing" through time in both directions at different * speeds. * * @author Dominic Widdows * @author John Taylor */ public class TimeTravelClock implements Clock { /** * A data holder for the time stepping speeds. */ private static class Speed { /** The speed in seconds per second. */ public double rate; /** The id of the Speed's string label. */ public int labelTag; public Speed(double rate, int labelTag) { this.rate = rate; this.labelTag = labelTag; } } public static final long STOPPED = 0; private static final Speed[] SPEEDS = { new Speed(-SECONDS_PER_WEEK, R.string.time_travel_week_speed_back), new Speed(-SECONDS_PER_DAY, R.string.time_travel_day_speed_back), new Speed(-SECONDS_PER_HOUR, R.string.time_travel_hour_speed_back), new Speed(-SECONDS_PER_10MINUTE, R.string.time_travel_10minute_speed_back), new Speed(-SECONDS_PER_MINUTE, R.string.time_travel_minute_speed_back), new Speed(-SECONDS_PER_SECOND, R.string.time_travel_second_speed_back), new Speed(STOPPED, R.string.time_travel_stopped), new Speed(SECONDS_PER_SECOND, R.string.time_travel_second_speed), new Speed(SECONDS_PER_MINUTE, R.string.time_travel_minute_speed), new Speed(SECONDS_PER_10MINUTE, R.string.time_travel_10minute_speed), new Speed(SECONDS_PER_HOUR, R.string.time_travel_hour_speed), new Speed(SECONDS_PER_DAY, R.string.time_travel_day_speed), new Speed(SECONDS_PER_WEEK, R.string.time_travel_week_speed), }; private static final int STOPPED_INDEX = SPEEDS.length / 2; private int speedIndex = STOPPED_INDEX; private static final String TAG = MiscUtil.getTag(TimeTravelClock.class); private long timeLastSet; private long simulatedTime; /** * Sets the internal time. * @param date Date to which the timeTravelDate will be set. */ public synchronized void setTimeTravelDate(Date date) { pauseTime(); timeLastSet = System.currentTimeMillis(); simulatedTime = date.getTime(); } /* * Controller logic for playing through time at different directions and * speeds. */ /** * Increases the rate of time travel into the future * (or decreases the rate of time travel into the past.) */ public synchronized void accelerateTimeTravel() { if (speedIndex < SPEEDS.length - 1) { Log.d(TAG, "Accelerating speed to: " + SPEEDS[speedIndex]); ++speedIndex; } else { Log.d(TAG, "Already at max forward speed"); } } /** * Decreases the rate of time travel into the future * (or increases the rate of time travel into the past.) */ public synchronized void decelerateTimeTravel() { if (speedIndex > 0) { Log.d(TAG, "Decelerating speed to: " + SPEEDS[speedIndex]); --speedIndex; } else { Log.d(TAG, "Already at maximum backwards speed"); } } /** * Pauses time. */ public synchronized void pauseTime() { Log.d(TAG, "Pausing time"); assert SPEEDS[STOPPED_INDEX].rate == 0.0; speedIndex = STOPPED_INDEX; } /** * @return The current speed tag, a string describing the speed of time * travel. */ public int getCurrentSpeedTag() { return SPEEDS[speedIndex].labelTag; } @Override public long getTimeInMillisSinceEpoch() { long now = System.currentTimeMillis(); long elapsedTimeMillis = now - timeLastSet; double rate = SPEEDS[speedIndex].rate; long timeDelta = (long) (rate * elapsedTimeMillis); if (Math.abs(rate) >= SECONDS_PER_DAY) { // For speeds greater than or equal to 1 day/sec we want to move in // increments of 1 day so that the map isn't dizzyingly fast. // This shows the slow annual procession of the stars. long days = (long) (timeDelta / MILLISECONDS_PER_DAY); if (days == 0) { return simulatedTime; } // Note that this assumes that time requests will occur right on the // day boundary. If they occur later then the next time jump // might be a bit shorter than it should be. Nevertheless the refresh // rate of the renderer is high enough that this should be unnoticeable. timeDelta = (long) (days * MILLISECONDS_PER_DAY); } timeLastSet = now; simulatedTime += timeDelta; return simulatedTime; } }