Java tutorial
/* * Copyright (C) 2012 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.ActivityNotFoundException; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.graphics.Rect; import android.net.Uri; import android.provider.MediaStore.Images; import android.support.v4.content.FileProvider; import android.util.Log; import android.util.TypedValue; import android.view.Display; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.webkit.WebSettings; import android.webkit.WebView; import android.widget.*; import org.javarosa.core.model.data.IAnswerData; import org.javarosa.core.model.data.StringData; import org.javarosa.form.api.FormEntryPrompt; import org.odk.collect.android.BuildConfig; import org.odk.collect.android.R; import org.odk.collect.android.activities.FormEntryActivity; import org.odk.collect.android.application.Collect; import org.odk.collect.android.utilities.MediaUtils; import java.io.File; import java.util.Date; /** * Widget that allows user to take pictures, sounds or video and add them to the * form. * * @author Carl Hartung (carlhartung@gmail.com) * @author Yaw Anokwa (yanokwa@gmail.com) */ public class ImageWebViewWidget extends QuestionWidget implements IBinaryWidget { private final static String t = "MediaWidget"; private Button mCaptureButton; private Button mChooseButton; private WebView mImageDisplay; private String mBinaryName; private String mInstanceFolder; private TextView mErrorTextView; public ImageWebViewWidget(Context context, FormEntryPrompt prompt) { super(context, prompt); mInstanceFolder = Collect.getInstance().getFormController().getInstancePath().getParent(); TableLayout.LayoutParams params = new TableLayout.LayoutParams(); params.setMargins(7, 5, 7, 5); mErrorTextView = new TextView(context); mErrorTextView.setId(QuestionWidget.newUniqueId()); mErrorTextView.setText("Selected file is not a valid image"); // setup capture button mCaptureButton = new Button(getContext()); mCaptureButton.setId(QuestionWidget.newUniqueId()); mCaptureButton.setText(getContext().getString(R.string.capture_image)); mCaptureButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, mAnswerFontsize); mCaptureButton.setPadding(20, 20, 20, 20); mCaptureButton.setEnabled(!prompt.isReadOnly()); mCaptureButton.setLayoutParams(params); // launch capture intent on click mCaptureButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Collect.getInstance().getActivityLogger().logInstanceAction(this, "captureButton", "click", mPrompt.getIndex()); mErrorTextView.setVisibility(View.GONE); Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); // We give the camera an absolute filename/path where to put the // picture because of bug: // http://code.google.com/p/android/issues/detail?id=1480 // The bug appears to be fixed in Android 2.0+, but as of feb 2, // 2010, G1 phones only run 1.6. Without specifying the path the // images returned by the camera in 1.6 (and earlier) are ~1/4 // the size. boo. // if this gets modified, the onActivityResult in // FormEntyActivity will also need to be updated. Uri tempPath = FileProvider.getUriForFile(getContext(), BuildConfig.APPLICATION_ID + ".provider", new File(Collect.TMPFILE_PATH)); i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, tempPath); try { Collect.getInstance().getFormController().setIndexWaitingForData(mPrompt.getIndex()); ((Activity) getContext()).startActivityForResult(i, FormEntryActivity.IMAGE_CAPTURE); } catch (ActivityNotFoundException e) { Toast.makeText(getContext(), getContext().getString(R.string.activity_not_found, "image capture"), Toast.LENGTH_SHORT).show(); Collect.getInstance().getFormController().setIndexWaitingForData(null); } } }); // setup chooser button mChooseButton = new Button(getContext()); mChooseButton.setId(QuestionWidget.newUniqueId()); mChooseButton.setText(getContext().getString(R.string.choose_image)); mChooseButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, mAnswerFontsize); mChooseButton.setPadding(20, 20, 20, 20); mChooseButton.setEnabled(!prompt.isReadOnly()); mChooseButton.setLayoutParams(params); // launch capture intent on click mChooseButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Collect.getInstance().getActivityLogger().logInstanceAction(this, "chooseButton", "click", mPrompt.getIndex()); mErrorTextView.setVisibility(View.GONE); Intent i = new Intent(Intent.ACTION_GET_CONTENT); i.setType("image/*"); try { Collect.getInstance().getFormController().setIndexWaitingForData(mPrompt.getIndex()); ((Activity) getContext()).startActivityForResult(i, FormEntryActivity.IMAGE_CHOOSER); } catch (ActivityNotFoundException e) { Toast.makeText(getContext(), getContext().getString(R.string.activity_not_found, "choose image"), Toast.LENGTH_SHORT) .show(); Collect.getInstance().getFormController().setIndexWaitingForData(null); } } }); // finish complex layout LinearLayout answerLayout = new LinearLayout(getContext()); answerLayout.setOrientation(LinearLayout.VERTICAL); answerLayout.addView(mCaptureButton); answerLayout.addView(mChooseButton); answerLayout.addView(mErrorTextView); // and hide the capture and choose button if read-only if (prompt.isReadOnly()) { mCaptureButton.setVisibility(View.GONE); mChooseButton.setVisibility(View.GONE); } mErrorTextView.setVisibility(View.GONE); // retrieve answer from data model and update ui mBinaryName = prompt.getAnswerText(); // Only add the imageView if the user has taken a picture if (mBinaryName != null) { mImageDisplay = new WebView(getContext()); mImageDisplay.setId(QuestionWidget.newUniqueId()); mImageDisplay.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); mImageDisplay.getSettings().setBuiltInZoomControls(true); mImageDisplay.getSettings().setDefaultZoom(WebSettings.ZoomDensity.FAR); mImageDisplay.setVisibility(View.VISIBLE); mImageDisplay.setLayoutParams(params); // HTML is used to display the image. String html = "<body>" + constructImageElement() + "</body>"; mImageDisplay.loadDataWithBaseURL("file:///" + mInstanceFolder + File.separator, html, "text/html", "utf-8", ""); answerLayout.addView(mImageDisplay); } addAnswerView(answerLayout); } private String constructImageElement() { File f = new File(mInstanceFolder + File.separator + mBinaryName); Display display = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay(); int screenWidth = display.getWidth(); // int screenHeight = display.getHeight(); String imgElement = f.exists() ? ("<img align=\"middle\" src=\"file:///" + f.getAbsolutePath() + // Appending the time stamp to the filename is a hack to prevent // caching. "?" + new Date().getTime() + "\" width=\"" + Integer.toString(screenWidth - 10) + "\" >") : ""; return imgElement; } public boolean suppressFlingGesture(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { if (mImageDisplay == null || mImageDisplay.getVisibility() != View.VISIBLE) { return false; } Rect rect = new Rect(); mImageDisplay.getHitRect(rect); // Log.i(t, "hitRect: " + rect.left + "," + rect.top + " : " + // rect.right + "," + rect.bottom ); // Log.i(t, "e1 Raw, Clean: " + e1.getRawX() + "," + e1.getRawY() + // " : " + e1.getX() + "," + e1.getY()); // Log.i(t, "e2 Raw, Clean: " + e2.getRawX() + "," + e2.getRawY() + // " : " + e2.getX() + "," + e2.getY()); // starts in WebView if (rect.contains((int) e1.getRawX(), (int) e1.getRawY())) { return true; } // ends in WebView if (rect.contains((int) e2.getRawX(), (int) e2.getRawY())) { return true; } // transits WebView if (rect.contains((int) ((e1.getRawX() + e2.getRawX()) / 2.0), (int) ((e1.getRawY() + e2.getRawY()) / 2.0))) { return true; } // Log.i(t, "NOT SUPPRESSED"); return false; } private void deleteMedia() { // get the file path and delete the file String name = mBinaryName; // clean up variables mBinaryName = null; // delete from media provider int del = MediaUtils.deleteImageFileFromMediaProvider(mInstanceFolder + File.separator + name); Log.i(t, "Deleted " + del + " rows from media content provider"); } @Override public void clearAnswer() { // remove the file deleteMedia(); if (mImageDisplay != null) { // update HTML to not hold image file reference. String html = "<body></body>"; mImageDisplay.loadDataWithBaseURL("file:///" + mInstanceFolder + File.separator, html, "text/html", "utf-8", ""); mImageDisplay.setVisibility(View.INVISIBLE); } mErrorTextView.setVisibility(View.GONE); // reset buttons mCaptureButton.setText(getContext().getString(R.string.capture_image)); } @Override public IAnswerData getAnswer() { if (mBinaryName != null) { return new StringData(mBinaryName.toString()); } else { return null; } } @Override public void setBinaryData(Object newImageObj) { // you are replacing an answer. delete the previous image using the // content provider. if (mBinaryName != null) { deleteMedia(); } File newImage = (File) newImageObj; if (newImage.exists()) { // Add the new image to the Media content provider so that the // viewing is fast in Android 2.0+ ContentValues values = new ContentValues(6); values.put(Images.Media.TITLE, newImage.getName()); values.put(Images.Media.DISPLAY_NAME, newImage.getName()); values.put(Images.Media.DATE_TAKEN, System.currentTimeMillis()); values.put(Images.Media.MIME_TYPE, "image/jpeg"); values.put(Images.Media.DATA, newImage.getAbsolutePath()); Uri imageURI = getContext().getContentResolver().insert(Images.Media.EXTERNAL_CONTENT_URI, values); Log.i(t, "Inserting image returned uri = " + imageURI.toString()); mBinaryName = newImage.getName(); Log.i(t, "Setting current answer to " + newImage.getName()); } else { Log.e(t, "NO IMAGE EXISTS at: " + newImage.getAbsolutePath()); } Collect.getInstance().getFormController().setIndexWaitingForData(null); } @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 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) { mCaptureButton.setOnLongClickListener(l); mChooseButton.setOnLongClickListener(l); } @Override public void cancelLongPress() { super.cancelLongPress(); mCaptureButton.cancelLongPress(); mChooseButton.cancelLongPress(); } }