package edu.mit.mitmobile2.facilities;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.InputStreamBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.ProgressDialog;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import edu.mit.mitmobile2.AttributesParser;
import edu.mit.mitmobile2.Global;
import edu.mit.mitmobile2.MobileWebApi;
import edu.mit.mitmobile2.NewModule;
import edu.mit.mitmobile2.NewModuleActivity;
import edu.mit.mitmobile2.R;
import edu.mit.mitmobile2.TwoLineActionRow;
public class FacilitiesDetailsActivity extends NewModuleActivity {
public static final String TAG = "FacilitiesProblemTypeActivity";
private static String ATTACH_PHOTO = "Attach Photo";
private static String CHANGE_PHOTO = "Change Photo";
private Context mContext;
private TextView problemStringTextView;
private EditText mProblemDescriptionEditText;
private TwoLineActionRow mAddAPhotoActionRow;
private EditText sendAsEditText;
private static final int CAMERA_PIC_REQUEST = 1;
private static final int PIC_SELECTION = 2;
private TwoLineActionRow submitActionRow;
private ImageView selectedImage;
private Uri mCapturedImageUri;
private Uri mSelectedImageUri;
private static final String CAPTURED_IMAGE_URL_KEY = "captured_image_url_key";
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// recover the image url used for captured photos.
if (savedInstanceState != null) {
String capturedImageUri = savedInstanceState.getString(CAPTURED_IMAGE_URL_KEY);
if (capturedImageUri != null) {
mCapturedImageUri = Uri.parse(capturedImageUri);
}
}
this.mContext = this;
createViews();
}
@Override
protected void onSaveInstanceState (Bundle outState) {
super.onSaveInstanceState(outState);
if (mCapturedImageUri != null) {
outState.putString(CAPTURED_IMAGE_URL_KEY, mCapturedImageUri.toString());
super.onSaveInstanceState(outState);
}
}
public void createViews() {
setContentView(R.layout.facilities_details);
// Set problem string
problemStringTextView = (TextView)findViewById(R.id.facilitiesProblemString);
String problemString = "I am reporting a problem";
String problemType = Global.sharedData.getFacilitiesData().getProblemType();
if(problemType == null) {
finish();
return;
}
if (problemType.equalsIgnoreCase("Other") == false) {
problemString += " with a " + problemType.toLowerCase();
}
if (Global.sharedData.getFacilitiesData().getUserAssignedLocationName() != null) {
problemString += " at \"" + Global.sharedData.getFacilitiesData().getUserAssignedLocationName() + "\"";
}
else if (Global.sharedData.getFacilitiesData().getUserAssignedRoomName() != null) {
problemString += " at " + Global.sharedData.getFacilitiesData().getBuildingNumber() + " near \"" + Global.sharedData.getFacilitiesData().getUserAssignedRoomName() + "\"";
}
else if (Global.sharedData.getFacilitiesData().getBuildingRoomName().equalsIgnoreCase("INSIDE")) {
problemString += " inside " + buildingNumberOrBuildingName();
}
else if (Global.sharedData.getFacilitiesData().getBuildingRoomName().equalsIgnoreCase("OUTSIDE")) {
problemString += " outside " + buildingNumberOrBuildingName();
}
else {
problemString += " at " + buildingNumberOrBuildingName() + " near " + Global.sharedData.getFacilitiesData().getBuildingRoomName();
}
problemString += ".";
TextWatcher textWatcher = new TextWatcher() {
@Override
public void afterTextChanged(Editable editable) {
boolean emailExists = sendAsEditText.getText().toString().trim().length() > 0;
boolean descriptionExists = mProblemDescriptionEditText.getText().toString().trim().length() > 0;
submitActionRow.setEnabled(emailExists && descriptionExists);
}
@Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {}
@Override
public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {}
};
problemStringTextView.setText(problemString);
mProblemDescriptionEditText = (EditText) findViewById(R.id.problemDescription);
mProblemDescriptionEditText.addTextChangedListener(textWatcher);
mProblemDescriptionEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if(!hasFocus) {
String problemText = mProblemDescriptionEditText.getText().toString();
mProblemDescriptionEditText.setText(problemText.trim());
}
}
});
initDescriptionPadding();
sendAsEditText = (EditText) findViewById(R.id.facilitiesSendAs);
sendAsEditText.addTextChangedListener(textWatcher);
sendAsEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if(!hasFocus) {
String sendAsText = sendAsEditText.getText().toString();
sendAsEditText.setText(sendAsText.trim());
}
}
});
// Add A Photo
mAddAPhotoActionRow = (TwoLineActionRow)findViewById(R.id.facilitiesAddAPhotoActionRow);
mAddAPhotoActionRow.setTitle(ATTACH_PHOTO);
mAddAPhotoActionRow.setActionIconResource(R.drawable.photoopp);
mAddAPhotoActionRow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
addOrChangePhoto();
}
});
// selected Image
selectedImage = (ImageView)findViewById(R.id.selectedImage);
selectedImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
addOrChangePhoto();
}
});
// Submit form
submitActionRow = (TwoLineActionRow)findViewById(R.id.facilitiesSubmitActionRow);
submitActionRow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
submitForm();
}
});
submitActionRow.setEnabled(false);
submitActionRow.setFocusableInTouchMode(true);
submitActionRow.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(submitActionRow.getWindowToken(), 0);
}
}
});
}
private String buildingNumberOrBuildingName() {
if(Global.sharedData.getFacilitiesData().getBuildingNumber() != null) {
return Global.sharedData.getFacilitiesData().getBuildingNumber();
} else {
return Global.sharedData.getFacilitiesData().getLocationName();
}
}
private void addOrChangePhoto() {
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setTitle("Choose Action");
String[] choices;
final int takePhoto = 0;
final int pickExistingPhoto = 1;
final int detachPhoto = 2;
if(mSelectedImageUri != null) {
choices = new String[] {"Take a Photo", "Pick Existing Photo", "Detach Photo"};
} else {
choices = new String[] {"Take a Photo", "Pick Existing Photo"};
}
builder.setItems(choices, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case takePhoto:
takePhoto();
break;
case pickExistingPhoto:
pickExistingPhoto();
break;
case detachPhoto:
detachPhoto();
break;
}
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
int mPaddingLeft;
int mPaddingTop;
int mPaddingRightNoPicture;
int mPaddingRightPicture;
int mPaddingBottom;
private void initDescriptionPadding() {
mPaddingLeft = mProblemDescriptionEditText.getPaddingLeft();
mPaddingTop = mProblemDescriptionEditText.getPaddingTop();
mPaddingRightNoPicture = mProblemDescriptionEditText.getPaddingRight();
mPaddingRightPicture = AttributesParser.parseDimension("104dip", mContext);
mPaddingBottom = mProblemDescriptionEditText.getPaddingBottom();
}
private void takePhoto() {
ContentValues values = new ContentValues();
mCapturedImageUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // Normally you would populate this with your custom intent.
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCapturedImageUri);
startActivityForResult(cameraIntent, CAMERA_PIC_REQUEST);
}
private void pickExistingPhoto() {
Intent choosePhoto = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI);
startActivityForResult(choosePhoto, PIC_SELECTION);
}
private void detachPhoto() {
selectedImage = (ImageView)findViewById(R.id.selectedImage);
selectedImage.setVisibility(View.GONE);
selectedImage.setImageBitmap(null);
mProblemDescriptionEditText.setPadding(mPaddingLeft, mPaddingTop, mPaddingRightNoPicture, mPaddingBottom);
mSelectedImageUri = null;
mAddAPhotoActionRow.setTitle(ATTACH_PHOTO);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
if (requestCode == CAMERA_PIC_REQUEST) {
mSelectedImageUri = mCapturedImageUri;
} else if(requestCode == PIC_SELECTION) {
mSelectedImageUri = data.getData();
}
mAddAPhotoActionRow.setTitle(CHANGE_PHOTO);
selectedImage.setVisibility(View.VISIBLE);
mProblemDescriptionEditText.setPadding(mPaddingLeft, mPaddingTop, mPaddingRightPicture, mPaddingBottom);
long imageId = ContentUris.parseId(mSelectedImageUri);
Bitmap thumbnail = MediaStore.Images.Thumbnails.getThumbnail(getContentResolver(), imageId, MediaStore.Images.Thumbnails.MINI_KIND, null);
selectedImage.setImageBitmap(thumbnail);
}
}
@Override
public boolean isModuleHomeActivity() {
return false;
}
void submitForm() {
FileUploader fileUploader = new FileUploader();
fileUploader.execute();
}
private class FileUploader extends AsyncTask<Void, Long, Boolean> implements FileUploadListener {
ProgressDialog mProgressDialog;
long mMaxBytes;
String mEmail;
String mProblemDescription;
CountingMultipartEntity mUploadEntity;
@Override
protected void onPreExecute() {
mProblemDescription = mProblemDescriptionEditText.getText().toString().trim();
mEmail = sendAsEditText.getText().toString().trim();
if(mProblemDescription.length() == 0 || mEmail.length() == 0) {
Builder builder = new AlertDialog.Builder(mContext);
builder.setMessage("Email and a description of the problem is required.");
builder.setNeutralButton("Okay", null);
AlertDialog dialog = builder.create();
dialog.show();
FileUploader.this.cancel(true);
return;
}
mUploadEntity = new CountingMultipartEntity(this);
mProgressDialog = new ProgressDialog(mContext);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setMessage("Uploading Data");
mProgressDialog.setIndeterminate(false);
mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
mUploadEntity.cancel();
FileUploader.this.cancel(true);
}
});
mProgressDialog.show();
}
@Override
protected Boolean doInBackground(Void... unusedArgs) {
try {
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost("http://" + Global.getMobileWebDomain() + "/api/?module=facilities&command=upload");
mMaxBytes = 500;
if(mSelectedImageUri != null) {
InputStream imageStream = getContentResolver().openInputStream(mSelectedImageUri);
byte[] imageData = IOUtils.toByteArray(imageStream);
mMaxBytes += imageData.length; // this an approximation of the total bytes to be transferred
InputStreamBody imageStreamBody = new InputStreamBody(new ByteArrayInputStream(imageData), "image/jpeg", "image");
mUploadEntity.addPart("image", imageStreamBody);
}
addField("email", mEmail);
addField("message", mProblemDescription);
addField("location", Global.sharedData.getFacilitiesData().getLocationId());
addField("locationName", Global.sharedData.getFacilitiesData().getLocationName());
addField("locationNameByUser", Global.sharedData.getFacilitiesData().getUserAssignedLocationName());
addField("buildingNumber", Global.sharedData.getFacilitiesData().getBuildingNumber());
addField("roomName", Global.sharedData.getFacilitiesData().getBuildingRoomName());
addField("roomNameByUser", Global.sharedData.getFacilitiesData().getUserAssignedRoomName());
addField("problemType", Global.sharedData.getFacilitiesData().getProblemType());
publishProgress( Long.valueOf(0)); // initialize the progress bar
httpPost.setEntity(mUploadEntity);
HttpResponse response;
response = httpClient.execute(httpPost);
String responseText = MobileWebApi.convertStreamToString(response.getEntity().getContent());
JSONObject responseObject = new JSONObject(responseText);
return responseObject.getBoolean("success");
} catch (FileNotFoundException fileException) {
fileException.printStackTrace();
} catch (IOException ioException) {
ioException.printStackTrace();
} catch(JSONException jsonException) {
jsonException.printStackTrace();
}
return false;
}
private void addField(String fieldName, String fieldValue) throws UnsupportedEncodingException {
if(fieldValue != null) {
mUploadEntity.addPart(fieldName, new StringBody(fieldValue));
}
}
@Override
protected void onPostExecute(Boolean success) {
mProgressDialog.dismiss();
if(success) {
Intent intent = new Intent(mContext, FacilitiesUploadSuccessModuleActivity.class);
mContext.startActivity(intent);
} else {
Toast.makeText(mContext, MobileWebApi.NETWORK_ERROR, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onBytesTransfered(long transferred) {
publishProgress(transferred);
}
@Override
public void onProgressUpdate(Long... progress) {
long progressBytes = progress[0];
if(progressBytes > mMaxBytes) {
// this a hack to account for the fact
// that maxKiloBytes is just an approximation
progressBytes = mMaxBytes;
}
if(mMaxBytes > 10000) {
// use kilobytes
int maxKiloBytes = (int)(mMaxBytes / 1000);
mProgressDialog.setMax(maxKiloBytes);
int progressKiloBytes = (int)(progress[0] / 1000);
mProgressDialog.setProgress(progressKiloBytes);
} else {
mProgressDialog.setMax((int)mMaxBytes);
mProgressDialog.setProgress((int)progressBytes);
}
}
}
/*
* methods and classes to hook into count how many bytes of the image have been uploaded
*/
private interface FileUploadListener {
void onBytesTransfered(long transferred);
}
private class CountingMultipartEntity extends MultipartEntity {
FileUploadListener mFileUploadListener;
CountingOutputStream mCountingOutputStream;
CountingMultipartEntity(FileUploadListener fileUploadListener) {
mFileUploadListener = fileUploadListener;
}
@Override
public void writeTo(final OutputStream outstream) throws IOException {
mCountingOutputStream = new CountingOutputStream(outstream, mFileUploadListener);
super.writeTo(mCountingOutputStream);
}
public void cancel() {
mCountingOutputStream.cancel();
}
}
public static class CountingOutputStream extends FilterOutputStream {
private long mTransferred;
FileUploadListener mFileUploadListener;
CountingMultipartEntity mUploadEntity;
boolean isCancelled = false;
public CountingOutputStream(final OutputStream out, FileUploadListener fileUploadListener) {
super(out);
mTransferred = 0;
mFileUploadListener = fileUploadListener;
}
public void cancel() {
isCancelled = true;
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
if(isCancelled) {
throw new IOException("Upload was cancelled");
}
out.write(b, off, len);
mTransferred += len;
mFileUploadListener.onBytesTransfered(mTransferred);
}
@Override
public void write(int b) throws IOException {
if(isCancelled) {
throw new IOException("Upload was cancelled");
}
out.write(b);
mTransferred++;
mFileUploadListener.onBytesTransfered(mTransferred);
}
}
@Override
protected NewModule getNewModule() {
// TODO Auto-generated method stub
return new FacilitiesModule();
}
@Override
protected boolean isScrollable() {
// TODO Auto-generated method stub
return false;
}
@Override
protected void onOptionSelected(String optionId) {
// TODO Auto-generated method stub
}
}