/*******************************************************************************
* Gaggle is Copyright 2010 by Geeksville Industries LLC, a California limited liability corporation.
*
* Gaggle is distributed under a dual license. We've chosen this approach because within Gaggle we've used a number
* of components that Geeksville Industries LLC might reuse for commercial products. Gaggle can be distributed under
* either of the two licenses listed below.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Commercial Distribution License
* If you would like to distribute Gaggle (or portions thereof) under a license other than
* the "GNU General Public License, version 2", contact Geeksville Industries. Geeksville Industries reserves
* the right to release Gaggle source code under a commercial license of its choice.
*
* GNU Public License, version 2
* All other distribution of Gaggle must conform to the terms of the GNU Public License, version 2. The full
* text of this license is included in the Gaggle source, see assets/manual/gpl-2.0.txt.
******************************************************************************/
package com.geeksville.gaggle;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import android.app.ListActivity;
import android.content.Intent;
import android.location.Location;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.flurry.android.FlurryAgent;
import com.geeksville.android.AndroidUtil;
import com.geeksville.android.ChangeHandler;
import com.geeksville.android.LifeCycleHandler;
import com.geeksville.android.LifeCyclePublisher;
import com.geeksville.android.LifeCyclePublisherImpl;
import com.geeksville.info.InfoListView;
import com.geeksville.info.SelectInfoFieldsActivity;
import com.geeksville.location.BarometerClient;
import com.geeksville.location.GPSClient;
import com.geeksville.location.GPSToPositionWriter;
import com.geeksville.location.LeonardoLiveWriter;
import com.geeksville.location.SkyLinesTrackingWriter;
import com.geeksville.location.LocationDBWriter;
import com.geeksville.location.LocationList;
import com.geeksville.location.LocationListWriter;
import com.geeksville.location.PositionWriter;
import com.geeksville.location.PositionWriterSet;
import com.geeksville.view.AsyncProgressDialog;
public class LoggingControl extends ListActivity implements LifeCyclePublisher,
ChangeHandler {
/**
* Debugging tag
*/
private static final String TAG = "LoggingControl";
private LifeCyclePublisherImpl lifePublish = new LifeCyclePublisherImpl();
private Button loggingButton;
private TextView loggingLabel;
private InfoListView infoView;
/**
* my preferences DB
*/
private GagglePrefs prefs;
// Need handler for callbacks to the UI thread
private final Handler handler = new Handler();
static final int INFOSELECT_REQUEST = 0;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.flight_main);
// Log.d(TAG, "onCreate() called");
prefs = new GagglePrefs(this);
// Attach to our views
infoView = (InfoListView) this.getListView();
loggingButton = (Button) findViewById(R.id.LoggingOnOffButton);
loggingButton.setOnClickListener(loggingToggle);
loggingLabel = (TextView) findViewById(R.id.LabelLiveFlight);
validateAccounts();
restoreInfoFields();
}
/**
* Collect app metrics on Flurry
*
* @see android.app.Activity#onStart()
*/
@Override
protected void onStart() {
// Log.d(TAG, "onStart() called");
super.onStart();
FlurryAgent.onStartSession(this, "XBPNNCR4T72PEBX17GKF");
lifePublish.onStart();
}
/**
* Collect app metrics on Flurry
*
* @see android.app.Activity#onStop()
*/
@Override
protected void onStop() {
// Log.d(TAG, "onStop() called");
super.onStop();
FlurryAgent.onEndSession(this);
lifePublish.onStop();
}
/*
* (non-Javadoc)
*
* @see android.app.Activity#onPause()
*/
@Override
protected void onPause() {
super.onPause();
saveInfoFields();
((GaggleApplication) getApplication()).getGpsLogger().setObserver(null);
// Log.d(TAG, "onPause() called");
lifePublish.onPause();
}
private void saveInfoFields() {
// Save our list of recipients - FIXME, use the correct android saving
// system
try {
ObjectOutputStream stream = AndroidUtil.writeObjectStream(this,
"infofields");
stream.writeObject(infoView.getChecked());
stream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void restoreInfoFields() {
try {
// FIXME - use correct android system
ObjectInputStream stream = AndroidUtil.readObjectStream(this,
"infofields");
infoView.setChecked((String[]) stream.readObject());
stream.close();
} catch (Exception e) {
// TODO Auto-generated catch block
// e.printStackTrace();
}
}
/*
* (non-Javadoc)
*
* @see android.app.Activity#onRestart()
*/
@Override
protected void onRestart() {
super.onRestart();
// Log.d(TAG, "onRestart() called");
}
/*
* (non-Javadoc)
*
* @see android.app.Activity#onResume()
*/
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
// Log.d(TAG, "onResume() called");
lifePublish.onResume();
((GaggleApplication) getApplication()).getGpsLogger().setObserver(this);
// Restore the toggle to the correct state. FIXME, why doesn't this
// state get stored automatically by android?
// Super skanky way to find our possibly not yet existing service
showLoggingStatus();
}
/*
* (non-Javadoc)
*
* @see android.app.Activity#onDestroy()
*/
@Override
protected void onDestroy() {
Log.d(TAG, "onDestroy() called");
super.onDestroy();
}
private void showLoggingStatus() {
GPSToPositionWriter gpsToPos = ((GaggleApplication) getApplication())
.getGpsLogger();
loggingLabel.setText(gpsToPos.getStatusString());
loggingButton.setText(gpsToPos.isLogging() ? R.string.stop_logging
: R.string.start_logging);
}
/**
* Update our GUI thread because the logging status has changed
*/
@Override
public void onChanged(Object data) {
handler.post(onLoggingChange);
}
private Runnable onLoggingChange = new Runnable() {
@Override
public void run() {
showLoggingStatus();
}
};
private Button.OnClickListener loggingToggle = new Button.OnClickListener() {
@Override
public void onClick(View arg0) {
GPSToPositionWriter gpsToPos = ((GaggleApplication) getApplication())
.getGpsLogger();
if (!gpsToPos.isLogging()) {
if (((GaggleApplication) getApplication())
.enableGPS(LoggingControl.this)) {
startLogging();
}
} else {
gpsToPos.stopLogging();
}
}
};
/**
* Create our options menu
*
* @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.current_flight, menu);
boolean showAltButton = false;
try {
showAltButton = BarometerClient.create(this) != null
&& GPSClient.instance != null
&& GPSClient.instance.getLastKnownLocation() != null
&& GPSClient.instance.getLastKnownLocation().hasAltitude();
} catch (VerifyError ex) {
Log.e(TAG, "Not on 1.5: " + ex);
}
menu.findItem(R.id.setAltFromGPS).setVisible(showAltButton);
return true;
}
/**
* @see android.app.Activity#onMenuItemSelected(int, android.view.MenuItem)
*/
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
switch (item.getItemId()) {
case R.id.customfields:
Intent intent = new Intent(this, SelectInfoFieldsActivity.class);
intent.putExtra("checked", infoView.getChecked());
startActivityForResult(intent, INFOSELECT_REQUEST);
return true;
case R.id.setAltFromGPS:
GPSClient client = GPSClient.instance;
// FIXME - http://blueflyvario.blogspot.com/2011_05_01_archive.html
Location loc = client.getLastKnownLocation();
client.updateReferenceLocation(loc);
return true;
}
return super.onMenuItemSelected(featureId, item);
}
/**
* @see android.app.Activity#onActivityResult(int, int,
* android.content.Intent)
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == INFOSELECT_REQUEST) {
if (data != null) {
String[] checked = data.getStringArrayExtra("checked");
infoView.setChecked(checked);
}
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
/**
* Check that the username is valid, if not, turn off the appropriate checkbox
*/
private void validateAccounts() {
}
private void editAccounts(int acctNum) {
/*
* Intent i = new Intent(this, AccountsActivity.class);
* i.putExtra(AccountsActivity.EXTRA_ACCT_NUM, acctNum);
* this.startActivity(i);
*/
validateAccounts();
}
/**
* Handle our menu
*
* @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
/*
* case R.id.accounts_menu: editAccounts(0); return true;
*/
}
return super.onOptionsItemSelected(item);
}
private void startLogging() {
final Account acct = new Account(this, "live2");
// We always log to the DB
final PositionWriter dbwriter = new LocationDBWriter(this,
prefs.isDelayedUpload(), prefs.getPilotName(), null);
// Also always keep the the current live track
// FIXME - skanky way we pass live tracks to the map
LocationList loclist = new LocationList();
FlyMapActivity.liveList = loclist;
final PositionWriter ramwriter = new LocationListWriter(loclist);
loggingButton.setEnabled(false); // Turn off the button until our
// background thread finishes
AsyncProgressDialog progress = new AsyncProgressDialog(this,
getString(R.string.starting_logging), getString(R.string.please_wait)) {
@Override
protected void doInBackground() {
PositionWriter[] selected = null;
// Possibly also leonardo live
if (prefs.isLiveUpload()) {
try {
if (!acct.isValid())
throw new Exception(
getString(R.string.username_or_password_is_unset));
// FIXME - do this in an async dialog helper
PositionWriter liveWriter = new LeonardoLiveWriter(
LoggingControl.this, acct.serverURL, acct.username,
acct.password, prefs.getWingModel(),
prefs.getLeonardoLiveVehicleType(),
prefs.getLiveLogTimeInterval());
selected = new PositionWriter[] { dbwriter, ramwriter, liveWriter };
} catch (Exception ex) {
// Bad password or connection problems
showCompletionDialog(
LoggingControl.this.getString(R.string.leonardolive_problem),
ex.getMessage());
}
}
// If we haven't already connected to the live server
if (selected == null)
selected = new PositionWriter[] { dbwriter, ramwriter };
if (prefs.isSkyLinesTrackingEnabled()) {
try {
long key = prefs.getSkyLinesKey();
if (key != 0) {
PositionWriter liveWriter =
new SkyLinesTrackingWriter(key,
prefs.getSkyLinesTrackingInterval());
PositionWriter[] n =
new PositionWriter[selected.length + 1];
System.arraycopy(selected, 0, n, 0, selected.length);
n[selected.length] = liveWriter;
selected = n;
}
} catch (Exception ex) {
showCompletionDialog(LoggingControl.this.getString(R.string.skylines_problem),
ex.getMessage());
}
}
PositionWriter writer = new PositionWriterSet(selected);
// Start up our logger service
GPSToPositionWriter gpsToPos = ((GaggleApplication) getApplication())
.getGpsLogger();
gpsToPos.startLogging(getApplication(), writer,
prefs.getLogTimeInterval(), prefs.getLaunchDistX(),
prefs.getLaunchDistY());
}
/**
* @see com.geeksville.view.AsyncProgressDialog#onPostExecute
* (java.lang.Void)
*/
@Override
protected void onPostExecute(Void unused) {
loggingButton.setEnabled(true);
super.onPostExecute(unused);
}
};
progress.execute();
}
@Override
public void addLifeCycleHandler(LifeCycleHandler h) {
lifePublish.addLifeCycleHandler(h);
}
@Override
public void removeLifeCycleHandler(LifeCycleHandler h) {
lifePublish.removeLifeCycleHandler(h);
}
}