/*
* Copyright (C) 2012 - 2013 jonas.oreland@gmail.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.runnerup.view;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.database.sqlite.SQLiteDatabase;
import android.location.Location;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.TabHost;
import android.widget.TabHost.OnTabChangeListener;
import android.widget.TabHost.TabSpec;
import android.widget.TextView;
import org.runnerup.BuildConfig;
import org.runnerup.R;
import org.runnerup.common.tracker.TrackerState;
import org.runnerup.common.util.Constants;
import org.runnerup.common.util.Constants.DB;
import org.runnerup.db.DBHelper;
import org.runnerup.hr.MockHRProvider;
import org.runnerup.notification.GpsBoundState;
import org.runnerup.notification.GpsSearchingState;
import org.runnerup.notification.NotificationManagerDisplayStrategy;
import org.runnerup.notification.NotificationStateManager;
import org.runnerup.tracker.GpsInformation;
import org.runnerup.tracker.Tracker;
import org.runnerup.tracker.component.TrackerHRM;
import org.runnerup.tracker.component.TrackerWear;
import org.runnerup.util.Formatter;
import org.runnerup.util.SafeParse;
import org.runnerup.util.TickListener;
import org.runnerup.widget.TitleSpinner;
import org.runnerup.widget.TitleSpinner.OnCloseDialogListener;
import org.runnerup.widget.TitleSpinner.OnSetValueListener;
import org.runnerup.widget.WidgetUtil;
import org.runnerup.workout.Dimension;
import org.runnerup.workout.Workout;
import org.runnerup.workout.Workout.StepListEntry;
import org.runnerup.workout.WorkoutBuilder;
import org.runnerup.workout.WorkoutSerializer;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
@TargetApi(Build.VERSION_CODES.FROYO)
public class StartActivity extends Activity implements TickListener, GpsInformation {
final static String TAB_BASIC = "basic";
final static String TAB_INTERVAL = "interval";
final static String TAB_ADVANCED = "advanced";
final static String TAB_MANUAL = "manual";
boolean skipStopGps = false;
Tracker mTracker = null;
org.runnerup.tracker.GpsStatus mGpsStatus = null;
private TabHost tabHost = null;
private Button startButton = null;
private TextView gpsInfoText = null;
private TextView gpsInfoView1 = null;
private TextView gpsInfoView2 = null;
private View gpsInfoLayout = null;
private TextView hrInfo = null;
ImageButton hrButton = null;
TextView hrValueText = null;
FrameLayout hrLayout = null;
boolean batteryLevelMessageShowed = false;
ImageButton wearButton = null;
TextView wearValueText = null;
FrameLayout wearLayout = null;
TitleSpinner simpleAudioSpinner = null;
AudioSchemeListAdapter simpleAudioListAdapter = null;
TitleSpinner simpleTargetType = null;
TitleSpinner simpleTargetPaceValue = null;
TitleSpinner simpleTargetHrz = null;
HRZonesListAdapter hrZonesAdapter = null;
TitleSpinner intervalType = null;
TitleSpinner intervalTime = null;
TitleSpinner intervalDistance = null;
TitleSpinner intervalRestType = null;
TitleSpinner intervalRestTime = null;
TitleSpinner intervalRestDistance = null;
TitleSpinner intervalAudioSpinner = null;
AudioSchemeListAdapter intervalAudioListAdapter = null;
TitleSpinner advancedWorkoutSpinner = null;
WorkoutListAdapter advancedWorkoutListAdapter = null;
TitleSpinner advancedAudioSpinner = null;
AudioSchemeListAdapter advancedAudioListAdapter = null;
Button advancedDownloadWorkoutButton = null;
Workout advancedWorkout = null;
ListView advancedStepList = null;
final WorkoutStepsAdapter advancedWorkoutStepsAdapter = new WorkoutStepsAdapter();
boolean manualSetValue = false;
TitleSpinner manualSport = null;
TitleSpinner manualDate = null;
TitleSpinner manualTime = null;
TitleSpinner manualDistance = null;
TitleSpinner manualDuration = null;
TitleSpinner manualPace = null;
EditText manualNotes = null;
SQLiteDatabase mDB = null;
Formatter formatter = null;
private NotificationStateManager notificationStateManager;
private GpsSearchingState gpsSearchingState;
private GpsBoundState gpsBoundState;
private boolean headsetRegistered = false;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDB = DBHelper.getWritableDatabase(this);
formatter = new Formatter(this);
bindGpsTracker();
mGpsStatus = new org.runnerup.tracker.GpsStatus(this);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationStateManager = new NotificationStateManager(new NotificationManagerDisplayStrategy(notificationManager));
gpsSearchingState = new GpsSearchingState(this, this);
gpsBoundState = new GpsBoundState(this);
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
setContentView(R.layout.start);
startButton = (Button) findViewById(R.id.start_button);
startButton.setOnClickListener(startButtonClick);
gpsInfoLayout = findViewById(R.id.gpsinfo);
gpsInfoText = (TextView) findViewById(R.id.gps_info_text);
gpsInfoView1 = (TextView) findViewById(R.id.gps_info1);
gpsInfoView2 = (TextView) findViewById(R.id.gps_info2);
hrInfo = (TextView) findViewById(R.id.hr_info);
hrButton = (ImageButton) findViewById(R.id.hr_button);
hrButton.setOnClickListener(hrButtonClick);
hrValueText = (TextView) findViewById(R.id.hr_value_text);
hrLayout = (FrameLayout) findViewById(R.id.hr_layout);
wearButton = (ImageButton) findViewById(R.id.wear_button);
wearValueText = (TextView) findViewById(R.id.wear_value_text);
wearLayout = (FrameLayout) findViewById(R.id.wear_layout);
tabHost = (TabHost) findViewById(R.id.tabhost_start);
tabHost.setup();
TabSpec tabSpec = tabHost.newTabSpec(TAB_BASIC);
tabSpec.setIndicator(WidgetUtil.createHoloTabIndicator(this, getString(R.string.Basic)));
tabSpec.setContent(R.id.tab_basic);
tabHost.addTab(tabSpec);
tabSpec = tabHost.newTabSpec(TAB_INTERVAL);
tabSpec.setIndicator(WidgetUtil.createHoloTabIndicator(this, getString(R.string.Interval)));
tabSpec.setContent(R.id.tab_interval);
tabHost.addTab(tabSpec);
tabSpec = tabHost.newTabSpec(TAB_ADVANCED);
tabSpec.setIndicator(WidgetUtil.createHoloTabIndicator(this, getString(R.string.Advanced)));
tabSpec.setContent(R.id.tab_advanced);
tabHost.addTab(tabSpec);
tabSpec = tabHost.newTabSpec(TAB_MANUAL);
tabSpec.setIndicator(WidgetUtil.createHoloTabIndicator(this, getString(R.string.Manual)));
tabSpec.setContent(R.id.tab_manual);
tabHost.addTab(tabSpec);
tabHost.setOnTabChangedListener(onTabChangeListener);
//tabHost.getTabWidget().setBackgroundColor(Color.DKGRAY);
simpleAudioListAdapter = new AudioSchemeListAdapter(mDB, inflater, false);
simpleAudioListAdapter.reload();
simpleAudioSpinner = (TitleSpinner) findViewById(R.id.basic_audio_cue_spinner);
simpleAudioSpinner.setAdapter(simpleAudioListAdapter);
simpleTargetType = (TitleSpinner) findViewById(R.id.tab_basic_target_type);
simpleTargetPaceValue = (TitleSpinner) findViewById(R.id.tab_basic_target_pace_max);
hrZonesAdapter = new HRZonesListAdapter(this, inflater);
simpleTargetHrz = (TitleSpinner) findViewById(R.id.tab_basic_target_hrz);
simpleTargetHrz.setAdapter(hrZonesAdapter);
simpleTargetType.setOnCloseDialogListener(simpleTargetTypeClick);
intervalType = (TitleSpinner) findViewById(R.id.interval_type);
intervalTime = (TitleSpinner) findViewById(R.id.interval_time);
intervalTime.setOnSetValueListener(onSetTimeValidator);
intervalDistance = (TitleSpinner) findViewById(R.id.interval_distance);
intervalType.setOnSetValueListener(intervalTypeSetValue);
intervalRestType = (TitleSpinner) findViewById(R.id.interval_rest_type);
intervalRestTime = (TitleSpinner) findViewById(R.id.interval_rest_time);
intervalRestTime.setOnSetValueListener(onSetTimeValidator);
intervalRestDistance = (TitleSpinner) findViewById(R.id.interval_rest_distance);
intervalRestType.setOnSetValueListener(intervalRestTypeSetValue);
intervalAudioListAdapter = new AudioSchemeListAdapter(mDB, inflater, false);
intervalAudioListAdapter.reload();
intervalAudioSpinner = (TitleSpinner) findViewById(R.id.interval_audio_cue_spinner);
intervalAudioSpinner.setAdapter(intervalAudioListAdapter);
advancedAudioListAdapter = new AudioSchemeListAdapter(mDB, inflater, false);
advancedAudioListAdapter.reload();
advancedAudioSpinner = (TitleSpinner) findViewById(R.id.advanced_audio_cue_spinner);
advancedAudioSpinner.setAdapter(advancedAudioListAdapter);
advancedWorkoutSpinner = (TitleSpinner) findViewById(R.id.advanced_workout_spinner);
advancedWorkoutListAdapter = new WorkoutListAdapter(inflater);
advancedWorkoutListAdapter.reload();
advancedWorkoutSpinner.setAdapter(advancedWorkoutListAdapter);
advancedWorkoutSpinner.setOnSetValueListener(new OnSetValueListener() {
@Override
public String preSetValue(String newValue)
throws IllegalArgumentException {
loadAdvanced(newValue);
return newValue;
}
@Override
public int preSetValue(int newValue)
throws IllegalArgumentException {
loadAdvanced(null);
return newValue;
}
});
advancedStepList = (ListView) findViewById(R.id.advanced_step_list);
advancedStepList.setDividerHeight(0);
advancedStepList.setAdapter(advancedWorkoutStepsAdapter);
advancedDownloadWorkoutButton = (Button) findViewById(R.id.advanced_download_button);
advancedDownloadWorkoutButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(StartActivity.this, ManageWorkoutsActivity.class);
StartActivity.this.startActivityForResult(intent, 113);
}
});
manualSport = (TitleSpinner) findViewById(R.id.manual_sport);
manualDate = (TitleSpinner) findViewById(R.id.manual_date);
manualDate.setOnSetValueListener(onSetValueManual);
manualTime = (TitleSpinner) findViewById(R.id.manual_time);
manualTime.setOnSetValueListener(onSetValueManual);
manualDistance = (TitleSpinner) findViewById(R.id.manual_distance);
manualDistance.setOnSetValueListener(onSetManualDistance);
manualDuration = (TitleSpinner) findViewById(R.id.manual_duration);
manualDuration.setOnSetValueListener(onSetManualDuration);
manualPace = (TitleSpinner) findViewById(R.id.manual_pace);
manualPace.setVisibility(View.GONE);
manualNotes = (EditText) findViewById(R.id.manual_notes);
if (getParent() != null && getParent().getIntent() != null) {
Intent i = getParent().getIntent();
if (i.hasExtra("mode")) {
if (i.getStringExtra("mode").equals(TAB_ADVANCED)) {
tabHost.setCurrentTab(2);
i.removeExtra("mode");
}
}
}
updateTargetView();
}
@Override
public void onStart() {
super.onStart();
registerStartEventListener();
}
@Override
public void onResume() {
super.onResume();
simpleAudioListAdapter.reload();
intervalAudioListAdapter.reload();
advancedAudioListAdapter.reload();
advancedWorkoutListAdapter.reload();
hrZonesAdapter.reload();
simpleTargetHrz.setAdapter(hrZonesAdapter);
if (!hrZonesAdapter.hrZones.isConfigured()) {
simpleTargetType.addDisabledValue(DB.DIMENSION.HRZ);
} else {
simpleTargetType.clearDisabled();
}
if (tabHost.getCurrentTabTag().contentEquals(TAB_ADVANCED)) {
loadAdvanced(null);
}
if (mIsBound == false || mTracker == null) {
bindGpsTracker();
} else {
onGpsTrackerBound();
}
this.updateView();
}
@Override
public void onPause() {
super.onPause();
if (getAutoStartGps()) {
/**
* If autoStartGps, then stop it during pause
*/
stopGps();
} else {
if (mTracker != null &&
((mTracker.getState() == TrackerState.INITIALIZED) ||
(mTracker.getState() == TrackerState.INITIALIZING))) {
Log.e(getClass().getName(), "mTracker.reset()");
mTracker.reset();
}
}
}
@Override
public void onStop() {
super.onStop();
unregisterStartEventListener();
}
@Override
public void onDestroy() {
stopGps();
unbindGpsTracker();
mGpsStatus = null;
mTracker = null;
DBHelper.closeDB(mDB);
super.onDestroy();
}
@Override
public void onBackPressed () {
if (!getAutoStartGps() && mGpsStatus.isLogging()) {
stopGps();
updateView();
} else {
super.onBackPressed();
}
}
private final BroadcastReceiver startEventBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (mTracker == null)
return;
if (!startButton.isEnabled())
return;
if (mTracker.getState() == TrackerState.INIT /* this will start gps */||
mTracker.getState() == TrackerState.INITIALIZED /* ...start a workout*/ ||
mTracker.getState() == TrackerState.CONNECTED) {
startButton.performClick();
}
}
});
}
};
private void registerStartEventListener() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Constants.Intents.START_WORKOUT);
registerReceiver(startEventBroadcastReceiver, intentFilter);
if (StartActivityHeadsetButtonReceiver.getAllowStartStopFromHeadsetKey(this)) {
headsetRegistered = true;
StartActivityHeadsetButtonReceiver.registerHeadsetListener(this);
}
}
private void unregisterStartEventListener() {
try {
unregisterReceiver(startEventBroadcastReceiver);
} catch (Exception e) {
}
if (headsetRegistered) {
headsetRegistered = false;
StartActivityHeadsetButtonReceiver.unregisterHeadsetListener(this);
}
}
void onGpsTrackerBound() {
if (getAutoStartGps()) {
startGps();
} else {
switch (mTracker.getState()) {
case INIT:
case CLEANUP:
mTracker.setup();
break;
case INITIALIZING:
case INITIALIZED:
break;
case CONNECTING:
case CONNECTED:
case STARTED:
case PAUSED:
if (BuildConfig.DEBUG) {
//Seem to happen when returning to RunnerUp
Log.e(getClass().getName(),"onGpsTrackerBound unexpected tracker state: "+mTracker.getState().toString());
}
break;
case ERROR:
break;
}
}
updateView();
}
boolean getAutoStartGps() {
Context ctx = getApplicationContext();
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(ctx);
return pref.getBoolean(getString(R.string.pref_startgps), false);
}
private void startGps() {
Log.e(getClass().getName(), "StartActivity.startGps()");
if (mGpsStatus != null && !mGpsStatus.isLogging())
mGpsStatus.start(this);
if (mTracker != null) {
mTracker.connect();
}
notificationStateManager.displayNotificationState(gpsSearchingState);
}
private void stopGps() {
Log.e(getClass().getName(), "StartActivity.stopGps() skipStop: " + this.skipStopGps);
if (skipStopGps == true)
return;
if (mGpsStatus != null)
mGpsStatus.stop(this);
if (mTracker != null)
mTracker.reset();
notificationStateManager.cancelNotification();
}
protected void notificationBatteryLevel(int batteryLevel) {
if ((batteryLevel < 0) || (batteryLevel > 100)) {
return;
}
final String pref_key = getString(R.string.pref_battery_level_low_notification_discard);
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
int batteryLevelHighThreshold = SafeParse.parseInt(prefs.getString(getString(
R.string.pref_battery_level_high_threshold), "75"), 75);
if ((batteryLevel > batteryLevelHighThreshold) && (prefs.contains(pref_key))) {
prefs.edit().remove(pref_key).commit();
return;
}
int batteryLevelLowThreshold = SafeParse.parseInt(prefs.getString(getString(
R.string.pref_battery_level_low_threshold), "15"), 15);
if (batteryLevel > batteryLevelLowThreshold) {
return;
}
if (prefs.getBoolean(pref_key, false)) {
return;
}
AlertDialog.Builder prompt = new AlertDialog.Builder(this);
final CheckBox dontShowAgain = new CheckBox(this);
dontShowAgain.setText(getResources().getText(R.string.Do_not_show_again));
prompt.setView(dontShowAgain);
prompt.setCancelable(false);
prompt.setMessage(getResources().getText(R.string.Low_HRM_battery_level)
+ "\n" + getResources().getText(R.string.Battery_level) + ": " + batteryLevel + "%");
prompt.setTitle(getResources().getText(R.string.Warning));
prompt.setPositiveButton(getResources().getText(R.string.OK), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (dontShowAgain.isChecked()) {
prefs.edit().putBoolean(pref_key, true).commit();
}
return;
}
});
prompt.show();
}
final OnTabChangeListener onTabChangeListener = new OnTabChangeListener() {
@Override
public void onTabChanged(String tabId) {
if (tabId.contentEquals(TAB_BASIC))
startButton.setVisibility(View.VISIBLE);
else if (tabId.contentEquals(TAB_INTERVAL))
startButton.setVisibility(View.VISIBLE);
else if (tabId.contentEquals(TAB_ADVANCED)) {
startButton.setVisibility(View.VISIBLE);
loadAdvanced(null);
} else if (tabId.contentEquals(TAB_MANUAL)) {
startButton.setText(getString(R.string.Save_activity));
}
updateView();
}
};
Workout prepareWorkout() {
Context ctx = getApplicationContext();
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(ctx);
SharedPreferences audioPref = null;
Workout w = null;
if (tabHost.getCurrentTabTag().contentEquals(TAB_BASIC)) {
audioPref = WorkoutBuilder.getAudioCuePreferences(ctx, pref,
getString(R.string.pref_basic_audio));
Dimension target = Dimension.valueOf(simpleTargetType.getValueInt());
w = WorkoutBuilder.createDefaultWorkout(getResources(), pref, target);
} else if (tabHost.getCurrentTabTag().contentEquals(TAB_INTERVAL)) {
audioPref = WorkoutBuilder.getAudioCuePreferences(ctx, pref,
getString(R.string.pref_interval_audio));
w = WorkoutBuilder.createDefaultIntervalWorkout(getResources(), pref);
} else if (tabHost.getCurrentTabTag().contentEquals(TAB_ADVANCED)) {
audioPref = WorkoutBuilder.getAudioCuePreferences(ctx, pref,
getString(R.string.pref_advanced_audio));
w = advancedWorkout;
}
WorkoutBuilder.prepareWorkout(getResources(), pref, w,
TAB_BASIC.contentEquals(tabHost.getCurrentTabTag()));
WorkoutBuilder.addAudioCuesToWorkout(getResources(), w, audioPref);
return w;
}
final OnClickListener startButtonClick = new OnClickListener() {
public void onClick(View v) {
if (tabHost.getCurrentTabTag().contentEquals(TAB_MANUAL)) {
manualSaveButtonClick.onClick(v);
return;
} else if (mGpsStatus.isEnabled() == false) {
startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
} else if (mTracker.getState() != TrackerState.CONNECTED) {
startGps();
} else if (mTracker.getState() == TrackerState.CONNECTED) {
mGpsStatus.stop(StartActivity.this);
/**
* unregister receivers
*/
unregisterStartEventListener();
/**
* This will start the advancedWorkoutSpinner!
*/
mTracker.setWorkout(prepareWorkout());
mTracker.start();
skipStopGps = true;
Intent intent = new Intent(StartActivity.this,
RunActivity.class);
StartActivity.this.startActivityForResult(intent, 112);
notificationStateManager.cancelNotification(); // will be added by RunActivity
return;
}
updateView();
}
};
final OnClickListener hrButtonClick = new OnClickListener() {
@Override
public void onClick(View arg0) {
}
};
private void updateView() {
if (!mGpsStatus.isEnabled() || !mGpsStatus.isLogging()) {
gpsInfoText.setVisibility(View.GONE);
gpsInfoView1.setText("");
gpsInfoView2.setText("");
} else {
int cnt0 = mGpsStatus.getSatellitesFixed();
int cnt1 = mGpsStatus.getSatellitesAvailable();
gpsInfoText.setVisibility(View.VISIBLE);
gpsInfoView1.setText(": " + cnt0 + "/" + cnt1);
gpsInfoView2.setText(getGpsAccuracy());
}
int playIcon = 0;
if (tabHost.getCurrentTabTag().contentEquals(TAB_MANUAL)) {
gpsInfoLayout.setVisibility(View.GONE);
startButton.setEnabled(manualSetValue);
startButton.setText(getString(R.string.Save_activity));
return;
} else if (mGpsStatus.isEnabled() == false) {
startButton.setEnabled(true);
startButton.setText(getString(R.string.Enable_GPS));
} else if (mGpsStatus.isLogging() == false) {
startButton.setEnabled(true);
startButton.setText(getString(R.string.Start_GPS));
} else if (mGpsStatus.isFixed() == false) {
startButton.setEnabled(false);
startButton.setText(getString(R.string.Waiting_for_GPS));
notificationStateManager.displayNotificationState(gpsSearchingState);
} else {
playIcon = R.drawable.ic_av_play_arrow;
startButton.setText(getString(R.string.Start_activity));
if (!tabHost.getCurrentTabTag().contentEquals(TAB_ADVANCED) || advancedWorkout != null) {
startButton.setEnabled(true);
} else {
startButton.setEnabled(false);
}
notificationStateManager.displayNotificationState(gpsBoundState);
}
startButton.setCompoundDrawablesWithIntrinsicBounds(0, 0, playIcon, 0);
gpsInfoLayout.setVisibility(View.VISIBLE);
{
Resources res = getResources();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
final String btDeviceName = prefs.getString(res.getString(R.string.pref_bt_name), null);
if (btDeviceName != null) {
hrInfo.setText(btDeviceName);
} else {
hrInfo.setText("");
if (MockHRProvider.NAME.contentEquals(prefs.getString(
res.getString(R.string.pref_bt_provider), ""))) {
final String btAddress = "mock: "
+ prefs.getString(res.getString(R.string.pref_bt_address), "???");
hrInfo.setText(btAddress);
}
}
}
boolean hideHR = true;
boolean hideWear = true;
if (mTracker != null) {
if (mTracker.isComponentConfigured(TrackerHRM.NAME)) {
hideHR = false;
Integer hrVal = null;
if (mTracker.isComponentConnected(TrackerHRM.NAME)) {
hrVal = mTracker.getCurrentHRValue();
}
if (hrVal != null) {
hrButton.setEnabled(false);
hrValueText.setText(Integer.toString(hrVal));
if (!batteryLevelMessageShowed) {
batteryLevelMessageShowed = true;
notificationBatteryLevel(mTracker.getCurrentBatteryLevel());
}
} else {
hrButton.setEnabled(true);
hrValueText.setText("?");
}
}
if (mTracker.isComponentConfigured(TrackerWear.NAME)) {
hideWear = false;
if (mTracker.isComponentConnected(TrackerWear.NAME)) {
wearValueText.setVisibility(View.GONE);
} else {
wearValueText.setText("?");
wearValueText.setVisibility(View.VISIBLE);
}
}
}
if (hideHR)
hrLayout.setVisibility(View.GONE);
else
hrLayout.setVisibility(View.VISIBLE);
if (hideWear)
wearLayout.setVisibility(View.GONE);
else
wearLayout.setVisibility(View.VISIBLE);
}
@Override
public String getGpsAccuracy() {
String s = "";
if (mTracker != null) {
Location l = mTracker.getLastKnownLocation();
if (l != null && l.getAccuracy() > 0) {
s = String.format(Locale.getDefault(), ", %s m", l.getAccuracy());
}
if (mTracker.getCurrentElevation() != null) {
s += String.format(Locale.getDefault(), " (%.1f m)", mTracker.getCurrentElevation());
}
}
return s;
}
private boolean mIsBound = false;
private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. Because we have bound to a explicit
// service that we know is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
mTracker = ((Tracker.LocalBinder) service).getService();
// Tell the user about this for our demo.
StartActivity.this.onGpsTrackerBound();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
// Because it is running in our same process, we should never
// see this happen.
mTracker = null;
}
};
void bindGpsTracker() {
// Establish a connection with the service. We use an explicit
// class name because we want a specific service implementation that
// we know will be running in our own process (and thus won't be
// supporting component replacement by other applications).
getApplicationContext().bindService(new Intent(this, Tracker.class),
mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
void unbindGpsTracker() {
if (mIsBound) {
// Detach our existing connection.
getApplicationContext().unbindService(mConnection);
mIsBound = false;
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
registerStartEventListener();
if (data != null) {
if (data.getStringExtra("url") != null)
Log.e(getClass().getName(), "data.getStringExtra(\"url\") => " + data.getStringExtra("url"));
if (data.getStringExtra("ex") != null)
Log.e(getClass().getName(), "data.getStringExtra(\"ex\") => " + data.getStringExtra("ex"));
if (data.getStringExtra("obj") != null)
Log.e(getClass().getName(), "data.getStringExtra(\"obj\") => " + data.getStringExtra("obj"));
}
if (requestCode == 112) {
skipStopGps = false;
if (mIsBound == false || mTracker == null) {
bindGpsTracker();
} else {
onGpsTrackerBound();
}
} else {
advancedWorkoutListAdapter.reload();
}
updateView();
}
@Override
public void onTick() {
updateView();
}
final OnCloseDialogListener simpleTargetTypeClick = new OnCloseDialogListener() {
@Override
public void onClose(TitleSpinner spinner, boolean ok) {
if (ok) {
updateTargetView();
}
}
};
void updateTargetView() {
Dimension dim = Dimension.valueOf(simpleTargetType.getValueInt());
if (dim == null) {
simpleTargetPaceValue.setEnabled(false);
simpleTargetHrz.setEnabled(false);
} else {
switch (dim) {
case PACE:
simpleTargetPaceValue.setEnabled(true);
simpleTargetPaceValue.setVisibility(View.VISIBLE);
simpleTargetHrz.setVisibility(View.GONE);
break;
case HRZ:
simpleTargetPaceValue.setVisibility(View.GONE);
simpleTargetHrz.setEnabled(true);
simpleTargetHrz.setVisibility(View.VISIBLE);
}
}
}
final OnSetValueListener intervalTypeSetValue = new OnSetValueListener() {
@Override
public String preSetValue(String newValue)
throws IllegalArgumentException {
return newValue;
}
@Override
public int preSetValue(int newValue) throws IllegalArgumentException {
boolean time = (newValue == 0);
intervalTime.setVisibility(time ? View.VISIBLE : View.GONE);
intervalDistance.setVisibility(time ? View.GONE : View.VISIBLE);
return newValue;
}
};
final OnSetValueListener intervalRestTypeSetValue = new OnSetValueListener() {
@Override
public String preSetValue(String newValue)
throws IllegalArgumentException {
return newValue;
}
@Override
public int preSetValue(int newValue) throws IllegalArgumentException {
boolean time = (newValue == 0);
intervalRestTime.setVisibility(time ? View.VISIBLE : View.GONE);
intervalRestDistance.setVisibility(time ? View.GONE : View.VISIBLE);
return newValue;
}
};
void loadAdvanced(String name) {
Context ctx = getApplicationContext();
if (name == null) {
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(ctx);
name = pref.getString(getResources().getString(R.string.pref_advanced_workout), "");
}
advancedWorkout = null;
if ("".contentEquals(name))
return;
try {
advancedWorkout = WorkoutSerializer.readFile(ctx, name);
advancedWorkoutStepsAdapter.steps = advancedWorkout.getStepList();
advancedWorkoutStepsAdapter.notifyDataSetChanged();
advancedDownloadWorkoutButton.setVisibility(View.GONE);
} catch (Exception ex) {
ex.printStackTrace();
AlertDialog.Builder builder = new AlertDialog.Builder(StartActivity.this);
builder.setTitle(getString(R.string.Failed_to_load_workout));
builder.setMessage("" + ex.toString());
builder.setPositiveButton(getString(R.string.OK),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builder.show();
return;
}
}
@Override
public int getSatellitesAvailable() {
return mGpsStatus.getSatellitesAvailable();
}
@Override
public int getSatellitesFixed() {
return mGpsStatus.getSatellitesFixed();
}
final class WorkoutStepsAdapter extends BaseAdapter {
List<StepListEntry> steps = new ArrayList<StepListEntry>();
@Override
public int getCount() {
return steps.size();
}
@Override
public Object getItem(int position) {
return steps.get(position);
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
StepListEntry entry = steps.get(position);
StepButton button = null;
if (convertView != null && convertView instanceof StepButton) {
button = (StepButton) convertView;
} else {
button = new StepButton(StartActivity.this, null);
}
button.setStep(entry.step);
button.setPadding(entry.level * 7, 0, 0, 0);
button.setOnChangedListener(onWorkoutChanged);
return button;
}
}
final Runnable onWorkoutChanged = new Runnable() {
@Override
public void run() {
String name = advancedWorkoutSpinner.getValue().toString();
if (advancedWorkout != null) {
Context ctx = getApplicationContext();
try {
WorkoutSerializer.writeFile(ctx, name, advancedWorkout);
} catch (Exception ex) {
AlertDialog.Builder builder = new AlertDialog.Builder(StartActivity.this);
builder.setTitle(getString(R.string.Failed_to_load_workout));
builder.setMessage("" + ex.toString());
builder.setPositiveButton(getString(R.string.OK),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builder.show();
return;
}
}
}
};
final OnSetValueListener onSetTimeValidator = new OnSetValueListener() {
@Override
public String preSetValue(String newValue)
throws IllegalArgumentException {
if (WorkoutBuilder.validateSeconds(newValue))
return newValue;
throw new IllegalArgumentException("Unable to parse time value: " + newValue);
}
@Override
public int preSetValue(int newValue) throws IllegalArgumentException {
return newValue;
}
};
final OnSetValueListener onSetValueManual = new OnSetValueListener() {
@Override
public String preSetValue(String newValue)
throws IllegalArgumentException {
manualSetValue = true;
startButton.setEnabled(true);
return newValue;
}
@Override
public int preSetValue(int newValue) throws IllegalArgumentException {
manualSetValue = true;
startButton.setEnabled(true);
return newValue;
}
};
void setManualPace(String distance, String duration) {
Log.e(getClass().getName(), "distance: >" + distance + "< duration: >" + duration + "<");
double dist = SafeParse.parseDouble(distance, 0); // convert to meters
long seconds = SafeParse.parseSeconds(duration, 0);
if (dist == 0 || seconds == 0) {
manualPace.setVisibility(View.GONE);
return;
}
double pace = seconds / dist;
manualPace.setValue(formatter.formatPace(Formatter.Format.TXT_SHORT, pace));
manualPace.setVisibility(View.VISIBLE);
return;
}
final OnSetValueListener onSetManualDistance = new OnSetValueListener() {
@Override
public String preSetValue(String newValue)
throws IllegalArgumentException {
setManualPace(newValue, manualDuration.getValue().toString());
startButton.setEnabled(true);
return newValue;
}
@Override
public int preSetValue(int newValue) throws IllegalArgumentException {
startButton.setEnabled(true);
return newValue;
}
};
final OnSetValueListener onSetManualDuration = new OnSetValueListener() {
@Override
public String preSetValue(String newValue)
throws IllegalArgumentException {
setManualPace(manualDistance.getValue().toString(), newValue);
startButton.setEnabled(true);
return newValue;
}
@Override
public int preSetValue(int newValue) throws IllegalArgumentException {
startButton.setEnabled(true);
return newValue;
}
};
final OnClickListener manualSaveButtonClick = new OnClickListener() {
@Override
public void onClick(View v) {
ContentValues save = new ContentValues();
int sport = manualSport.getValueInt();
CharSequence date = manualDate.getValue();
CharSequence time = manualTime.getValue();
CharSequence distance = manualDistance.getValue();
CharSequence duration = manualDuration.getValue();
String notes = manualNotes.getText().toString().trim();
long start_time = 0;
if (notes.length() > 0) {
save.put(DB.ACTIVITY.COMMENT, notes);
}
double dist = 0;
if (distance.length() > 0) {
dist = Double.parseDouble(distance.toString()); // convert to
// meters
save.put(DB.ACTIVITY.DISTANCE, dist);
}
long secs = 0;
if (duration.length() > 0) {
secs = SafeParse.parseSeconds(duration.toString(), 0);
save.put(DB.ACTIVITY.TIME, secs);
}
if (date.length() > 0) {
DateFormat df = android.text.format.DateFormat.getDateFormat(StartActivity.this);
try {
Date d = df.parse(date.toString());
start_time += d.getTime() / 1000;
} catch (ParseException e) {
}
}
if (time.length() > 0) {
DateFormat df = android.text.format.DateFormat.getTimeFormat(StartActivity.this);
try {
Date d = df.parse(time.toString());
start_time += d.getTime() / 1000;
} catch (ParseException e) {
}
}
save.put(DB.ACTIVITY.START_TIME, start_time);
save.put(DB.ACTIVITY.SPORT, sport);
long id = mDB.insert(DB.ACTIVITY.TABLE, null, save);
ContentValues lap = new ContentValues();
lap.put(DB.LAP.ACTIVITY, id);
lap.put(DB.LAP.LAP, 0);
lap.put(DB.LAP.INTENSITY, DB.INTENSITY.ACTIVE);
lap.put(DB.LAP.TIME, secs);
lap.put(DB.LAP.DISTANCE, dist);
mDB.insert(DB.LAP.TABLE, null, lap);
Intent intent = new Intent(StartActivity.this, DetailActivity.class);
intent.putExtra("mode", "save");
intent.putExtra("ID", id);
StartActivity.this.startActivityForResult(intent, 0);
}
};
}