// 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 android.app.FragmentManager;
import android.app.SearchManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.media.MediaPlayer;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.util.Log;
import android.view.GestureDetector;
import android.view.KeyEvent;
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.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.stardroid.ApplicationConstants;
import com.google.android.stardroid.R;
import com.google.android.stardroid.activities.dialogs.EulaDialogFragment;
import com.google.android.stardroid.activities.dialogs.HelpDialogFragment;
import com.google.android.stardroid.activities.dialogs.MultipleSearchResultsDialogFragment;
import com.google.android.stardroid.activities.dialogs.NoSearchResultsDialogFragment;
import com.google.android.stardroid.activities.dialogs.NoSensorsDialogFragment;
import com.google.android.stardroid.activities.dialogs.TimeTravelDialogFragment;
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.activities.util.FullscreenControlsManager;
import com.google.android.stardroid.activities.util.GooglePlayServicesChecker;
import com.google.android.stardroid.base.Lists;
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.inject.HasComponent;
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.touch.GestureInterpreter;
import com.google.android.stardroid.touch.MapMover;
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.SensorAccuracyMonitor;
import com.google.android.stardroid.views.ButtonLayerView;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
/**
* The main map-rendering Activity.
*/
public class DynamicStarMapActivity extends InjectableActivity
implements OnSharedPreferenceChangeListener, HasComponent<DynamicStarMapComponent> {
private static final int TIME_DISPLAY_DELAY_MILLIS = 1000;
private FullscreenControlsManager fullscreenControlsManager;
@Override
public DynamicStarMapComponent getComponent() {
return daggerComponent;
}
/**
* 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;
private boolean horizontalRotation;
public RendererModelUpdateClosure(AstronomerModel model,
RendererController rendererController, SharedPreferences sharedPreferences) {
this.model = model;
this.rendererController = rendererController;
this.horizontalRotation = sharedPreferences.getBoolean(ApplicationConstants.ROTATE_HORIZON_PREFKEY, false);
model.setHorizontalRotation(this.horizontalRotation);
}
@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 up = model.getPhoneUpDirection();
rendererController.queueTextAngle(MathUtil.atan2(up.x, up.y));
rendererController.queueViewerUpDirection(model.getZenith().copy());
float fieldOfView = model.getFieldOfView();
rendererController.queueFieldOfView(fieldOfView);
}
}
// Activity for result Ids
public static final int GOOGLE_PLAY_SERVICES_REQUEST_CODE = 1;
public static final int GOOGLE_PLAY_SERVICES_REQUEST_LOCATION_PERMISSION_CODE = 2;
// End Activity for result Ids
private static final float ROTATION_SPEED = 10;
private static final String TAG = MiscUtil.getTag(DynamicStarMapActivity.class);
private ImageButton cancelSearchButton;
@Inject ControllerGroup controller;
private GestureDetector gestureDetector;
@Inject AstronomerModel model;
private RendererController rendererController;
private boolean nightMode = false;
private boolean searchMode = false;
private GeocentricCoordinates searchTarget = GeocentricCoordinates.getInstance(0, 0);
@Inject SharedPreferences sharedPreferences;
private GLSurfaceView skyView;
private PowerManager.WakeLock wakeLock;
private String searchTargetName;
@Inject LayerManager layerManager;
// TODO(widdows): Figure out if we should break out the
// time dialog and time player into separate activities.
private View timePlayerUI;
private DynamicStarMapComponent daggerComponent;
@Inject @Named("timetravel") Provider<MediaPlayer> timeTravelNoiseProvider;
@Inject @Named("timetravelback") Provider<MediaPlayer> timeTravelBackNoiseProvider;
private MediaPlayer timeTravelNoise;
private MediaPlayer timeTravelBackNoise;
@Inject Handler handler;
@Inject Analytics analytics;
@Inject GooglePlayServicesChecker playServicesChecker;
@Inject FragmentManager fragmentManager;
@Inject EulaDialogFragment eulaDialogFragmentNoButtons;
@Inject TimeTravelDialogFragment timeTravelDialogFragment;
@Inject HelpDialogFragment helpDialogFragment;
@Inject NoSearchResultsDialogFragment noSearchResultsDialogFragment;
@Inject MultipleSearchResultsDialogFragment multipleSearchResultsDialogFragment;
@Inject NoSensorsDialogFragment noSensorsDialogFragment;
@Inject SensorAccuracyMonitor sensorAccuracyMonitor;
// A list of runnables to post on the handler when we resume.
private List<Runnable> onResumeRunnables = new ArrayList<>();
// We need to maintain references to these objects to keep them from
// getting gc'd.
@SuppressWarnings("unused")
@Inject MagneticDeclinationCalculatorSwitcher magneticSwitcher;
private DragRotateZoomGestureDetector dragZoomRotateDetector;
@Inject Animation flashAnimation;
private ActivityLightLevelManager activityLightLevelManager;
private long sessionStartTime;
@Override
public void onCreate(Bundle icicle) {
Log.d(TAG, "onCreate at " + System.currentTimeMillis());
super.onCreate(icicle);
daggerComponent = DaggerDynamicStarMapComponent.builder()
.applicationComponent(getApplicationComponent())
.dynamicStarMapModule(new DynamicStarMapModule(this)).build();
daggerComponent.inject(this);
sharedPreferences.registerOnSharedPreferenceChangeListener(this);
// Set up full screen mode, hide the system UI etc.
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
WindowManager.LayoutParams.FLAG_FULLSCREEN);
// TODO(jontayler): upgrade to
// getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
// when we reach API level 16.
// http://developer.android.com/training/system-ui/immersive.html for the right way
// to do it at API level 19.
//getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
// Eventually we should check at the point of use, but this will do for now. If the
// user revokes the permission later then odd things may happen.
playServicesChecker.maybeCheckForGooglePlayServices();
initializeModelViewController();
checkForSensorsAndMaybeWarn();
// Search related
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
ActivityLightLevelChanger activityLightLevelChanger = new ActivityLightLevelChanger(this,
new NightModeable() {
@Override
public void setNightMode(boolean nightMode1) {
DynamicStarMapActivity.this.rendererController.queueNightVisionMode(nightMode1);
}});
activityLightLevelManager = new ActivityLightLevelManager(activityLightLevelChanger,
sharedPreferences);
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);
}
Log.d(TAG, "-onCreate at " + System.currentTimeMillis());
}
private void checkForSensorsAndMaybeWarn() {
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
if (sensorManager != null && sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null
&& sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null) {
Log.i(TAG, "Minimum sensors present");
// 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(ApplicationConstants.AUTO_MODE_PREF_KEY, true).apply();
setAutoMode(true);
return;
}
// Missing at least one sensor. Warn the user.
handler.post(new Runnable() {
@Override
public void run() {
if (!sharedPreferences
.getBoolean(ApplicationConstants.NO_WARN_ABOUT_MISSING_SENSORS, false)) {
Log.d(TAG, "showing no sensor dialog");
noSensorsDialogFragment.show(fragmentManager, "No sensors dialog");
// First time, force manual mode.
sharedPreferences.edit().putBoolean(ApplicationConstants.AUTO_MODE_PREF_KEY, false)
.apply();
setAutoMode(false);
} else {
Log.d(TAG, "showing no sensor toast");
Toast.makeText(
DynamicStarMapActivity.this, R.string.no_sensor_warning, Toast.LENGTH_LONG).show();
// Don't force manual mode second time through - leave it up to the user.
}
}
});
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Trigger the initial hide() shortly after the activity has been
// created, to briefly hint to the user that UI controls
// are available.
if (fullscreenControlsManager != null) {
fullscreenControlsManager.flashTheControls();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.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);
fullscreenControlsManager.delayHideTheControls();
switch (item.getItemId()) {
case R.id.menu_item_search:
Log.d(TAG, "Search");
analytics.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.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.trackEvent(Analytics.USER_ACTION_CATEGORY,
Analytics.MENU_ITEM, Analytics.HELP_OPENED_LABEL, 1);
helpDialogFragment.show(fragmentManager, "Help Dialog");
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.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.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.");
}
timeTravelDialogFragment.show(fragmentManager, "Time Travel");
break;
case R.id.menu_item_gallery:
Log.d(TAG, "Loading gallery");
analytics.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.trackEvent(Analytics.USER_ACTION_CATEGORY,
Analytics.MENU_ITEM, Analytics.TOS_OPENED_LABEL, 1);
eulaDialogFragmentNoButtons.show(fragmentManager, "Eula Dialog No Buttons");
break;
case R.id.menu_item_calibrate:
Log.d(TAG, "Loading Calibration");
analytics.trackEvent(Analytics.USER_ACTION_CATEGORY,
Analytics.MENU_ITEM, Analytics.CALIBRATION_OPENED_LABEL, 1);
Intent intent = new Intent(this, CompassCalibrationActivity.class);
intent.putExtra(CompassCalibrationActivity.HIDE_CHECKBOX, true);
startActivity(intent);
break;
case R.id.menu_item_diagnostics:
Log.d(TAG, "Loading Diagnostics");
analytics.trackEvent(Analytics.USER_ACTION_CATEGORY,
Analytics.MENU_ITEM, Analytics.DIAGNOSTICS_OPENED_LABEL, 1);
startActivity(new Intent(this, DiagnosticActivity.class));
break;
default:
Log.e(TAG, "Unwired-up menu item");
return false;
}
return true;
}
@Override
public void onStart() {
super.onStart();
analytics.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.onStop();
// 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.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");
timeTravelNoise = timeTravelNoiseProvider.get();
timeTravelBackNoise = timeTravelBackNoiseProvider.get();
wakeLock.acquire();
Log.i(TAG, "Starting view");
skyView.onResume();
Log.i(TAG, "Starting controller");
controller.start();
activityLightLevelManager.onResume();
if (controller.isAutoMode()) {
sensorAccuracyMonitor.start();
}
for (Runnable runnable : onResumeRunnables) {
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(ApplicationConstants.SOUND_EFFECTS, true)) {
try {
timeTravelNoise.start();
} catch (IllegalStateException | NullPointerException 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(ApplicationConstants.SOUND_EFFECTS, true)) {
try {
timeTravelBackNoise.start();
} catch (IllegalStateException | NullPointerException 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();
sensorAccuracyMonitor.stop();
if (timeTravelNoise != null) {
timeTravelNoise.release();
timeTravelNoise = null;
}
if (timeTravelBackNoise != null) {
timeTravelBackNoise.release();
timeTravelBackNoise = null;
}
for (Runnable runnable : onResumeRunnables) {
handler.removeCallbacks(runnable);
}
activityLightLevelManager.onPause();
controller.stop();
skyView.onPause();
wakeLock.release();
// Debug.stopMethodTracing();
Log.d(TAG, "DynamicStarMap -onPause");
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Log.d(TAG, "Preferences changed: key=" + key);
switch (key) {
case ApplicationConstants.AUTO_MODE_PREF_KEY:
boolean autoMode = sharedPreferences.getBoolean(key, true);
Log.d(TAG, "Automode is set to " + autoMode);
if (!autoMode) {
Log.d(TAG, "Switching to manual control");
Toast.makeText(DynamicStarMapActivity.this, R.string.set_manual, Toast.LENGTH_SHORT).show();
} else {
Log.d(TAG, "Switching to sensor control");
Toast.makeText(DynamicStarMapActivity.this, R.string.set_auto, Toast.LENGTH_SHORT).show();
}
setAutoMode(autoMode);
break;
case ApplicationConstants.ROTATE_HORIZON_PREFKEY:
model.setHorizontalRotation(sharedPreferences.getBoolean(key, false));
default:
return;
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// Log.d(TAG, "Touch event " + event);
// Either of the following detectors can absorb the event, but one
// must not hide it from the other
boolean eventAbsorbed = false;
if (gestureDetector.onTouchEvent(event)) {
eventAbsorbed = true;
}
if (dragZoomRotateDetector.onTouchEvent(event)) {
eventAbsorbed = true;
}
return eventAbsorbed;
}
@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.trackEvent(
Analytics.USER_ACTION_CATEGORY, Analytics.SEARCH, "search:" + queryString,
results.size() > 0 ? 1 : 0);
if (results.size() == 0) {
Log.d(TAG, "No results returned");
noSearchResultsDialogFragment.show(fragmentManager, "No Search Results");
} else if (results.size() > 1) {
Log.d(TAG, "Multiple results returned");
showUserChooseResultDialog(results);
} else {
Log.d(TAG, "One result returned.");
final SearchResult result = results.get(0);
activateSearchTarget(result.coords, result.capitalizedName);
}
}
private void showUserChooseResultDialog(List<SearchResult> results) {
multipleSearchResultsDialogFragment.clearResults();
for (SearchResult result : results) {
multipleSearchResultsDialogFragment.add(result);
}
multipleSearchResultsDialogFragment.show(fragmentManager, "Multiple Search Results");
}
private void initializeModelViewController() {
Log.i(TAG, "Initializing Model, View and Controller @ " + System.currentTimeMillis());
setContentView(R.layout.skyrenderer);
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, sharedPreferences));
Log.i(TAG, "Setting layers @ " + System.currentTimeMillis());
layerManager.registerWithRenderer(rendererController);
Log.i(TAG, "Set up controllers @ " + System.currentTimeMillis());
controller.setModel(model);
wireUpScreenControls(); // TODO(johntaylor) move these?
wireUpTimePlayer(); // TODO(widdows) move these?
}
private void setAutoMode(boolean auto) {
analytics.trackEvent(Analytics.USER_ACTION_CATEGORY,
Analytics.MENU_ITEM, Analytics.TOGGLED_MANUAL_MODE_LABEL, auto ? 0 : 1);
controller.setAutoMode(auto);
if (auto) {
sensorAccuracyMonitor.start();
} else {
sensorAccuracyMonitor.stop();
}
}
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();
}
});
ButtonLayerView providerButtons = (ButtonLayerView) findViewById(R.id.layer_buttons_control);
int numChildren = providerButtons.getChildCount();
List<View> buttonViews = new ArrayList<>();
for (int i = 0; i < numChildren; ++i) {
ImageButton button = (ImageButton) providerButtons.getChildAt(i);
buttonViews.add(button);
}
buttonViews.add(findViewById(R.id.manual_auto_toggle));
ButtonLayerView manualButtonLayer = (ButtonLayerView) findViewById(
R.id.layer_manual_auto_toggle);
fullscreenControlsManager = new FullscreenControlsManager(
this,
findViewById(R.id.main_sky_view),
Lists.<View>asList(manualButtonLayer, providerButtons),
buttonViews);
MapMover mapMover = new MapMover(model, controller, this);
gestureDetector = new GestureDetector(this, new GestureInterpreter(
fullscreenControlsManager, 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 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(ApplicationConstants.BUNDLE_SEARCH_MODE);
float x = icicle.getFloat(ApplicationConstants.BUNDLE_X_TARGET);
float y = icicle.getFloat(ApplicationConstants.BUNDLE_Y_TARGET);
float z = icicle.getFloat(ApplicationConstants.BUNDLE_Z_TARGET);
searchTarget = new GeocentricCoordinates(x, y, z);
searchTargetName = icicle.getString(ApplicationConstants.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(ApplicationConstants.BUNDLE_NIGHT_MODE, false);
}
@Override
protected void onSaveInstanceState(Bundle icicle) {
Log.d(TAG, "DynamicStarMap onSaveInstanceState");
icicle.putBoolean(ApplicationConstants.BUNDLE_SEARCH_MODE, searchMode);
icicle.putFloat(ApplicationConstants.BUNDLE_X_TARGET, searchTarget.x);
icicle.putFloat(ApplicationConstants.BUNDLE_Y_TARGET, searchTarget.y);
icicle.putFloat(ApplicationConstants.BUNDLE_Z_TARGET, searchTarget.z);
icicle.putString(ApplicationConstants.BUNDLE_TARGET_NAME, searchTargetName);
icicle.putBoolean(ApplicationConstants.BUNDLE_NIGHT_MODE, nightMode);
super.onSaveInstanceState(icicle);
}
public 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(ApplicationConstants.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);
}
};
onResumeRunnables.add(displayUpdater);
}
public AstronomerModel getModel() {
return model;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == GOOGLE_PLAY_SERVICES_REQUEST_CODE) {
playServicesChecker.runAfterDialog();
return;
}
Log.w(TAG, "Unhandled activity result");
}
@Override
public void onRequestPermissionsResult(int requestCode,
String[] permissions,
int[] grantResults) {
if (requestCode == GOOGLE_PLAY_SERVICES_REQUEST_LOCATION_PERMISSION_CODE) {
playServicesChecker.runAfterPermissionsCheck(requestCode, permissions, grantResults);
return;
}
Log.w(TAG, "Unhandled request permissions result");
}
}