package com.aviary.android.feather.effects;
import it.sephiroth.android.library.imagezoom.ImageViewTouch;
import android.R.attr;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.StateListDrawable;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnKeyListener;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import com.aviary.android.feather.R;
import com.aviary.android.feather.graphics.DefaultGalleryCheckboxDrawable;
import com.aviary.android.feather.graphics.OverlayGalleryCheckboxDrawable;
import com.aviary.android.feather.library.graphics.drawable.EditableDrawable;
import com.aviary.android.feather.library.graphics.drawable.TextDrawable;
import com.aviary.android.feather.library.moa.MoaAction;
import com.aviary.android.feather.library.moa.MoaActionFactory;
import com.aviary.android.feather.library.moa.MoaActionList;
import com.aviary.android.feather.library.moa.MoaColorParameter;
import com.aviary.android.feather.library.moa.MoaPointParameter;
import com.aviary.android.feather.library.services.ConfigService;
import com.aviary.android.feather.library.services.EffectContext;
import com.aviary.android.feather.library.utils.BitmapUtils;
import com.aviary.android.feather.library.utils.MatrixUtils;
import com.aviary.android.feather.library.utils.UIConfiguration;
import com.aviary.android.feather.utils.UIUtils;
import com.aviary.android.feather.widget.AdapterView;
import com.aviary.android.feather.widget.DrawableHighlightView;
import com.aviary.android.feather.widget.Gallery;
import com.aviary.android.feather.widget.Gallery.OnItemsScrollListener;
import com.aviary.android.feather.widget.ImageViewDrawableOverlay;
import com.aviary.android.feather.widget.ImageViewDrawableOverlay.OnDrawableEventListener;
public class TextPanel extends AbstractContentPanel implements OnDrawableEventListener, OnEditorActionListener {
abstract class MyTextWatcher implements TextWatcher {
public DrawableHighlightView view;
}
Gallery mGallery;
View mSelected;
int mSelectedPosition;
/** The available text colors. */
int[] mColors;
/** The available text stroke colors. */
int[] mStrokeColors;
/** The current selected color. */
private int mColor = 0;
/** The current selected stroke color. */
private int mStrokeColor = 0;
/** The minimum text size. */
private int minTextSize = 16;
/** The default text size. */
private int defaultTextSize = 16;
/** The text padding. */
private int textPadding = 10;
/** The drawing canvas. */
private Canvas mCanvas;
/** The android input manager. */
private InputMethodManager mInputManager;
/** The current edit text. */
private EditText mEditText;
private ConfigService config;
/** The m highlight stroke color down. */
private int mHighlightEllipse, mHighlightStrokeWidth;
private ColorStateList mHighlightFillColorStateList, mHighlightStrokeColorStateList;
/** The m edit text watcher. */
private final MyTextWatcher mEditTextWatcher = new MyTextWatcher() {
@Override
public void afterTextChanged( final Editable s ) {
mLogger.info( "afterTextChanged" );
}
@Override
public void beforeTextChanged( final CharSequence s, final int start, final int count, final int after ) {
mLogger.info( "beforeTextChanged" );
}
@Override
public void onTextChanged( final CharSequence s, final int start, final int before, final int count ) {
if ( ( view != null ) && ( view.getContent() instanceof EditableDrawable ) ) {
final EditableDrawable editable = (EditableDrawable) view.getContent();
if ( !editable.isEditing() ) return;
editable.setText( s.toString() );
view.forceUpdate();
}
}
};
/**
* Instantiates a new text panel.
*
* @param context
* the context
*/
public TextPanel( final EffectContext context ) {
super( context );
}
/**
* Begin edit and open the android soft keyboard if available
*
* @param view
* the view
*/
private void beginEdit( final DrawableHighlightView view ) {
if( null != view ){
view.setFocused( true );
}
mEditTextWatcher.view = null;
mEditText.removeTextChangedListener( mEditTextWatcher );
mEditText.setOnKeyListener( null );
final EditableDrawable editable = (EditableDrawable) view.getContent();
final String oldText = editable.isTextHint() ? "" : (String) editable.getText();
mEditText.setText( oldText );
mEditText.setSelection( mEditText.length() );
mEditText.setImeOptions( EditorInfo.IME_ACTION_DONE );
mEditText.requestFocusFromTouch();
// mInputManager.showSoftInput( mEditText, InputMethodManager.SHOW_IMPLICIT );
mInputManager.toggleSoftInput( InputMethodManager.SHOW_FORCED, 0 );
mEditTextWatcher.view = view;
mEditText.setOnEditorActionListener( this );
mEditText.addTextChangedListener( mEditTextWatcher );
mEditText.setOnKeyListener( new OnKeyListener() {
@Override
public boolean onKey( View v, int keyCode, KeyEvent event ) {
mLogger.log( "onKey: " + event );
if ( keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_BACK ) {
if ( editable.isTextHint() && editable.isEditing() ) {
editable.setText( "" );
view.forceUpdate();
}
}
return false;
}
} );
}
/**
* Creates the and configure preview.
*/
private void createAndConfigurePreview() {
if ( ( mPreview != null ) && !mPreview.isRecycled() ) {
mPreview.recycle();
mPreview = null;
}
mPreview = BitmapUtils.copy( mBitmap, mBitmap.getConfig() );
mCanvas = new Canvas( mPreview );
}
/**
* End edit text and closes the android keyboard
*
* @param view
* the view
*/
private void endEdit( final DrawableHighlightView view ) {
if( null != view ){
view.setFocused( false );
view.forceUpdate();
}
mEditTextWatcher.view = null;
mEditText.removeTextChangedListener( mEditTextWatcher );
mEditText.setOnKeyListener( null );
if ( mInputManager.isActive( mEditText ) ) mInputManager.hideSoftInputFromWindow( mEditText.getWindowToken(), 0 );
}
/*
* (non-Javadoc)
*
* @see com.aviary.android.feather.effects.AbstractContentPanel#generateContentView(android.view.LayoutInflater)
*/
@Override
protected View generateContentView( final LayoutInflater inflater ) {
return inflater.inflate( R.layout.feather_text_content, null );
}
/*
* (non-Javadoc)
*
* @see com.aviary.android.feather.effects.AbstractOptionPanel#generateOptionView(android.view.LayoutInflater,
* android.view.ViewGroup)
*/
@Override
protected ViewGroup generateOptionView( final LayoutInflater inflater, ViewGroup parent ) {
return (ViewGroup) inflater.inflate( R.layout.feather_text_panel, parent, false );
}
/*
* (non-Javadoc)
*
* @see com.aviary.android.feather.effects.AbstractEffectPanel#getIsChanged()
*/
@Override
public boolean getIsChanged() {
return super.getIsChanged() || ( ( (ImageViewDrawableOverlay) mImageView ).getHighlightCount() > 0 );
}
/**
* Add a new editable text on the screen.
*/
private void onAddNewText() {
final ImageViewDrawableOverlay image = (ImageViewDrawableOverlay) mImageView;
if ( image.getHighlightCount() > 0 ) onApplyCurrent( image.getHighlightViewAt( 0 ) );
final TextDrawable text = new TextDrawable( "", defaultTextSize );
text.setTextColor( mColor );
text.setTextHint( getContext().getBaseContext().getString( R.string.enter_text_here ) );
final DrawableHighlightView hv = new DrawableHighlightView( mImageView, text );
final Matrix mImageMatrix = mImageView.getImageViewMatrix();
final int width = mImageView.getWidth();
final int height = mImageView.getHeight();
final int imageSize = Math.max( width, height );
// width/height of the sticker
int cropWidth = text.getIntrinsicWidth();
int cropHeight = text.getIntrinsicHeight();
final int cropSize = Math.max( cropWidth, cropHeight );
if ( cropSize > imageSize ) {
cropWidth = width / 2;
cropHeight = height / 2;
}
final int x = ( width - cropWidth ) / 2;
final int y = ( height - cropHeight ) / 2;
final Matrix matrix = new Matrix( mImageMatrix );
matrix.invert( matrix );
final float[] pts = new float[] { x, y, x + cropWidth, y + cropHeight };
MatrixUtils.mapPoints( matrix, pts );
final RectF cropRect = new RectF( pts[0], pts[1], pts[2], pts[3] );
final Rect imageRect = new Rect( 0, 0, width, height );
hv.setRotateAndScale( true );
hv.showDelete( false );
hv.setup( mImageMatrix, imageRect, cropRect, false );
hv.drawOutlineFill( true );
hv.drawOutlineStroke( true );
hv.setPadding( textPadding );
hv.setMinSize( minTextSize );
hv.setOutlineEllipse( mHighlightEllipse );
hv.setOutlineFillColor( mHighlightFillColorStateList );
hv.setOutlineStrokeColor( mHighlightStrokeColorStateList );
Paint stroke = hv.getOutlineStrokePaint();
stroke.setStrokeWidth( mHighlightStrokeWidth );
image.addHighlightView( hv );
// image.setSelectedHighlightView( hv );
onClick( hv );
}
/**
* apply the current text and flatten it over the image.
*/
private MoaActionList onApplyCurrent() {
final MoaActionList nullActions = MoaActionFactory.actionList();
final ImageViewDrawableOverlay image = (ImageViewDrawableOverlay) mImageView;
if ( image.getHighlightCount() < 1 ) return nullActions;
final DrawableHighlightView hv = ( (ImageViewDrawableOverlay) mImageView ).getHighlightViewAt( 0 );
if ( hv != null ) {
if ( hv.getContent() instanceof EditableDrawable ) {
EditableDrawable editable = (EditableDrawable) hv.getContent();
if ( editable != null ) {
if ( editable.isTextHint() ) {
setIsChanged( false );
return nullActions;
}
}
}
return onApplyCurrent( hv );
}
return nullActions;
}
/**
* Flatten the view on the current image
*
* @param hv
* the hv
*/
private MoaActionList onApplyCurrent( final DrawableHighlightView hv ) {
MoaActionList actionList = MoaActionFactory.actionList();
if ( hv != null ) {
setIsChanged( true );
final RectF cropRect = hv.getCropRectF();
final Rect rect = new Rect( (int) cropRect.left, (int) cropRect.top, (int) cropRect.right, (int) cropRect.bottom );
final Matrix rotateMatrix = hv.getCropRotationMatrix();
final int w = mBitmap.getWidth();
final int h = mBitmap.getHeight();
final float left = cropRect.left / w;
final float top = cropRect.top / h;
final float right = cropRect.right / w;
final float bottom = cropRect.bottom / h;
final Matrix matrix = new Matrix( mImageView.getImageMatrix() );
if ( !matrix.invert( matrix ) ) mLogger.error( "unable to invert matrix" );
EditableDrawable editable = (EditableDrawable) hv.getContent();
editable.endEdit();
mImageView.invalidate();
MoaAction action = MoaActionFactory.action( "addtext" );
action.setValue( "text", (String) editable.getText() );
action.setValue( "fillcolor", new MoaColorParameter( mColor ) );
action.setValue( "outlinecolor", new MoaColorParameter( mStrokeColor ) );
action.setValue( "rotation", hv.getRotation() );
action.setValue( "topleft", new MoaPointParameter( left, top ) );
action.setValue( "bottomright", new MoaPointParameter( right, bottom ) );
actionList.add( action );
final int saveCount = mCanvas.save( Canvas.MATRIX_SAVE_FLAG );
mCanvas.concat( rotateMatrix );
hv.getContent().setBounds( rect );
hv.getContent().draw( mCanvas );
mCanvas.restoreToCount( saveCount );
mImageView.invalidate();
onClearCurrent( hv );
}
onPreviewChanged( mPreview, false );
return actionList;
}
/**
* Removes the current text
*
* @param hv
* the hv
*/
private void onClearCurrent( final DrawableHighlightView hv ) {
hv.setOnDeleteClickListener( null );
( (ImageViewDrawableOverlay) mImageView ).removeHightlightView( hv );
}
/*
* (non-Javadoc)
*
* @see
* com.aviary.android.feather.widget.ImageViewDrawableOverlay.OnDrawableEventListener#onClick(com.aviary.android.feather.widget
* .DrawableHighlightView)
*/
@Override
public void onClick( final DrawableHighlightView view ) {
if ( view != null ) if ( view.getContent() instanceof EditableDrawable ) {
if( !view.getFocused() ){
beginEdit( view );
}
/*
final EditableDrawable text = (EditableDrawable) view.getContent();
if ( !text.isEditing() ) {
text.beginEdit();
beginEdit( view );
}
*/
}
}
/*
* (non-Javadoc)
*
* @see com.aviary.android.feather.effects.AbstractEffectPanel#onCreate(android.graphics.Bitmap)
*/
@Override
public void onCreate( final Bitmap bitmap ) {
super.onCreate( bitmap );
config = getContext().getService( ConfigService.class );
mColors = config.getIntArray( R.array.feather_text_fill_colors );
mStrokeColors = config.getIntArray( R.array.feather_text_stroke_colors );
mSelectedPosition = config.getInteger( R.integer.feather_text_selected_color );
mColor = mColors[mSelectedPosition];
mStrokeColor = mStrokeColors[mSelectedPosition];
mGallery = (Gallery) getOptionView().findViewById( R.id.gallery );
mGallery.setCallbackDuringFling( false );
mGallery.setSpacing( 0 );
mGallery.setAdapter( new GalleryAdapter( getContext().getBaseContext(), mColors ) );
mGallery.setSelection( mSelectedPosition, false, true );
mGallery.setOnItemsScrollListener( new OnItemsScrollListener() {
@Override
public void onScrollFinished( AdapterView<?> parent, View view, int position, long id ) {
updateSelection( view, position );
final int color = mColors[position];
mColor = color;
mStrokeColor = mStrokeColors[position];
updateCurrentHighlightColor();
updateColorButtonBitmap();
}
@Override
public void onScrollStarted( AdapterView<?> parent, View view, int position, long id ) {
// TODO Auto-generated method stub
}
@Override
public void onScroll( AdapterView<?> parent, View view, int position, long id ) {
// TODO Auto-generated method stub
}
} );
mEditText = (EditText) getContentView().findViewById( R.id.invisible_text );
mImageView = (ImageViewTouch) getContentView().findViewById( R.id.overlay );
mImageView.setDoubleTapEnabled( false );
createAndConfigurePreview();
updateColorButtonBitmap();
mImageView.setImageBitmap( mPreview, true, getContext().getCurrentImageViewMatrix(), UIConfiguration.IMAGE_VIEW_MAX_ZOOM );
}
/*
* (non-Javadoc)
*
* @see com.aviary.android.feather.effects.AbstractEffectPanel#onActivate()
*/
@Override
public void onActivate() {
super.onActivate();
disableHapticIsNecessary( mGallery );
mHighlightFillColorStateList = config.getColorStateList( R.color.feather_effect_text_color_fill_selector );
mHighlightStrokeColorStateList = config.getColorStateList( R.color.feather_effect_text_color_stroke_selector );
mHighlightEllipse = config.getInteger( R.integer.feather_text_highlight_ellipse );
mHighlightStrokeWidth = config.getInteger( R.integer.feather_text_highlight_stroke_width );
minTextSize = config.getDimensionPixelSize( R.dimen.feather_text_drawable_min_size );
defaultTextSize = config.getDimensionPixelSize( R.dimen.feather_text_default_size );
defaultTextSize = Math.max( minTextSize, defaultTextSize );
textPadding = config.getInteger( R.integer.feather_text_padding );
( (ImageViewDrawableOverlay) mImageView ).setOnDrawableEventListener( this );
( (ImageViewDrawableOverlay) mImageView ).setScaleWithContent( true );
( (ImageViewDrawableOverlay) mImageView ).setForceSingleSelection( false );
mInputManager = (InputMethodManager) getContext().getBaseContext().getSystemService( Context.INPUT_METHOD_SERVICE );
mImageView.requestLayout();
updateSelection( (View) mGallery.getSelectedView(), mGallery.getSelectedItemPosition() );
contentReady();
onAddNewText();
}
/*
* (non-Javadoc)
*
* @see com.aviary.android.feather.effects.AbstractEffectPanel#onDeactivate()
*/
@Override
public void onDeactivate() {
( (ImageViewDrawableOverlay) mImageView ).setOnDrawableEventListener( null );
endEdit( null );
super.onDeactivate();
}
/*
* (non-Javadoc)
*
* @see com.aviary.android.feather.effects.AbstractEffectPanel#onDestroy()
*/
@Override
public void onDestroy() {
mCanvas = null;
mInputManager = null;
super.onDestroy();
}
/**
* Update selection.
*
* @param newSelection
* the new selection
* @param position
* the position
*/
protected void updateSelection( View newSelection, int position ) {
if ( mSelected != null ) {
mSelected.setSelected( false );
}
mSelected = newSelection;
mSelectedPosition = position;
if ( mSelected != null ) {
mSelected = newSelection;
mSelected.setSelected( true );
}
}
/*
* (non-Javadoc)
*
* @see
* com.aviary.android.feather.widget.ImageViewDrawableOverlay.OnDrawableEventListener#onDown(com.aviary.android.feather.widget
* .DrawableHighlightView)
*/
@Override
public void onDown( final DrawableHighlightView view ) {}
/*
* (non-Javadoc)
*
* @see
* com.aviary.android.feather.widget.ImageViewDrawableOverlay.OnDrawableEventListener#onFocusChange(com.aviary.android.feather
* .widget.DrawableHighlightView, com.aviary.android.feather.widget.DrawableHighlightView)
*/
@Override
public void onFocusChange( final DrawableHighlightView newFocus, final DrawableHighlightView oldFocus ) {
EditableDrawable text;
if ( oldFocus != null ) if ( oldFocus.getContent() instanceof EditableDrawable ) {
text = (EditableDrawable) oldFocus.getContent();
if ( text.isEditing() ) {
//text.endEdit();
endEdit( oldFocus );
}
// if ( !oldFocus.equals( newFocus ) )
// if ( text.getText().length() == 0 )
// onClearCurrent( oldFocus );
}
if ( newFocus != null ) if ( newFocus.getContent() instanceof EditableDrawable ) {
text = (EditableDrawable) newFocus.getContent();
mColor = text.getTextColor();
mStrokeColor = text.getTextStrokeColor();
updateColorButtonBitmap();
}
}
/*
* (non-Javadoc)
*
* @see com.aviary.android.feather.effects.AbstractEffectPanel#onGenerateResult()
*/
@Override
protected void onGenerateResult() {
MoaActionList actions = onApplyCurrent();
super.onGenerateResult( actions );
}
/*
* (non-Javadoc)
*
* @see
* com.aviary.android.feather.widget.ImageViewDrawableOverlay.OnDrawableEventListener#onMove(com.aviary.android.feather.widget
* .DrawableHighlightView)
*/
@Override
public void onMove( final DrawableHighlightView view ) {
if ( view.getContent() instanceof EditableDrawable ) if ( ( (EditableDrawable) view.getContent() ).isEditing() ) {
//( (EditableDrawable) view.getContent() ).endEdit();
endEdit( view );
}
}
/**
* Update color button bitmap.
*/
private void updateColorButtonBitmap() {}
/**
* Update current highlight color.
*/
private void updateCurrentHighlightColor() {
final ImageViewDrawableOverlay image = (ImageViewDrawableOverlay) mImageView;
if ( image.getSelectedHighlightView() != null ) {
final DrawableHighlightView hv = image.getSelectedHighlightView();
if ( hv.getContent() instanceof EditableDrawable ) {
( (EditableDrawable) hv.getContent() ).setTextColor( mColor );
( (EditableDrawable) hv.getContent() ).setTextStrokeColor( mStrokeColor );
mImageView.postInvalidate();
}
}
}
/*
* (non-Javadoc)
*
* @see android.widget.TextView.OnEditorActionListener#onEditorAction(android.widget.TextView, int, android.view.KeyEvent)
*/
@Override
public boolean onEditorAction( TextView v, int actionId, KeyEvent event ) {
if ( mEditText != null ) {
if ( mEditText.equals( v ) ) {
if ( actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_ACTION_UNSPECIFIED ) {
final ImageViewDrawableOverlay image = (ImageViewDrawableOverlay) mImageView;
if ( image.getSelectedHighlightView() != null ) {
DrawableHighlightView d = image.getSelectedHighlightView();
if ( d.getContent() instanceof EditableDrawable ) {
EditableDrawable text = (EditableDrawable) d.getContent();
if ( text.isEditing() ) {
//text.endEdit();
endEdit( d );
}
}
}
}
}
}
return false;
}
class GalleryAdapter extends BaseAdapter {
private final int VALID_POSITION = 0;
private final int INVALID_POSITION = 1;
private int[] colors;
Resources mRes;
LayoutInflater mLayoutInflater;
/**
* Instantiates a new gallery adapter.
*
* @param context
* the context
* @param values
* the values
*/
public GalleryAdapter( Context context, int[] values ) {
mLayoutInflater = UIUtils.getLayoutInflater();
colors = values;
mRes = getContext().getBaseContext().getResources();
}
/*
* (non-Javadoc)
*
* @see android.widget.Adapter#getCount()
*/
@Override
public int getCount() {
return colors.length;
}
/*
* (non-Javadoc)
*
* @see android.widget.Adapter#getItem(int)
*/
@Override
public Object getItem( int position ) {
return colors[position];
}
/*
* (non-Javadoc)
*
* @see android.widget.Adapter#getItemId(int)
*/
@Override
public long getItemId( int position ) {
return 0;
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public int getItemViewType( int position ) {
final boolean valid = position >= 0 && position < getCount();
return valid ? VALID_POSITION : INVALID_POSITION;
}
/*
* (non-Javadoc)
*
* @see android.widget.Adapter#getView(int, android.view.View, android.view.ViewGroup)
*/
@SuppressWarnings("deprecation")
@Override
public View getView( int position, View convertView, ViewGroup parent ) {
final int type = getItemViewType( position );
View view;
if ( convertView == null ) {
if ( type == VALID_POSITION ) {
view = mLayoutInflater.inflate( R.layout.feather_color_button, mGallery, false );
Drawable unselectedBackground = new OverlayGalleryCheckboxDrawable( mRes, false, null, 1.0f, 20 );
Drawable selectedBackground = new OverlayGalleryCheckboxDrawable( mRes, true, null, 1.0f, 20 );
StateListDrawable st = new StateListDrawable();
st.addState( new int[] { -attr.state_selected }, unselectedBackground );
st.addState( new int[] { attr.state_selected }, selectedBackground );
view.setBackgroundDrawable( st );
} else {
// use the blank view
view = mLayoutInflater.inflate( R.layout.feather_default_blank_gallery_item, mGallery, false );
Drawable unselectedBackground = new DefaultGalleryCheckboxDrawable( mRes, false );
view.setBackgroundDrawable( unselectedBackground );
}
} else {
view = convertView;
}
if ( type == VALID_POSITION ) {
ImageView masked = (ImageView) view.findViewById( R.id.color_mask );
final int color = (Integer) getItem( position );
LayerDrawable layer = (LayerDrawable) masked.getDrawable();
GradientDrawable shape = (GradientDrawable) layer.findDrawableByLayerId( R.id.masked );
shape.setColor( color );
view.setSelected( position == mSelectedPosition );
}
return view;
}
}
}