/*
* Copyright (C) 2009 University of Washington
*
* 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 org.odk.collect.android.widgets;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TableLayout;
import android.widget.TextView;
import org.javarosa.core.model.data.GeoPointData;
import org.javarosa.core.model.data.IAnswerData;
import org.javarosa.form.api.FormEntryPrompt;
import org.odk.collect.android.R;
import org.odk.collect.android.activities.FormEntryActivity;
import org.odk.collect.android.activities.GeoPointActivity;
import org.odk.collect.android.application.Collect;
import java.text.DecimalFormat;
/**
* GeoPointWidget is the widget that allows the user to get GPS readings.
*
* @author Carl Hartung (carlhartung@gmail.com)
* @author Yaw Anokwa (yanokwa@gmail.com)
*/
public class GeoPointWidget extends QuestionWidget implements IBinaryWidget {
public static final String LOCATION = "gp";
public static final String ACCURACY_THRESHOLD = "accuracyThreshold";
public static final String READ_ONLY = "readOnly";
public static final double DEFAULT_LOCATION_ACCURACY = 5.0;
private Button mGetLocationButton;
private TextView mStringAnswer;
private TextView mAnswerDisplay;
private final boolean mReadOnly;
private String mAppearance;
private double mAccuracyThreshold;
public GeoPointWidget(Context context, FormEntryPrompt prompt) {
super(context, prompt);
// Determine the activity threshold to use
String acc = prompt.getQuestion().getAdditionalAttribute(null, ACCURACY_THRESHOLD);
if ( acc != null && acc.length() != 0 ) {
mAccuracyThreshold = Double.parseDouble(acc);
} else {
mAccuracyThreshold = DEFAULT_LOCATION_ACCURACY;
}
// Determine whether or not to use the plain, maps, or mapsV2 activity
mAppearance = prompt.getAppearanceHint();
mReadOnly = prompt.isReadOnly();
// assemble the widget...
setOrientation(LinearLayout.VERTICAL);
TableLayout.LayoutParams params = new TableLayout.LayoutParams();
params.setMargins(7, 5, 7, 5);
mStringAnswer = new TextView(getContext());
mStringAnswer.setId(QuestionWidget.newUniqueId());
mAnswerDisplay = new TextView(getContext());
mAnswerDisplay.setId(QuestionWidget.newUniqueId());
mAnswerDisplay
.setTextSize(TypedValue.COMPLEX_UNIT_DIP, mAnswerFontsize);
mAnswerDisplay.setGravity(Gravity.CENTER);
mGetLocationButton = new Button(getContext());
mGetLocationButton.setId(QuestionWidget.newUniqueId());
mGetLocationButton.setPadding(20, 20, 20, 20);
mGetLocationButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP,
mAnswerFontsize);
mGetLocationButton.setEnabled(!prompt.isReadOnly());
mGetLocationButton.setLayoutParams(params);
// when you press the button
mGetLocationButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Collect.getInstance()
.getActivityLogger()
.logInstanceAction(this, "recordLocation", "click",
mPrompt.getIndex());
Intent i = null;
i = new Intent(getContext(), GeoPointActivity.class);
String s = mStringAnswer.getText().toString();
if ( s.length() != 0 ) {
String[] sa = s.split(" ");
double gp[] = new double[4];
gp[0] = Double.valueOf(sa[0]).doubleValue();
gp[1] = Double.valueOf(sa[1]).doubleValue();
gp[2] = Double.valueOf(sa[2]).doubleValue();
gp[3] = Double.valueOf(sa[3]).doubleValue();
i.putExtra(LOCATION, gp);
}
i.putExtra(READ_ONLY, mReadOnly);
i.putExtra(ACCURACY_THRESHOLD, mAccuracyThreshold);
Collect.getInstance().getFormController()
.setIndexWaitingForData(mPrompt.getIndex());
((Activity) getContext()).startActivityForResult(i,
FormEntryActivity.LOCATION_CAPTURE);
}
});
// finish complex layout
// control what gets shown with setVisibility(View.GONE)
addView(mGetLocationButton);
addView(mAnswerDisplay);
// figure out what text and buttons to enable or to show...
boolean dataAvailable = false;
String s = prompt.getAnswerText();
if (s != null && !s.equals("")) {
dataAvailable = true;
setBinaryData(s);
}
updateButtonLabelsAndVisibility(dataAvailable);
}
private void updateButtonLabelsAndVisibility(boolean dataAvailable) {
// if it is read-only, hide the get-location button...
if ( mReadOnly ) {
mGetLocationButton.setVisibility(View.GONE);
} else {
mGetLocationButton.setVisibility(View.VISIBLE);
mGetLocationButton.setText(getContext()
.getString(dataAvailable ?
R.string.replace_location : R.string.get_location));
}
}
@Override
public void clearAnswer() {
mStringAnswer.setText(null);
mAnswerDisplay.setText(null);
updateButtonLabelsAndVisibility(false);
}
@Override
public IAnswerData getAnswer() {
String s = mStringAnswer.getText().toString();
if (s == null || s.equals("")) {
return null;
} else {
try {
// segment lat and lon
String[] sa = s.split(" ");
double gp[] = new double[4];
gp[0] = Double.valueOf(sa[0]).doubleValue();
gp[1] = Double.valueOf(sa[1]).doubleValue();
gp[2] = Double.valueOf(sa[2]).doubleValue();
gp[3] = Double.valueOf(sa[3]).doubleValue();
return new GeoPointData(gp);
} catch (Exception NumberFormatException) {
return null;
}
}
}
private String truncateDouble(String s) {
DecimalFormat df = new DecimalFormat("#.##");
return df.format(Double.valueOf(s));
}
private String formatGps(double coordinates, String type) {
String location = Double.toString(coordinates);
String degreeSign = "\u00B0";
String degree = location.substring(0, location.indexOf("."))
+ degreeSign;
location = "0." + location.substring(location.indexOf(".") + 1);
double temp = Double.valueOf(location) * 60;
location = Double.toString(temp);
String mins = location.substring(0, location.indexOf(".")) + "'";
location = "0." + location.substring(location.indexOf(".") + 1);
temp = Double.valueOf(location) * 60;
location = Double.toString(temp);
String secs = location.substring(0, location.indexOf(".")) + '"';
if (type.equalsIgnoreCase("lon")) {
if (degree.startsWith("-")) {
degree = "W " + degree.replace("-", "") + mins + secs;
} else
degree = "E " + degree.replace("-", "") + mins + secs;
} else {
if (degree.startsWith("-")) {
degree = "S " + degree.replace("-", "") + mins + secs;
} else
degree = "N " + degree.replace("-", "") + mins + secs;
}
return degree;
}
@Override
public void setFocus(Context context) {
// Hide the soft keyboard if it's showing.
InputMethodManager inputManager = (InputMethodManager) context
.getSystemService(Context.INPUT_METHOD_SERVICE);
inputManager.hideSoftInputFromWindow(this.getWindowToken(), 0);
}
@Override
public void setBinaryData(Object answer) {
String s = (String) answer;
mStringAnswer.setText(s);
String[] sa = s.split(" ");
mAnswerDisplay.setText(getContext().getString(R.string.latitude) + ": "
+ formatGps(Double.parseDouble(sa[0]), "lat") + "\n"
+ getContext().getString(R.string.longitude) + ": "
+ formatGps(Double.parseDouble(sa[1]), "lon") + "\n"
+ getContext().getString(R.string.altitude) + ": "
+ truncateDouble(sa[2]) + "m\n"
+ getContext().getString(R.string.accuracy) + ": "
+ truncateDouble(sa[3]) + "m");
Collect.getInstance().getFormController().setIndexWaitingForData(null);
updateButtonLabelsAndVisibility(true);
}
@Override
public boolean isWaitingForBinaryData() {
return mPrompt.getIndex().equals(
Collect.getInstance().getFormController()
.getIndexWaitingForData());
}
@Override
public void cancelWaitingForBinaryData() {
Collect.getInstance().getFormController().setIndexWaitingForData(null);
}
@Override
public void setOnLongClickListener(OnLongClickListener l) {
mGetLocationButton.setOnLongClickListener(l);
mStringAnswer.setOnLongClickListener(l);
mAnswerDisplay.setOnLongClickListener(l);
}
@Override
public void cancelLongPress() {
super.cancelLongPress();
mGetLocationButton.cancelLongPress();
mStringAnswer.cancelLongPress();
mAnswerDisplay.cancelLongPress();
}
}