package org.azavea.otm.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler.Callback;
import android.os.Message;
import android.provider.MediaStore;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.Toast;
import com.loopj.android.http.JsonHttpResponseHandler;
import org.azavea.helpers.Logger;
import org.azavea.otm.App;
import org.azavea.otm.R;
import org.azavea.otm.data.Plot;
import org.azavea.otm.fields.FieldGroup;
import org.azavea.otm.rest.RequestGenerator;
import org.azavea.otm.rest.handlers.LoggingJsonHttpResponseHandler;
import org.azavea.otm.rest.handlers.RestHandler;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import cz.msebera.android.httpclient.Header;
public class TreeEditDisplay extends TreeDisplay {
// Intent Request codes
public static final int FIELD_ACTIVITY_REQUEST_CODE = 0;
protected static final int TREE_MOVE = 2;
protected static final int PHOTO_USING_CAMERA_RESPONSE = 7;
protected static final int PHOTO_USING_GALLERY_RESPONSE = 8;
private ProgressDialog deleteDialog = null;
private ProgressDialog saveDialog = null;
private static String outputFilePath;
private final RestHandler<Plot> deleteTreeHandler = new RestHandler<Plot>(new Plot()) {
@Override
public void failure(Throwable e, String message) {
safeDismiss(deleteDialog);
Toast.makeText(App.getAppInstance(), "Unable to delete tree", Toast.LENGTH_SHORT).show();
Logger.error("Unable to delete tree.", e);
}
@Override
public void dataReceived(Plot response) {
safeDismiss(deleteDialog);
Toast.makeText(App.getAppInstance(), "The tree was deleted.", Toast.LENGTH_SHORT).show();
Intent resultIntent = new Intent();
// The tree was deleted, so return to the info page, and bring along
// the data for the new plot, which was the response from the
// delete operation
resultIntent.putExtra("plot", response.getData().toString());
setResult(RESULT_OK, resultIntent);
finish();
}
};
private final JsonHttpResponseHandler deletePlotHandler = new LoggingJsonHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
try {
if (response.getBoolean("ok")) {
safeDismiss(deleteDialog);
Toast.makeText(App.getAppInstance(), "The planting site was deleted.", Toast.LENGTH_SHORT).show();
setResult(RESULT_PLOT_DELETED);
finish();
}
} catch (JSONException e) {
Logger.error("Unable to delete plot", e);
safeDismiss(deleteDialog);
Toast.makeText(App.getAppInstance(), "Unable to delete plot", Toast.LENGTH_SHORT).show();
}
}
@Override
public void failure(Throwable e, String message) {
safeDismiss(deleteDialog);
Toast.makeText(App.getAppInstance(), "Unable to delete plot", Toast.LENGTH_SHORT).show();
}
};
private Bitmap newTreePhoto;
@Override
public void onCreate(Bundle savedInstanceState) {
mapFragmentId = R.id.vignette_map_edit_mode;
super.onCreate(savedInstanceState);
setContentView(R.layout.plot_edit_activity);
setUpMapIfNeeded();
initializeEditPage();
onMapLoad(map -> {
map.setOnMapClickListener(point -> {
Intent treeMoveIntent = new Intent(TreeEditDisplay.this, TreeMove.class);
treeMoveIntent.putExtra("plot", plot.getData().toString());
startActivityForResult(treeMoveIntent, TREE_MOVE);
});
});
}
@Override
public void onResume() {
super.onResume();
plotLocation = getPlotLocation(plot);
showPositionOnMap();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.tree_edit_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle presses on the action bar items
int id = item.getItemId();
if (id == R.id.plot_save_button) {
save();
return true;
} else if (id == R.id.edit_tree_picture) {
changeTreePhoto();
return true;
} else if (id == android.R.id.home) {
cancel();
return true;
}
return super.onOptionsItemSelected(item);
}
private void initializeEditPage() {
if (plot == null) {
finish();
}
LinearLayout fieldList = (LinearLayout) findViewById(R.id.field_list);
LayoutInflater layout = this.getLayoutInflater();
// Add all the fields to the display for edit mode
for (FieldGroup group : App.getFieldManager().getFieldGroups()) {
View fieldGroup = group.renderForEdit(layout, plot, TreeEditDisplay.this, fieldList);
if (fieldGroup != null) {
fieldList.addView(fieldGroup);
}
}
setupDeleteButtons(layout, fieldList);
}
/**
* Delete options for tree and plot are available under certain situations
* as reported from the /plot API endpoint as attributes of a plot/user
* combo. Don't give delete tree option if a tree isn't present
*/
private void setupDeleteButtons(LayoutInflater layout, LinearLayout fieldList) {
View actionPanel = layout.inflate(R.layout.plot_edit_delete_buttons, null);
actionPanel.findViewById(R.id.delete_plot).setVisibility(View.GONE);
actionPanel.findViewById(R.id.delete_tree).setVisibility(View.GONE);
fieldList.addView(actionPanel);
}
public void confirmDelete(int messageResource, final Callback callback) {
final Activity thisActivity = this;
new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle(R.string.confirm_delete)
.setMessage(messageResource).setPositiveButton(R.string.delete, (dialog, which) -> {
deleteDialog = ProgressDialog.show(thisActivity, "", "Deleting...", true);
Message resultMessage = new Message();
Bundle data = new Bundle();
data.putBoolean("confirm", true);
resultMessage.setData(data);
callback.handleMessage(resultMessage);
}).setNegativeButton(R.string.cancel, null).show();
}
public void deleteTree(View view) {
Callback confirm = msg -> {
if (msg.getData().getBoolean("confirm")) {
RequestGenerator rc = new RequestGenerator();
try {
rc.deleteCurrentTreeOnPlot(App.getAppInstance(), plot.getId(), deleteTreeHandler);
} catch (JSONException e) {
Logger.error("Error deleting tree", e);
}
}
return true;
};
confirmDelete(R.string.confirm_delete_tree_msg, confirm);
}
public void deletePlot(View view) {
Callback confirm = msg -> {
if (msg.getData().getBoolean("confirm")) {
RequestGenerator rc = new RequestGenerator();
try {
rc.deletePlot(App.getAppInstance(), plot.getId(), deletePlotHandler);
} catch (JSONException e) {
Logger.error("Error deleting tree plot", e);
}
}
return true;
};
confirmDelete(R.string.confirm_delete_plot_msg, confirm);
}
/**
* By default cancel() will finish the activity
*/
public void cancel() {
cancel(true);
}
/**
* Cancel the editing and return to the view profile, unchanged.
*
* @param doFinish - if the back button was pushed, finished will be called for
* you
*/
public void cancel(boolean doFinish) {
setResult(RESULT_CANCELED);
if (doFinish) {
finish();
}
}
private void save() {
saveDialog = ProgressDialog.show(this, "", "Saving...", true);
try {
for (FieldGroup group : App.getFieldManager().getFieldGroups()) {
group.update(plot);
}
RequestGenerator rg = new RequestGenerator();
RestHandler<Plot> responseHandler = new RestHandler<Plot>(new Plot()) {
@Override
public void dataReceived(Plot updatedPlot) {
// Tree was updated, check if a photo needs to be also added
savePhotoForPlot(updatedPlot);
}
@Override
public void failure(Throwable e, String responseBody) {
Logger.warning("Failure saving tree", e);
handleSaveFailure(e);
}
};
if (addMode()) {
rg.addPlot(plot, responseHandler);
} else {
if (App.getCurrentInstance().canEditTree()) {
rg.updatePlot(plot, responseHandler);
} else {
savePhotoForPlot(plot);
}
}
} catch (Exception e) {
handleSaveFailure(e);
}
}
private void safeDismiss(ProgressDialog dialog) {
if (dialog != null) {
dialog.dismiss();
}
}
private void handleSaveFailure (Throwable e) {
String msg = getString(R.string.save_tree_failure);
Logger.error(msg, e);
safeDismiss(saveDialog);
Toast.makeText(App.getAppInstance(), msg, Toast.LENGTH_SHORT).show();
}
private void handlePhotoSaveFailure (Throwable e) {
String msg = getString(R.string.save_tree_photo_failure);
Logger.error(msg, e);
safeDismiss(saveDialog);
Toast.makeText(App.getAppInstance(), msg, Toast.LENGTH_SHORT).show();
}
private void savePhotoForPlot(final Plot updatedPlot) {
if (this.newTreePhoto == null) {
doFinish(updatedPlot, saveDialog);
return;
}
if (!App.getCurrentInstance().canEditTreePhoto()) {
handlePhotoSaveFailure(null);
return;
}
RequestGenerator rc = new RequestGenerator();
try {
rc.addTreePhoto(updatedPlot, this.newTreePhoto, new LoggingJsonHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
try {
if (response.has("image")) {
updatedPlot.assignNewTreePhoto(response);
safeDismiss(saveDialog);
doFinish(updatedPlot, saveDialog);
} else {
handlePhotoSaveFailure(null);
}
} catch (JSONException e) {
handlePhotoSaveFailure(e);
}
}
@Override
public void failure(Throwable e, String errorResponse) {
handlePhotoSaveFailure(e);
}
});
} catch (JSONException e) {
handlePhotoSaveFailure(e);
}
}
private void doFinish(Plot updatedPlot, ProgressDialog saveDialog) {
safeDismiss(saveDialog);
setResultOk(updatedPlot);
// Updating may have changed the georev
App.getCurrentInstance().setGeoRevId(updatedPlot.getUpdatedGeoRev());
finish();
}
/**
* Is the intent in add tree mode?
*/
private boolean addMode() {
return (getIntent().getStringExtra("new_tree") != null) && getIntent().getStringExtra("new_tree").equals("1");
}
/**
* Set the result code to OK and set the updated plot as an intent extra
*
* @param updatedPlot
*/
private void setResultOk(Plot updatedPlot) {
Intent resultIntent = new Intent();
resultIntent.putExtra("plot", updatedPlot.getData().toString());
setResult(RESULT_OK, resultIntent);
}
@Override
public void onBackPressed() {
cancel(false);
super.onBackPressed();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
// In order to allow Fields to handle activity results themselves, share the activity
// result with all of the FieldGroups, which will dispatch to the appropriate fields
// based on the keys in the intent data
case FIELD_ACTIVITY_REQUEST_CODE:
if (resultCode == Activity.RESULT_OK) {
for (FieldGroup group : App.getFieldManager().getFieldGroups()) {
group.receiveActivityResult(resultCode, data, this);
}
}
break;
case TREE_MOVE:
if (resultCode == Activity.RESULT_OK) {
try {
plot.setData(new JSONObject(data.getStringExtra("plot")));
} catch (JSONException e) {
Logger.error(e);
}
plotLocation = getPlotLocation(plot);
showPositionOnMap();
}
break;
case PHOTO_USING_CAMERA_RESPONSE:
if (resultCode == RESULT_OK) {
changePhotoUsingCamera(outputFilePath);
}
break;
case PHOTO_USING_GALLERY_RESPONSE:
if (resultCode == RESULT_OK) {
changePhotoUsingGallery(data);
}
break;
}
}
/*
* Photo Editing Functions
*/
public void changeTreePhoto() {
Log.d("PHOTO", "changePhoto");
if (!App.getCurrentInstance().canEditTreePhoto()) {
Toast.makeText(getApplicationContext(), getString(R.string.perms_add_tree_photo_fail), Toast.LENGTH_SHORT).show();
return;
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setNegativeButton(R.string.use_camera, (dialog, id) -> {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File outputFile = PhotoActivity.createImageFile();
outputFilePath = outputFile.getAbsolutePath();
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(outputFile));
startActivityForResult(intent, PHOTO_USING_CAMERA_RESPONSE);
});
builder.setPositiveButton(R.string.use_gallery, (dialog, id) -> {
Intent intent = new Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, PHOTO_USING_GALLERY_RESPONSE);
});
AlertDialog alert = builder.create();
alert.show();
}
// This function is called at the end of the whole camera process. You might
// want to call your rc.submit method here, or store the bm in a class level
// variable.
protected void submitBitmap(Bitmap bm) {
this.newTreePhoto = bm;
}
protected void changePhotoUsingCamera(String filePath) {
Bitmap pic = PhotoActivity.getCorrectedCameraBitmap(filePath);
if (pic != null) {
submitBitmap(pic);
}
}
protected void changePhotoUsingGallery(Intent data) {
submitBitmap(PhotoActivity.getCorrectedGalleryBitmap(data));
}
}