package org.commcare.activities; import android.app.Activity; import android.content.Context; import android.content.pm.ActivityInfo; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.view.Display; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.Button; import android.widget.LinearLayout; import android.widget.RelativeLayout; import org.commcare.dalvik.R; import org.commcare.models.ODKStorage; import org.commcare.utils.FileUtil; import org.commcare.utils.MediaUtil; import org.commcare.utils.StringUtils; import org.commcare.views.dialogs.DialogChoiceItem; import org.commcare.views.dialogs.PaneledChoiceDialog; import org.commcare.views.widgets.ImageWidget; import org.commcare.views.widgets.SignatureWidget; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; /** * Modified from the FingerPaint example found in The Android Open Source * Project. * * @author BehrAtherton@gmail.com */ public class DrawActivity extends Activity { private static final String t = "DrawActivity"; public static final String OPTION = "option"; public static final String OPTION_SIGNATURE = "signature"; private static final String OPTION_ANNOTATE = "annotate"; private static final String OPTION_DRAW = "draw"; public static final String REF_IMAGE = "refImage"; public static final String EXTRA_OUTPUT = android.provider.MediaStore.EXTRA_OUTPUT; private static final String SAVEPOINT_IMAGE = "savepointImage"; // during // restore // incoming options... private String loadOption = null; private File refImage = null; private File output = null; private File savepointImage = null; private DrawView drawView; private String alertTitleString; @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); try { saveFile(savepointImage); } catch (FileNotFoundException e) { e.printStackTrace(); } if (savepointImage.exists()) { outState.putString(SAVEPOINT_IMAGE, savepointImage.getAbsolutePath()); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); Bundle extras = getIntent().getExtras(); if (extras == null) { loadOption = OPTION_DRAW; refImage = null; savepointImage = SignatureWidget.getTempFileForDrawingCapture(); savepointImage.delete(); output = ImageWidget.getTempFileForImageCapture(); } else { loadOption = extras.getString(OPTION); if (loadOption == null) { loadOption = OPTION_DRAW; } // refImage can also be present if resuming a drawing Uri uri = (Uri)extras.get(REF_IMAGE); if (uri != null) { refImage = new File(uri.getPath()); } String savepoint = extras.getString(SAVEPOINT_IMAGE); if (savepoint != null) { savepointImage = new File(savepoint); if (!savepointImage.exists() && refImage != null && refImage.exists()) { try { FileUtil.copyFile(refImage, savepointImage); }catch (IOException e) { Log.e(t, "IOExeception copying drawn image."); e.printStackTrace(); } } } else { savepointImage = SignatureWidget.getTempFileForDrawingCapture(); savepointImage.delete(); if (refImage != null && refImage.exists()) { try { FileUtil.copyFile(refImage, savepointImage); } catch (IOException e) { Log.e(t, "IOExeception copying drawn image"); e.printStackTrace(); } } } //sets where the result will be saved to uri = (Uri)extras.get(EXTRA_OUTPUT); if (uri != null) { output = new File(uri.getPath()); } else { output = ImageWidget.getTempFileForImageCapture(); } } // At this point, we have: // loadOption -- type of activity (draw, signature, annotate) // refImage -- original image to work with // savepointImage -- drawing to use as a starting point (may be copy of // original) // output -- where the output should be written if (OPTION_SIGNATURE.equals(loadOption)) { // set landscape setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); alertTitleString = getString(R.string.quit_application, getString(R.string.sign_button)); } else if (OPTION_ANNOTATE.equals(loadOption)) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); alertTitleString = getString(R.string.quit_application, getString(R.string.markup_image)); } else { alertTitleString = getString(R.string.quit_application, getString(R.string.draw_image)); } setTitle(getString(R.string.application_name) + " > " + getString(R.string.draw_image)); LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); RelativeLayout v = (RelativeLayout)inflater.inflate( R.layout.draw_layout, null); LinearLayout ll = (LinearLayout)v.findViewById(R.id.drawViewLayout); Paint paint = new Paint(); paint.setAntiAlias(true); paint.setDither(true); paint.setStyle(Paint.Style.STROKE); paint.setStrokeJoin(Paint.Join.ROUND); paint.setStrokeWidth(10); paint.setColor(Color.BLACK); Paint pointPaint = new Paint(); pointPaint.setAntiAlias(true); pointPaint.setDither(true); pointPaint.setStyle(Paint.Style.FILL_AND_STROKE); pointPaint.setStrokeWidth(10); pointPaint.setColor(Color.BLACK); drawView = new DrawView(this, OPTION_SIGNATURE.equals(loadOption), savepointImage, paint, pointPaint); ll.addView(drawView); setContentView(v); Button btnFinished = (Button)findViewById(R.id.btnFinishDraw); btnFinished.setText(StringUtils.getStringRobust(this, R.string.save_and_close)); btnFinished.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { saveAndClose(); } }); Button btnReset = (Button)findViewById(R.id.btnResetDraw); btnReset.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { reset(); } }); btnReset.setText(StringUtils.getStringRobust(this, R.string.reset_image)); Button btnCancel = (Button)findViewById(R.id.btnCancelDraw); btnCancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { cancelAndClose(); } }); btnCancel.setText(StringUtils.getStringRobust(this, R.string.cancel)); } private void saveAndClose() { try { saveFile(output); setResult(Activity.RESULT_OK); } catch (FileNotFoundException e) { e.printStackTrace(); setResult(Activity.RESULT_CANCELED); } this.finish(); } private void saveFile(File f) throws FileNotFoundException { if (drawView.getWidth() == 0 || drawView.getHeight() == 0) { // apparently on 4.x, the orientation change notification can occur // sometime before the view is rendered. In that case, the view // dimensions will not be known. Log.e(t, "view has zero width or zero height"); } else { FileOutputStream fos; fos = new FileOutputStream(f); Bitmap bitmap = Bitmap.createBitmap(drawView.getWidth(), drawView.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); drawView.draw(canvas); bitmap.compress(Bitmap.CompressFormat.JPEG, 70, fos); try { fos.flush(); fos.close(); } catch (Exception e) { } } } private void reset() { savepointImage.delete(); if (!OPTION_SIGNATURE.equals(loadOption) && refImage != null && refImage.exists()) { try { FileUtil.copyFile(refImage, savepointImage); }catch (IOException e) { Log.e(t, "IOExeception while video audio"); e.printStackTrace(); } } drawView.reset(); drawView.invalidate(); } private void cancelAndClose() { setResult(Activity.RESULT_CANCELED); this.finish(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: createQuitDrawDialog(); return true; case KeyEvent.KEYCODE_DPAD_RIGHT: if (event.isAltPressed()) { createQuitDrawDialog(); return true; } break; case KeyEvent.KEYCODE_DPAD_LEFT: if (event.isAltPressed()) { createQuitDrawDialog(); return true; } break; } return super.onKeyDown(keyCode, event); } /** * Create a dialog with options to save and exit, save, or quit without * saving */ private void createQuitDrawDialog() { final PaneledChoiceDialog dialog = new PaneledChoiceDialog(this, alertTitleString); View.OnClickListener keepChangesListener = new View.OnClickListener() { @Override public void onClick(View v) { saveAndClose(); } }; DialogChoiceItem keepOption = new DialogChoiceItem(getString(R.string.keep_changes), -1, keepChangesListener); View.OnClickListener discardChangesListener = new View.OnClickListener() { @Override public void onClick(View v) { cancelAndClose(); } }; DialogChoiceItem discardOption = new DialogChoiceItem(getString(R.string.do_not_save), -1, discardChangesListener); dialog.setChoiceItems(new DialogChoiceItem[]{keepOption, discardOption}); dialog.addButton(getString(R.string.cancel), new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); } }); dialog.showNonPersistentDialog(); } private static class DrawView extends View { private boolean isSignature; private Bitmap mBitmap; private Canvas mCanvas; private final Path mCurrentPath; private final Paint mBitmapPaint; private File mBackgroundBitmapFile; private final Paint paint; private final Paint pointPaint; private float mX, mY; public DrawView(final Context c, Paint paint, Paint pointPaint) { super(c); this.paint = paint; this.pointPaint = pointPaint; isSignature = false; mBitmapPaint = new Paint(Paint.DITHER_FLAG); mCurrentPath = new Path(); setBackgroundColor(0xFFFFFFFF); mBackgroundBitmapFile = SignatureWidget.getTempFileForDrawingCapture(); } public DrawView(Context c, boolean isSignature, File f, Paint paint, Paint pointPaint) { this(c, paint, pointPaint); this.isSignature = isSignature; mBackgroundBitmapFile = f; } public void reset() { Display display = ((WindowManager)getContext().getSystemService( Context.WINDOW_SERVICE)).getDefaultDisplay(); int screenWidth = display.getWidth(); int screenHeight = display.getHeight(); resetImage(screenWidth, screenHeight); } public void resetImage(int w, int h) { if (mBackgroundBitmapFile.exists()) { mBitmap = MediaUtil.getBitmapScaledToContainer( mBackgroundBitmapFile, w, h).copy( Bitmap.Config.ARGB_8888, true); // mBitmap = // Bitmap.createScaledBitmap(BitmapFactory.decodeFile(mBackgroundBitmapFile.getPath()), // w, h, true); mCanvas = new Canvas(mBitmap); } else { mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); mCanvas = new Canvas(mBitmap); mCanvas.drawColor(0xFFFFFFFF); if (isSignature) { drawSignLine(); } } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); resetImage(w, h); } @Override protected void onDraw(Canvas canvas) { canvas.drawColor(getResources().getColor(R.color.grey)); canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint); canvas.drawPath(mCurrentPath, paint); } private void touch_start(float x, float y) { mCurrentPath.reset(); mCurrentPath.moveTo(x, y); mX = x; mY = y; } public void drawSignLine() { mCanvas.drawLine(0, (int)(mCanvas.getHeight() * .7), mCanvas.getWidth(), (int)(mCanvas.getHeight() * .7), paint); } private void touch_move(float x, float y) { mCurrentPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2); mX = x; mY = y; } private void touch_up() { if (mCurrentPath.isEmpty()) { mCanvas.drawPoint(mX, mY, pointPaint); } else { mCurrentPath.lineTo(mX, mY); // commit the path to our offscreen mCanvas.drawPath(mCurrentPath, paint); } // kill this so we don't double draw mCurrentPath.reset(); } @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: touch_start(x, y); invalidate(); break; case MotionEvent.ACTION_MOVE: touch_move(x, y); invalidate(); break; case MotionEvent.ACTION_UP: touch_up(); invalidate(); break; } return true; } } }