package org.linnaeus.activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.*;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.*;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import org.linnaeus.AppPreferences;
import org.linnaeus.ShakeManager;
import org.linnaeus.actions.*;
import org.linnaeus.actions.AttachImageAction;
import org.linnaeus.bean.PaintAction;
import org.linnaeus.dialog.ColorPickerDialog;
import org.linnaeus.drawing.*;
import org.linnaeus.utils.FileUtils;
import org.linnaeus.utils.ImageUtils;
import org.linnaeus.utils.WarningAlert;
import org.openintents.about.AboutActivity;
import org.openintents.colorpicker.ColorPickerActivity;
import org.openintents.filemanager.FileManagerActivity;
import org.openintents.intents.AboutIntents;
import org.openintents.intents.ColorPickerIntents;
import org.openintents.intents.FileManagerIntents;
import java.io.File;
import java.util.ArrayList;
import java.util.Random;
import java.util.Stack;
public class PaintAreaActivity extends GraphicsActivity
implements MenuIdCollection,
ColorPickerDialog.OnColorChangedListener,
ShakeManager.ShakeEventListener {
private static final int HISTORY_CAPACITY = 10;
private static final String PREFERENCE_COLOR = "color";
private static final String PREFERENCE_BRUSH_STYLE = "brush_style";
private static final String PREFERENCE_CHILD_MODE = "child_mode";
private static final String PREFERENCE_BRUSH_WIDTH = "brush_width";
private PaintView _paintView;
private ShakeManager _shakeManager;
private AppPreferences _preferences;
private Stack<PaintAction> _paintActions;
private int _currentAction;
private int _brushStyle;
ImageButton _btnColor;
ImageButton _btnChildMode;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
_paintActions = new Stack<PaintAction>();
_preferences = AppPreferences.getAppPreferences(this);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(12);
mPaint.setColor(0xFFFF0000);
mEmboss = new EmbossMaskFilter(new float[]{1, 1, 1}, 0.4f, 6, 3.5f);
mBlur = new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL);
initPaintViewLayout();
registerForContextMenu(_paintView);
_shakeManager = new ShakeManager(this);
_shakeManager.addListener(this);
if (_preferences.isSaveStateOnExit()) {
new RestorePaintViewAction().doAction(this, _paintView);
}
Bitmap bitmap = _paintView.getDrawableBitmapCopy();
_paintActions.push(new PaintAction(bitmap));
_currentAction = _paintActions.size();
}
private void initPaintViewLayout() {
setContentView(R.layout.paint_area);
LinearLayout layout = (LinearLayout) findViewById(R.id.paint_view_layout);
_paintView = new PaintView(this);
layout.addView(_paintView);
_btnColor = (ImageButton) findViewById(R.id.btn_color);
colorChanged(mPaint.getColor()); // Update color icon.
_btnColor.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(PaintAreaActivity.this, ColorPickerActivity.class);
startActivityForResult(intent, COLOR_MENU_ID);
}
});
ImageButton btnBrush = (ImageButton) findViewById(R.id.btn_brush);
btnBrush.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onBrushMenuItemSelected();
}
});
ImageButton btnUndo = (ImageButton) findViewById(R.id.btn_undo);
btnUndo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
redo();
}
});
ImageButton btnRedo = (ImageButton) findViewById(R.id.btn_redo);
BitmapDrawable bmd = ImageUtils.rotate(this, R.drawable.ic_menu_revert);
btnRedo.setImageDrawable(bmd);
btnRedo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
undo();
}
});
_btnChildMode = (ImageButton) findViewById(R.id.btn_child);
_btnChildMode.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
_btnChildMode.setSelected(!_btnChildMode.isSelected());
}
});
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
if (_preferences.isSaveStateOnExit()) {
new SavePaintViewAction().doAction(this, _paintView);
}
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
//new RestorePaintViewAction().doAction(this, _paintView);
}
@Override
protected void onPause() {
super.onPause();
if (_shakeManager != null) {
_shakeManager.onPause();
}
if (_preferences.isSaveStateOnExit()) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor edit = prefs.edit();
edit.putInt(PREFERENCE_BRUSH_STYLE, _brushStyle);
edit.putFloat(PREFERENCE_BRUSH_WIDTH, mPaint.getStrokeWidth());
edit.putInt(PREFERENCE_COLOR, mPaint.getColor());
edit.putBoolean(PREFERENCE_CHILD_MODE, _btnChildMode.isSelected());
edit.commit();
}
}
@Override
protected void onResume() {
super.onResume();
_shakeManager.onResume();
if (_preferences.isSaveStateOnExit()) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
_brushStyle = prefs.getInt(PREFERENCE_BRUSH_STYLE, 0);
setBrushStyle();
mPaint.setStrokeWidth(prefs.getFloat(PREFERENCE_BRUSH_WIDTH, 12));
colorChanged(prefs.getInt(PREFERENCE_COLOR, Color.RED));
_btnChildMode.setSelected(prefs.getBoolean(PREFERENCE_CHILD_MODE, false));
}
}
private Paint mPaint;
private MaskFilter mEmboss;
private MaskFilter mBlur;
public void colorChanged(int color) {
mPaint.setColor(color);
BitmapDrawable bmd = ImageUtils.drawPoint(this, R.drawable.color_picker, color);
_btnColor.setImageDrawable(bmd);
}
@Override
public void onShakeEvent() {
// TODO: add smart shake mamager initialization.
if (!_preferences.isShakeFeatureEnabled()) {
return;
}
new OnShakeAction().doAction(this, _paintView);
}
public class PaintView extends View {
//private static final float MINP = 0.25f;
//private static final float MAXP = 0.75f;
private Bitmap mBitmap;
private Canvas mCanvas;
private Shape mShape;
private Paint mBitmapPaint;
public PaintView(Context c) {
super(c);
Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
// TODO: fix delta height
int menusHeight = 124;
// Initial size = 320x480
mBitmap = Bitmap.createBitmap(width, height - menusHeight, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mCanvas.drawColor(0xFFFFFFFF);
mShape = new PathShape();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
}
public void onNewShapeSelect(Shape shape) {
mShape = shape;
}
public void onClear() {
mCanvas.drawColor(0xFFFFFFFF);
invalidate();
addActionToHistory();
}
private void addActionToHistory() {
if (_paintActions.size() >= HISTORY_CAPACITY) {
_paintActions.get(0).getBitmap().recycle();
_paintActions.remove(0);
}
_paintActions.push(new PaintAction(Bitmap.createBitmap(mBitmap)));
_currentAction = _paintActions.size();
}
public Bitmap getDrawableBitmapCopy() {
return Bitmap.createBitmap(mBitmap);
}
public void setDrawableBitmap(Bitmap bitmap) {
mCanvas.drawBitmap(bitmap, 0, 0, mBitmapPaint);
invalidate();
addActionToHistory();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(0xFFFFFFFF);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
mShape.draw(canvas, mPaint);
}
public void repaintAction(PaintAction paintAction) {
mCanvas.drawBitmap(paintAction.getBitmap(), 0, 0, mBitmapPaint);
invalidate();
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
@Override
public boolean onTouchEvent(MotionEvent event) {
if (ActivityManager.isUserAMonkey()) {
_btnChildMode.performClick();
}
float x = event.getX();
float y = event.getY();
int action = event.getAction();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
if (_btnChildMode.isSelected()) {
applyChildCorrections();
}
mX = x;
mY = y;
mShape.onTouchEvent((int) x, (int) y, action);
invalidate();
break;
}
case MotionEvent.ACTION_MOVE: {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mX = x;
mY = y;
mShape.onTouchEvent((int) x, (int) y, action);
}
invalidate();
break;
}
case MotionEvent.ACTION_UP: {
mShape.onTouchEvent((int) x, (int) y, action);
mShape.draw(mCanvas, mPaint);
mShape.reset();
invalidate();
if (_currentAction < _paintActions.size()) {
for (int i = 0; i < _paintActions.size() - _currentAction + 1; i++) {
_paintActions.removeElementAt(_paintActions.size() - 1);
}
}
addActionToHistory();
break;
}
}
return true;
}
private Random random = new Random(0);
private void applyChildCorrections() {
int color = Color.rgb(random.nextInt(256),
random.nextInt(256),
random.nextInt(256));
colorChanged(color);
}
}
@Override
public void onCreateContextMenu(ContextMenu menu, View view,
ContextMenu.ContextMenuInfo menuInfo) {
}
@Override
public boolean onContextItemSelected(MenuItem item) {
super.onContextItemSelected(item);
return false;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
SubMenu drawingItem = menu.addSubMenu(0, DRAWING_MENU_ID, 0, "Drawing")
.setIcon(R.drawable.ic_menu_compose);
drawingItem.add(0, BRUSH_WIDTH_MENU_ID, 0, "Brush width");
drawingItem.add(0, FIGURE_MENU_ID, 0, "Shapes");
drawingItem.add(0, CLEAR_SHAPE_MENU_ID, 0, "Clear");
//drawingItem.add(0, ATTACH_IMAGE_MENU_ID, 0, "Use image as...");
menu.add(0, SHARE_MENU_ID, 0, "Share").setIcon(android.R.drawable.ic_menu_share);
//SubMenu shareItem = menu.addSubMenu(0, SHARE_MENU_ID, 0, "Share")
// .setIcon(android.R.drawable.ic_menu_share);
//shareItem.add(0, SHARE_FACEBOOK_MENU_ID, 0, "Facebook");
//shareItem.add(0, SHARE_OTHER_MENU_ID, 0, "Other...");
menu.add(0, PREFERENCES_MENU_ID, 0, "Preferences").setIcon(android.R.drawable.ic_menu_preferences);
menu.add(0, OPEN_FILE_MENU_ID, 0, "Open").setIcon(R.drawable.ic_menu_archive);
menu.add(0, SAVE_FILE_MENU_ID, 0, "Save").setIcon(android.R.drawable.ic_menu_save);
menu.add(0, ABOUT_MENU_ID, 0, "About").setIcon(android.R.drawable.ic_menu_info_details);
return true;
}
private void onBrushWidthMenuItemSelected() {
final String[] items = new String[MAX_BRUSH_WIDTH_VALUE / BRUSH_VALUE_STEP];
for (int i = MIN_BRUSH_WIDTH_VALUE; i <= MAX_BRUSH_WIDTH_VALUE; i += BRUSH_VALUE_STEP) {
items[i / BRUSH_VALUE_STEP - 1] = String.valueOf(i);
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Select brush width");
BrushWidthListAdapter adapter = new BrushWidthListAdapter(this, items);
builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
mPaint.setStrokeWidth(Float.valueOf(items[item]));
dialog.cancel();
}
});
builder.create().show();
}
private void onBrushMenuItemSelected() {
// TODO: Change on more suatable implementation.
final String[] items = {"Simple",
"Eraser",
"Emboss",
"Blur",
"SrcATop"};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Pick a brush style");
BrushStyleListAdapter adapter = new BrushStyleListAdapter(this, items);
builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
_brushStyle = item;
setBrushStyle();
dialog.cancel();
}
});
builder.create().show();
}
private void setBrushStyle() {
switch (_brushStyle) {
case 0: {
mPaint.setMaskFilter(null);
mPaint.setXfermode(null);
mPaint.setAlpha(0xFF);
break;
}
case 1: {
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
break;
}
case 2: {
mPaint.setMaskFilter(mEmboss);
break;
}
case 3: {
mPaint.setMaskFilter(mBlur);
break;
}
case 4: {
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
mPaint.setAlpha(0x80);
break;
}
}
}
private void onFigureMenuItemSelected() {
final ArrayList<Shape> shapeItems = new ArrayList<Shape>();
shapeItems.add(new LineShape());
shapeItems.add(new PathShape());
shapeItems.add(new OvalShape());
shapeItems.add(new RectShape());
shapeItems.add(new RoundRectShape());
shapeItems.add(new TriangleShape());
shapeItems.add(new DiamondShape());
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Pick a shape");
ShapeListAdapter listAdapter = new ShapeListAdapter(this, shapeItems);
builder.setAdapter(listAdapter, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int itemIndex) {
Class shapeClass = shapeItems.get(itemIndex).getClass();
try {
Shape instance = (Shape) shapeClass.newInstance();
_paintView.onNewShapeSelect(instance);
}
catch (Exception ex) {
ex.printStackTrace();
WarningAlert.show(PaintAreaActivity.this,
"Cannot select shape: " + ex.getMessage());
}
dialog.cancel();
}
});
builder.create().show();
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
return true;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case COLOR_MENU_ID: {
if (resultCode == RESULT_OK) {
int color = data.getExtras().getInt(ColorPickerIntents.EXTRA_COLOR);
colorChanged(color);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor edit = prefs.edit();
edit.putInt(PREFERENCE_COLOR, color);
edit.commit();
_btnChildMode.setSelected(false);
}
break;
}
case OPEN_FILE_MENU_ID: {
if (resultCode == RESULT_OK) {
Uri fileUri = data.getData();
try {
File openFile = new File(fileUri.getPath());
if (!openFile.isFile()) {
WarningAlert.show(this, "Please select a valid image file.");
} else {
Bitmap bitmapImage = BitmapFactory.decodeFile(fileUri.getPath());
_paintView.setDrawableBitmap(bitmapImage);
bitmapImage.recycle();
}
}
catch (Exception ex) {
WarningAlert.show(this, "Cannot open image: " + ex.getMessage());
ex.printStackTrace();
}
}
break;
}
case SAVE_FILE_MENU_ID: {
if (resultCode == RESULT_OK) {
Uri fileUri = data.getData();
File openFile = new File(fileUri.getPath());
if (openFile.isDirectory()) {
WarningAlert.show(this, "Please specify a valid image path.");
} else {
Bitmap bitmap = _paintView.getDrawableBitmapCopy();
FileUtils.saveImage(this, bitmap, fileUri.getPath());
bitmap.recycle();
}
}
break;
}
case PREFERENCES_MENU_ID: {
if (resultCode == RESULT_OK) {
}
break;
}
case ABOUT_MENU_ID: {
if (resultCode == RESULT_OK) {
}
break;
}
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case DRAWING_MENU_ID: {
return true;
}
case BRUSH_WIDTH_MENU_ID: {
onBrushWidthMenuItemSelected();
return true;
}
case FIGURE_MENU_ID: {
onFigureMenuItemSelected();
return true;
}
case ATTACH_IMAGE_MENU_ID: {
new AttachImageAction().doAction(this, _paintView);
return true;
}
case CLEAR_SHAPE_MENU_ID: {
_paintView.onClear();
break;
}
case OPEN_FILE_MENU_ID: {
Intent intent = new Intent(this, FileManagerActivity.class);
intent.setAction(FileManagerIntents.ACTION_PICK_FILE);
intent.putExtra(FileManagerIntents.EXTRA_TITLE, "Select image to open");
startActivityForResult(intent, OPEN_FILE_MENU_ID);
return true;
}
case SAVE_FILE_MENU_ID: {
Intent intent = new Intent(this, FileManagerActivity.class);
intent.setAction(FileManagerIntents.ACTION_PICK_FILE);
intent.putExtra(FileManagerIntents.EXTRA_TITLE, "Select image to save");
startActivityForResult(intent, SAVE_FILE_MENU_ID);
return true;
}
case PREFERENCES_MENU_ID: {
Intent intent = new Intent(this, PreferencesActivity.class);
startActivityForResult(intent, PREFERENCES_MENU_ID);
return true;
}
case ABOUT_MENU_ID: {
Intent intent = new Intent(this, AboutActivity.class);
intent.setAction(AboutIntents.ACTION_SHOW_ABOUT_DIALOG);
startActivityForResult(intent, ABOUT_MENU_ID);
return true;
}
case SHARE_MENU_ID: {
new ShareToAction().doAction(this, _paintView);
// TODO: Add dynamic subitems list composition
return true;
}
case SHARE_FACEBOOK_MENU_ID: {
new FacebookShareAction().doAction(this, _paintView);
return true;
}
case SHARE_OTHER_MENU_ID: {
new AttachImageAction().doAction(this, _paintView);
return true;
}
}
return super.onOptionsItemSelected(item);
}
private void undo() {
if (_currentAction < _paintActions.size()) {
_paintView.repaintAction(_paintActions.get(_currentAction));
_currentAction++;
}
}
private void redo() {
if (_currentAction > 1) {
_currentAction--;
_paintView.repaintAction(_paintActions.get(_currentAction - 1));
}
}
}