// 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.activities; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.SearchManager; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.media.MediaPlayer; import android.opengl.GLSurfaceView; import android.os.Bundle; import android.os.Handler; import android.os.PowerManager; import android.preference.PreferenceManager; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.WindowManager; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.ImageButton; import android.widget.TextView; import android.widget.Toast; import com.google.android.glass.touchpad.Gesture; import com.google.android.glass.touchpad.GestureDetector; import com.google.android.stardroid.R; import com.google.android.stardroid.StardroidApplication; import com.google.android.stardroid.activities.util.ActivityLightLevelChanger; import com.google.android.stardroid.activities.util.ActivityLightLevelChanger.NightModeable; import com.google.android.stardroid.activities.util.ActivityLightLevelManager; import com.google.android.stardroid.control.AstronomerModel; import com.google.android.stardroid.control.AstronomerModel.Pointing; import com.google.android.stardroid.control.ControllerGroup; import com.google.android.stardroid.control.MagneticDeclinationCalculatorSwitcher; import com.google.android.stardroid.kml.KmlManager; import com.google.android.stardroid.layers.LayerManager; import com.google.android.stardroid.renderer.RendererController; import com.google.android.stardroid.renderer.SkyRenderer; import com.google.android.stardroid.renderer.util.AbstractUpdateClosure; import com.google.android.stardroid.search.SearchResult; import com.google.android.stardroid.touch.DragRotateZoomGestureDetector; import com.google.android.stardroid.units.GeocentricCoordinates; import com.google.android.stardroid.units.Vector3; import com.google.android.stardroid.util.Analytics; import com.google.android.stardroid.util.MathUtil; import com.google.android.stardroid.util.MiscUtil; import com.google.android.stardroid.util.OsVersions; /** * The main map-rendering Activity. */ public class DynamicStarMapActivity // extends Activity // implements GestureDetector.BaseListener, // GestureDetector.FingerListener, // GestureDetector.ScrollListener, // GestureDetector.TwoFingerScrollListener { private static final int TIME_DISPLAY_DELAY_MILLIS = 1000; /** * Passed to the renderer to get per-frame updates from the model. * * @author John Taylor */ private static final class RendererModelUpdateClosure extends AbstractUpdateClosure { private RendererController rendererController; private AstronomerModel model; public RendererModelUpdateClosure(AstronomerModel model, RendererController rendererController) { this.model = model; this.rendererController = rendererController; } @Override public void run() { Pointing pointing = model.getPointing(); float directionX = pointing.getLineOfSightX(); float directionY = pointing.getLineOfSightY(); float directionZ = pointing.getLineOfSightZ(); float upX = pointing.getPerpendicularX(); float upY = pointing.getPerpendicularY(); float upZ = pointing.getPerpendicularZ(); rendererController.queueSetViewOrientation(directionX, directionY, directionZ, upX, upY, upZ); Vector3 acceleration = model.getPhoneAcceleration(); rendererController.queueTextAngle(MathUtil.atan2(-acceleration.x, -acceleration.y)); rendererController.queueViewerUpDirection(model.getZenith().copy()); float fieldOfView = model.getFieldOfView(); rendererController.queueFieldOfView(fieldOfView); } } private static final String AUTO_MODE_PREF_KEY = "auto_mode"; private static final String BUNDLE_TARGET_NAME = "target_name"; private static final String BUNDLE_NIGHT_MODE = "night_mode"; private static final String BUNDLE_X_TARGET = "bundle_x_target"; private static final String BUNDLE_Y_TARGET = "bundle_y_target"; private static final String BUNDLE_Z_TARGET = "bundle_z_target"; private static final String BUNDLE_SEARCH_MODE = "bundle_search"; private static final String SOUND_EFFECTS = "sound_effects"; private static final int DELAY_BETWEEN_ZOOM_REPEATS_MILLIS = 100; private static final float ROTATION_SPEED = 10; private static final String TAG = MiscUtil.getTag(DynamicStarMapActivity.class); // Preference that keeps track of whether or not the user accepted the ToS public static final String READ_TOS_PREF = "read_tos"; private ImageButton cancelSearchButton; private ControllerGroup controller; private GestureDetector gestureDetector; private AstronomerModel model; private RendererController rendererController; private boolean nightMode = false; private boolean searchMode = false; private GeocentricCoordinates searchTarget = GeocentricCoordinates.getInstance(0, 0); private SharedPreferences sharedPreferences; private GLSurfaceView skyView; private PowerManager.WakeLock wakeLock; private String searchTargetName; private LayerManager layerManager; // TODO(widdows): Figure out if we should break out the // time dialog and time player into separate activities. private View timePlayerUI; private DialogFactory dialogFactory; private MediaPlayer timeTravelNoise; private MediaPlayer timeTravelBackNoise; KmlManager kmlManager; private Handler handler = new Handler(); // A list of runnables to post on the handler when we resume. private List<Runnable> runnables = new ArrayList<Runnable>(); // We need to maintain references to these objects to keep them from // getting gc'd. @SuppressWarnings("unused") private MagneticDeclinationCalculatorSwitcher magneticSwitcher; private DragRotateZoomGestureDetector dragZoomRotateDetector; private Animation flashAnimation; private ActivityLightLevelManager activityLightLevelManager; private long sessionStartTime; @Override public void onCreate(Bundle icicle) { Log.d(TAG, "onCreate at " + System.currentTimeMillis()); super.onCreate(icicle); timeTravelNoise = MediaPlayer.create(this, R.raw.timetravel); timeTravelBackNoise = MediaPlayer.create(this, R.raw.timetravelback); flashAnimation = AnimationUtils.loadAnimation(this, R.anim.timetravelflash); dialogFactory = new DialogFactory(this); sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); //sharedPreferences.registerOnSharedPreferenceChangeListener(this); boolean eulaConfirmed = sharedPreferences.getBoolean(READ_TOS_PREF, false); if (!eulaConfirmed) { new AlertDialog.Builder(this) .setMessage("Do you agree to the Terms and Conditions located at:\nhttp://m.google.com/tospage") .setCancelable(false) .setPositiveButton("Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(DynamicStarMapActivity.this); Editor editor = sharedPreferences.edit(); editor.putBoolean(DynamicStarMapActivity.READ_TOS_PREF, true); editor.commit(); } }) .setNegativeButton("No", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { finish(); } }) .show(); } getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); model = StardroidApplication.getModel(); layerManager = StardroidApplication.getLayerManager(getAssets(), sharedPreferences, getResources(), this); initializeModelViewController(); // We want to reset to auto mode on every restart, as users seem to get // stuck in manual mode and can't find their way out. // TODO(johntaylor): this is a bit of an abuse of the prefs system, but // the button we use is wired into the preferences system. Should probably // change this to a use a different mechanism. sharedPreferences.edit().putBoolean(AUTO_MODE_PREF_KEY, true).commit(); setAutoMode(true); // Search related setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); kmlManager = new KmlManager(layerManager); ActivityLightLevelChanger activityLightLevelChanger = new ActivityLightLevelChanger(this, new NightModeable() { @Override public void setNightMode(boolean nightMode) { DynamicStarMapActivity.this.rendererController.queueNightVisionMode(nightMode); } }); activityLightLevelManager = new ActivityLightLevelManager(activityLightLevelChanger, sharedPreferences); final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); wakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, TAG); // Were we started as the result of a search? Intent intent = getIntent(); Log.d(TAG, "Intent received: " + intent); if (Intent.ACTION_SEARCH.equals(intent.getAction())) { Log.d(TAG, "Started as a result of a search"); doSearchWithIntent(intent); } gestureDetector = new GestureDetector(this); gestureDetector.setBaseListener(this); gestureDetector.setFingerListener(this); gestureDetector.setScrollListener(this); gestureDetector.setTwoFingerScrollListener(this); Log.d(TAG, "-onCreate at " + System.currentTimeMillis()); } @Override public void onBackPressed() { new AlertDialog.Builder(this) .setMessage("Are you sure you want to exit?") .setCancelable(false) .setPositiveButton("Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { finish(); } }) .setNegativeButton("No", null) .show(); } @Override public boolean onGenericMotionEvent(MotionEvent event) { if (gestureDetector != null) { return gestureDetector.onMotionEvent(event); } return false; } @Override public boolean onGesture(Gesture gesture) { // TODO Auto-generated method stub return false; } @Override public void onFingerCountChanged(int previousCound, int currentCount) { // TODO Auto-generated method stub } @Override public boolean onScroll(float displacement, float delta, float velocity) { /* if (distanceX < -1) { controller.zoomIn(); } else if (distanceX > 1) { controller.zoomOut(); } */ return true; } @Override public boolean onTwoFingerScroll(float displacement, float delta, float velocity) { // TODO Auto-generated method stub return false; } /* @Override public boolean onSingleTapUp(MotionEvent e) { openOptionsMenu(); return true; } */ @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); MenuInflater inflator = getMenuInflater(); inflator.inflate(R.menu.main, menu); return true; } @Override public void onDestroy() { Log.d(TAG, "DynamicStarMap onDestroy"); super.onDestroy(); } /* @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case (KeyEvent.KEYCODE_DPAD_LEFT): Log.d(TAG, "Key left"); controller.rotate(-10.0f); break; case (KeyEvent.KEYCODE_DPAD_RIGHT): Log.d(TAG, "Key right"); controller.rotate(10.0f); break; case (KeyEvent.KEYCODE_BACK): // If we're in search mode when the user presses 'back' the natural // thing is to back out of search. Log.d(TAG, "In search mode " + searchMode); if (searchMode) { cancelSearch(); break; } default: Log.d(TAG, "Key: " + event); return super.onKeyDown(keyCode, event); } return true; } */ @Override public boolean onOptionsItemSelected(MenuItem item) { super.onOptionsItemSelected(item); switch (item.getItemId()) { /* case R.id.menu_item_search: Log.d(TAG, "Search"); Analytics.getInstance(this).trackEvent(Analytics.USER_ACTION_CATEGORY, Analytics.MENU_ITEM, Analytics.SEARCH_REQUESTED_LABEL, 1); onSearchRequested(); break; */ case R.id.menu_item_settings: Log.d(TAG, "Settings"); Analytics.getInstance(this).trackEvent(Analytics.USER_ACTION_CATEGORY, Analytics.MENU_ITEM, Analytics.SETTINGS_OPENED_LABEL, 1); startActivity(new Intent(this, EditSettingsActivity.class)); break; case R.id.menu_item_help: Log.d(TAG, "Help"); Analytics.getInstance(this).trackEvent(Analytics.USER_ACTION_CATEGORY, Analytics.MENU_ITEM, Analytics.HELP_OPENED_LABEL, 1); showDialog(DialogFactory.DIALOG_ID_HELP); break; case R.id.menu_item_dim: Log.d(TAG, "Toggling nightmode"); nightMode = !nightMode; sharedPreferences.edit().putString(ActivityLightLevelManager.LIGHT_MODE_KEY, nightMode ? "NIGHT" : "DAY").commit(); Analytics.getInstance(this).trackEvent(Analytics.USER_ACTION_CATEGORY, Analytics.MENU_ITEM, Analytics.TOGGLED_NIGHT_MODE_LABEL, nightMode ? 1 : 0); break; /* case R.id.menu_item_time: Log.d(TAG, "Starting Time Dialog from menu"); Analytics.getInstance(this).trackEvent(Analytics.USER_ACTION_CATEGORY, Analytics.MENU_ITEM, Analytics.TIME_TRAVEL_OPENED_LABEL, 1); if (!timePlayerUI.isShown()) { Log.d(TAG, "Resetting time in time travel dialog."); controller.goTimeTravel(new Date()); } else { Log.d(TAG, "Resuming current time travel dialog."); } showDialog(DialogFactory.DIALOG_ID_TIME_TRAVEL); break; case R.id.menu_item_gallery: Log.d(TAG, "Loading gallery"); Analytics.getInstance(this).trackEvent(Analytics.USER_ACTION_CATEGORY, Analytics.MENU_ITEM, Analytics.GALLERY_OPENED_LABEL, 1); startActivity(new Intent(this, ImageGalleryActivity.class)); break; */ case R.id.menu_item_tos: Log.d(TAG, "Loading ToS"); Analytics.getInstance(this).trackEvent(Analytics.USER_ACTION_CATEGORY, Analytics.MENU_ITEM, Analytics.TOS_OPENED_LABEL, 1); showDialog(DialogFactory.DIALOG_EULA_NO_BUTTONS); break; default: Log.e(TAG, "Unwired-up menu item"); return false; } return true; } @Override public void onStart() { super.onStart(); Analytics.getInstance(this).trackPageView(Analytics.DYNAMIC_STARMAP_ACTIVITY); sessionStartTime = System.currentTimeMillis(); } private enum SessionBucketLength { LESS_THAN_TEN_SECS(10), TEN_SECS_TO_THIRTY_SECS(30), THIRTY_SECS_TO_ONE_MIN(60), ONE_MIN_TO_FIVE_MINS(300), MORE_THAN_FIVE_MINS(Integer.MAX_VALUE); private int seconds; private SessionBucketLength(int seconds) { this.seconds = seconds; } } private SessionBucketLength getSessionLengthBucket(int sessionLengthSeconds) { for (SessionBucketLength bucket : SessionBucketLength.values()) { if (sessionLengthSeconds < bucket.seconds) { return bucket; } } Log.e(TAG, "Programming error - should not get here"); return SessionBucketLength.MORE_THAN_FIVE_MINS; } @Override public void onStop() { super.onStart(); // Define a session as being the time between the main activity being in // the foreground and pushed back. Note that this will mean that sessions // do get interrupted by (e.g.) loading preference or help screens. int sessionLengthSeconds = (int) (( System.currentTimeMillis() - sessionStartTime) / 1000); SessionBucketLength bucket = getSessionLengthBucket(sessionLengthSeconds); Analytics.getInstance(this).trackEvent( Analytics.GENERAL_CATEGORY, Analytics.SESSION_LENGTH_BUCKET, bucket.toString(), sessionLengthSeconds); } @Override public void onResume() { Log.d(TAG, "onResume at " + System.currentTimeMillis()); super.onResume(); Log.i(TAG, "Resuming"); wakeLock.acquire(); Log.i(TAG, "Starting view"); skyView.onResume(); Log.i(TAG, "Starting controller"); controller.start(); activityLightLevelManager.onResume(); for (Runnable runnable : runnables) { handler.post(runnable); } Log.d(TAG, "-onResume at " + System.currentTimeMillis()); } public void setTimeTravelMode(Date newTime) { SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy.MM.dd G HH:mm:ss z"); Toast.makeText(this, String.format(getString(R.string.time_travel_start_message_alt), dateFormatter.format(newTime)), Toast.LENGTH_LONG).show(); if (sharedPreferences.getBoolean(SOUND_EFFECTS, true)) { try { timeTravelNoise.start(); } catch (IllegalStateException e) { Log.e(TAG, "Exception trying to play time travel sound", e); // It's not the end of the world - carry on. } } Log.d(TAG, "Showing TimePlayer UI."); timePlayerUI.setVisibility(View.VISIBLE); timePlayerUI.requestFocus(); flashTheScreen(); controller.goTimeTravel(newTime); } public void setNormalTimeModel() { if (sharedPreferences.getBoolean(SOUND_EFFECTS, true)) { try { timeTravelBackNoise.start(); } catch (IllegalStateException e) { Log.e(TAG, "Exception trying to play return time travel sound", e); // It's not the end of the world - carry on. } } flashTheScreen(); controller.useRealTime(); Toast.makeText(this, R.string.time_travel_close_message, Toast.LENGTH_SHORT).show(); Log.d(TAG, "Leaving Time Travel mode."); timePlayerUI.setVisibility(View.GONE); } private void flashTheScreen() { final View view = findViewById(R.id.view_mask); // We don't need to set it invisible again - the end of the // animation will see to that. // TODO(johntaylor): check if setting it to GONE will bring // performance benefits. view.setVisibility(View.VISIBLE); view.startAnimation(flashAnimation); } @Override public void onPause() { Log.d(TAG, "DynamicStarMap onPause"); super.onPause(); for (Runnable runnable : runnables) { handler.removeCallbacks(runnable); } activityLightLevelManager.onPause(); controller.stop(); skyView.onPause(); wakeLock.release(); // Debug.stopMethodTracing(); Log.d(TAG, "DynamicStarMap -onPause"); } @Override public boolean onTouchEvent(MotionEvent event) { return false; } /* @Override public boolean onTrackballEvent(MotionEvent event) { Log.d(TAG, "Trackball motion " + event); controller.rotate(event.getX() * ROTATION_SPEED); return true; } */ private void doSearchWithIntent(Intent searchIntent) { // If we're already in search mode, cancel it. if (searchMode) { cancelSearch(); } Log.d(TAG, "Performing Search"); final String queryString = searchIntent.getStringExtra(SearchManager.QUERY); searchMode = true; Log.d(TAG, "Query string " + queryString); List<SearchResult> results = layerManager.searchByObjectName(queryString); // Log the search, with value "1" for successful searches Analytics.getInstance(this).trackEvent( Analytics.USER_ACTION_CATEGORY, Analytics.SEARCH, "search:" + queryString, results.size() > 0 ? 1 : 0); if (results.size() == 0) { Log.d(TAG, "No results returned"); showDialog(DialogFactory.DIALOG_ID_NO_SEARCH_RESULTS); } else if (results.size() > 1) { Log.d(TAG, "Multiple results returned"); dialogFactory.showUserChooseResultDialog(results); } else { Log.d(TAG, "One result returned."); final SearchResult result = results.get(0); activateSearchTarget(result.coords, result.capitalizedName); } } private void initializeModelViewController() { Log.i(TAG, "Initializing Model, View and Controller @ " + System.currentTimeMillis()); setContentView(R.layout.skyrenderer); OsVersions.setSystemStatusBarVisible(findViewById(R.id.main_sky_view_root), false); skyView = (GLSurfaceView) findViewById(R.id.skyrenderer_view); // We don't want a depth buffer. skyView.setEGLConfigChooser(false); SkyRenderer renderer = new SkyRenderer(getResources()); skyView.setRenderer(renderer); rendererController = new RendererController(renderer, skyView); // The renderer will now call back every frame to get model updates. rendererController.addUpdateClosure( new RendererModelUpdateClosure(model, rendererController)); Log.i(TAG, "Setting layers @ " + System.currentTimeMillis()); layerManager.registerWithRenderer(rendererController); Log.i(TAG, "Set up controllers @ " + System.currentTimeMillis()); controller = ControllerGroup.createControllerGroup(this); controller.setModel(model); // wireUpScreenControls(); // TODO(johntaylor) move these? magneticSwitcher = new MagneticDeclinationCalculatorSwitcher(model, sharedPreferences); wireUpTimePlayer(); // TODO(widdows) move these? } private void setAutoMode(boolean auto) { controller.setAutoMode(auto); } /* private void wireUpScreenControls() { cancelSearchButton = (ImageButton) findViewById(R.id.cancel_search_button); // TODO(johntaylor): move to set this in the XML once we don't support 1.5 cancelSearchButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { cancelSearch(); } }); final ZoomControls zooms = (ZoomControls) findViewById(R.id.zoom_control); final WidgetFader zoomControlFader = new WidgetFader(new Fadeable() { @Override public void hide() { zooms.hide(); } @Override public void show() { zooms.show(); } }); zooms.setOnZoomInClickListener(new OnClickListener() { @Override public void onClick(View v) { controller.zoomIn(); zoomControlFader.keepActive(); } }); zooms.setOnZoomOutClickListener(new OnClickListener() { @Override public void onClick(View v) { controller.zoomOut(); zoomControlFader.keepActive(); } }); zooms.setZoomSpeed(DELAY_BETWEEN_ZOOM_REPEATS_MILLIS); zooms.hide(); final ButtonLayerView providerButtons = (ButtonLayerView) findViewById(R.id.layer_buttons_control); final WidgetFader layerControlFader = new WidgetFader(providerButtons, 2500); providerButtons.hide(); final int numChildren = providerButtons.getChildCount(); for (int i = 0; i < numChildren; ++i) { final ImageButton button = (ImageButton) providerButtons.getChildAt(i); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { layerControlFader.keepActive(); } }); } final ButtonLayerView manualButtonLayer = (ButtonLayerView) findViewById(R.id.layer_manual_auto_toggle); final WidgetFader manualControlFader = new WidgetFader(manualButtonLayer); manualButtonLayer.hide(); final ImageButton manualAuto = (ImageButton) findViewById(R.id.manual_auto_toggle); manualAuto.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { manualControlFader.keepActive(); } }); MapMover mapMover = new MapMover(model, controller, this, sharedPreferences); gestureDetector = new GestureDetector(new GestureInterpreter( new WidgetFader[] {manualControlFader, layerControlFader, zoomControlFader}, mapMover)); dragZoomRotateDetector = new DragRotateZoomGestureDetector(mapMover); } */ private void cancelSearch() { View searchControlBar = findViewById(R.id.search_control_bar); searchControlBar.setVisibility(View.INVISIBLE); rendererController.queueDisableSearchOverlay(); searchMode = false; } @Override protected Dialog onCreateDialog(int id) { return dialogFactory.onCreateDialog(id); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); Log.d(TAG, "New Intent received " + intent); if (Intent.ACTION_SEARCH.equals(intent.getAction())) { doSearchWithIntent(intent); } } @Override protected void onRestoreInstanceState(Bundle icicle) { Log.d(TAG, "DynamicStarMap onRestoreInstanceState"); super.onRestoreInstanceState(icicle); if (icicle == null) return; searchMode = icicle.getBoolean(BUNDLE_SEARCH_MODE); float x = icicle.getFloat(BUNDLE_X_TARGET); float y = icicle.getFloat(BUNDLE_Y_TARGET); float z = icicle.getFloat(BUNDLE_Z_TARGET); searchTarget = new GeocentricCoordinates(x, y, z); searchTargetName = icicle.getString(BUNDLE_TARGET_NAME); if (searchMode) { Log.d(TAG, "Searching for target " + searchTargetName + " at target=" + searchTarget); rendererController.queueEnableSearchOverlay(searchTarget, searchTargetName); cancelSearchButton.setVisibility(View.VISIBLE); } nightMode = icicle.getBoolean(BUNDLE_NIGHT_MODE, false); } @Override protected void onSaveInstanceState(Bundle icicle) { Log.d(TAG, "DynamicStarMap onSaveInstanceState"); icicle.putBoolean(BUNDLE_SEARCH_MODE, searchMode); icicle.putFloat(BUNDLE_X_TARGET, searchTarget.x); icicle.putFloat(BUNDLE_Y_TARGET, searchTarget.y); icicle.putFloat(BUNDLE_Z_TARGET, searchTarget.z); icicle.putString(BUNDLE_TARGET_NAME, searchTargetName); icicle.putBoolean(BUNDLE_NIGHT_MODE, nightMode); super.onSaveInstanceState(icicle); } void activateSearchTarget(GeocentricCoordinates target, final String searchTerm) { Log.d(TAG, "Item " + searchTerm + " selected"); // Store these for later. searchTarget = target; searchTargetName = searchTerm; Log.d(TAG, "Searching for target=" + target); rendererController.queueViewerUpDirection(model.getZenith().copy()); rendererController.queueEnableSearchOverlay(target.copy(), searchTerm); boolean autoMode = sharedPreferences.getBoolean(AUTO_MODE_PREF_KEY, true); if (!autoMode) { controller.teleport(target); } TextView searchPromptText = (TextView) findViewById(R.id.search_status_label); searchPromptText.setText( String.format("%s %s", getString(R.string.search_target_looking_message), searchTerm)); View searchControlBar = findViewById(R.id.search_control_bar); searchControlBar.setVisibility(View.VISIBLE); } /** * Creates and wire up all time player controls. */ private void wireUpTimePlayer() { Log.d(TAG, "Initializing TimePlayer UI."); timePlayerUI = findViewById(R.id.time_player_view); ImageButton timePlayerCancelButton = (ImageButton) findViewById(R.id.time_player_close); ImageButton timePlayerBackwardsButton = (ImageButton) findViewById( R.id.time_player_play_backwards); ImageButton timePlayerStopButton = (ImageButton) findViewById(R.id.time_player_play_stop); ImageButton timePlayerForwardsButton = (ImageButton) findViewById( R.id.time_player_play_forwards); final TextView timeTravelSpeedLabel = (TextView) findViewById(R.id.time_travel_speed_label); timePlayerCancelButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Log.d(TAG, "Heard time player close click."); setNormalTimeModel(); } }); timePlayerBackwardsButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Log.d(TAG, "Heard time player play backwards click."); controller.decelerateTimeTravel(); timeTravelSpeedLabel.setText(controller.getCurrentSpeedTag()); } }); timePlayerStopButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Log.d(TAG, "Heard time player play stop click."); controller.pauseTime(); timeTravelSpeedLabel.setText(controller.getCurrentSpeedTag()); } }); timePlayerForwardsButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Log.d(TAG, "Heard time player play forwards click."); controller.accelerateTimeTravel(); timeTravelSpeedLabel.setText(controller.getCurrentSpeedTag()); } }); Runnable displayUpdater = new Runnable() { private TextView timeTravelTimeReadout = (TextView) findViewById( R.id.time_travel_time_readout); private TextView timeTravelStatusLabel = (TextView) findViewById( R.id.time_travel_status_label); private TextView timeTravelSpeedLabel = (TextView) findViewById( R.id.time_travel_speed_label); private final SimpleDateFormat dateFormatter = new SimpleDateFormat( "yyyy.MM.dd G HH:mm:ss z"); private Date date = new Date(); @Override public void run() { long time = model.getTimeMillis(); date.setTime(time); timeTravelTimeReadout.setText(dateFormatter.format(date)); if (time > System.currentTimeMillis()) { timeTravelStatusLabel.setText(R.string.time_travel_label_future); } else { timeTravelStatusLabel.setText(R.string.time_travel_label_past); } timeTravelSpeedLabel.setText(controller.getCurrentSpeedTag()); handler.postDelayed(this, TIME_DISPLAY_DELAY_MILLIS); } }; runnables.add(displayUpdater); } public AstronomerModel getModel() { return model; } }