package com.aviary.android.feather.effects;
import it.sephiroth.android.library.imagezoom.ImageViewTouch;
import it.sephiroth.android.library.imagezoom.ImageViewTouchBase.OnBitmapChangedListener;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.ResultReceiver;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import com.aviary.android.feather.R;
import com.aviary.android.feather.graphics.RepeatableHorizontalDrawable;
import com.aviary.android.feather.library.filters.FilterLoaderFactory;
import com.aviary.android.feather.library.filters.FilterLoaderFactory.Filters;
import com.aviary.android.feather.library.filters.MemeFilter;
import com.aviary.android.feather.library.graphics.drawable.EditableDrawable;
import com.aviary.android.feather.library.graphics.drawable.MemeTextDrawable;
import com.aviary.android.feather.library.moa.MoaActionList;
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.utils.TypefaceUtils;
import com.aviary.android.feather.widget.DrawableHighlightView;
import com.aviary.android.feather.widget.ImageViewDrawableOverlay;
import com.aviary.android.feather.widget.ImageViewDrawableOverlay.OnDrawableEventListener;
import com.aviary.android.feather.widget.ImageViewDrawableOverlay.OnLayoutListener;
/**
* The Class MemePanel.
*/
public class MemePanel extends AbstractContentPanel implements OnEditorActionListener, OnClickListener, OnDrawableEventListener,
OnLayoutListener {
Button editTopButton, editBottomButton;
EditText editTopText, editBottomText;
InputMethodManager mInputManager;
Canvas mCanvas;
DrawableHighlightView topHv, bottomHv;
Typeface mTypeface;
String fontName;
Button clearButtonTop, clearButtonBottom;
/**
* Instantiates a new meme panel.
*
* @param context
* the context
*/
public MemePanel( EffectContext context ) {
super( context );
ConfigService config = context.getService( ConfigService.class );
if ( config != null ) {
fontName = config.getString( R.string.feather_meme_default_font );
}
}
/*
* (non-Javadoc)
*
* @see com.aviary.android.feather.effects.AbstractEffectPanel#onCreate(android.graphics.Bitmap)
*/
@SuppressWarnings("deprecation")
@Override
public void onCreate( Bitmap bitmap ) {
super.onCreate( bitmap );
editTopButton = (Button) getOptionView().findViewById( R.id.button1 );
editBottomButton = (Button) getOptionView().findViewById( R.id.button2 );
mImageView = (ImageViewTouch) getContentView().findViewById( R.id.overlay );
editTopText = (EditText) getContentView().findViewById( R.id.invisible_text_1 );
editBottomText = (EditText) getContentView().findViewById( R.id.invisible_text_2 );
clearButtonTop = (Button) getOptionView().findViewById( R.id.clear_button_top );
clearButtonBottom = (Button) getOptionView().findViewById( R.id.clear_button_bottom );
mImageView.setDoubleTapEnabled( false );
mImageView.setScaleEnabled( false );
mImageView.setScrollEnabled( false );
createAndConfigurePreview();
mImageView.setOnBitmapChangedListener( new OnBitmapChangedListener() {
@Override
public void onBitmapChanged( Drawable drawable ) {
final Matrix mImageMatrix = mImageView.getImageViewMatrix();
float[] matrixValues = getMatrixValues( mImageMatrix );
final int height = (int) ( mBitmap.getHeight() * matrixValues[Matrix.MSCALE_Y] );
View view = getContentView().findViewById( R.id.feather_meme_dumb );
LinearLayout.LayoutParams p = (LinearLayout.LayoutParams) view.getLayoutParams();
p.height = height - 30;
view.setLayoutParams( p );
view.requestLayout();
}
} );
mImageView.setImageBitmap( mPreview, true, null );
View content = getOptionView().findViewById( R.id.content );
content.setBackgroundDrawable( RepeatableHorizontalDrawable.createFromView( content ) );
}
/*
* (non-Javadoc)
*
* @see com.aviary.android.feather.effects.AbstractEffectPanel#onActivate()
*/
@Override
public void onActivate() {
super.onActivate();
createTypeFace();
onAddTopText();
onAddBottomText();
( (ImageViewDrawableOverlay) mImageView ).setOnDrawableEventListener( this );
( (ImageViewDrawableOverlay) mImageView ).setOnLayoutListener( this );
mInputManager = (InputMethodManager) getContext().getBaseContext().getSystemService( Context.INPUT_METHOD_SERVICE );
editTopButton.setOnClickListener( this );
editBottomButton.setOnClickListener( this );
editTopText.setVisibility( View.VISIBLE );
editBottomText.setVisibility( View.VISIBLE );
editTopText.getBackground().setAlpha( 0 );
editBottomText.getBackground().setAlpha( 0 );
clearButtonTop.setOnClickListener( this );
clearButtonBottom.setOnClickListener( this );
getContentView().setVisibility( View.VISIBLE );
contentReady();
}
/*
* (non-Javadoc)
*
* @see com.aviary.android.feather.effects.AbstractEffectPanel#onDeactivate()
*/
@Override
public void onDeactivate() {
super.onDeactivate();
endEditView( topHv );
endEditView( bottomHv );
( (ImageViewDrawableOverlay) mImageView ).setOnDrawableEventListener( null );
( (ImageViewDrawableOverlay) mImageView ).setOnLayoutListener( null );
editTopButton.setOnClickListener( null );
editBottomButton.setOnClickListener( null );
clearButtonTop.setOnClickListener( null );
clearButtonBottom.setOnClickListener( null );
if ( mInputManager.isActive( editTopText ) ) mInputManager.hideSoftInputFromWindow( editTopText.getWindowToken(), 0 );
if ( mInputManager.isActive( editBottomText ) ) mInputManager.hideSoftInputFromWindow( editBottomText.getWindowToken(), 0 );
}
/*
* (non-Javadoc)
*
* @see com.aviary.android.feather.effects.AbstractEffectPanel#onDestroy()
*/
@Override
public void onDestroy() {
mCanvas = null;
mInputManager = null;
super.onDestroy();
}
/*
* (non-Javadoc)
*
* @see com.aviary.android.feather.effects.AbstractContentPanel#generateContentView(android.view.LayoutInflater)
*/
@Override
protected View generateContentView( LayoutInflater inflater ) {
return inflater.inflate( R.layout.feather_meme_content, null );
}
/*
* (non-Javadoc)
*
* @see com.aviary.android.feather.effects.AbstractOptionPanel#generateOptionView(android.view.LayoutInflater,
* android.view.ViewGroup)
*/
@Override
protected ViewGroup generateOptionView( LayoutInflater inflater, ViewGroup parent ) {
return (ViewGroup) inflater.inflate( R.layout.feather_meme_panel, parent, false );
}
/*
* (non-Javadoc)
*
* @see com.aviary.android.feather.effects.AbstractEffectPanel#onGenerateResult()
*/
@Override
protected void onGenerateResult() {
MemeFilter filter = (MemeFilter) FilterLoaderFactory.get( Filters.MEME );
flattenText( topHv, filter );
flattenText( bottomHv, filter );
MoaActionList actionList = (MoaActionList) filter.getActions().clone();
super.onGenerateResult( actionList );
}
/*
* (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 ) {
mLogger.info( "onEditorAction", v, actionId, event );
if ( v != null ) {
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 ) {
endEditView( d );
}
}
}
}
return false;
}
/**
* Flatten text.
*
* @param hv
* the hv
*/
private void flattenText( final DrawableHighlightView hv, final MemeFilter filter ) {
if ( hv != null ) {
hv.setHidden( true );
final Matrix mImageMatrix = mImageView.getImageViewMatrix();
float[] matrixValues = getMatrixValues( mImageMatrix );
mLogger.log( "image scaled: " + matrixValues[Matrix.MSCALE_X] );
// TODO: check this modification
final int width = (int) ( mBitmap.getWidth() );
final int height = (int) ( mBitmap.getHeight() );
final RectF cropRect = hv.getCropRectF();
final Rect rect = new Rect( (int) cropRect.left, (int) cropRect.top, (int) cropRect.right, (int) cropRect.bottom );
final MemeTextDrawable editable = (MemeTextDrawable) hv.getContent();
final int saveCount = mCanvas.save( Canvas.MATRIX_SAVE_FLAG );
// force end edit and hide the blinking cursor
editable.endEdit();
editable.invalidateSelf();
editable.setContentSize( width, height );
editable.setBounds( rect.left, rect.top, rect.right, rect.bottom );
editable.draw( mCanvas );
if ( topHv == hv ) {
filter.setTopText( (String) editable.getText(), (double) editable.getTextSize() / mBitmap.getWidth() );
filter.setTopOffset( ( cropRect.left + (double) editable.getXoff() ) / mBitmap.getWidth(),
( cropRect.top + (double) editable.getYoff() ) / mBitmap.getHeight() );
// action.setValue( "toptext", (String) editable.getText() );
// action.setValue( "topsize", (double)editable.getTextSize()/mBitmap.getWidth() );
// action.setValue( "topxoff", (cropRect.left + (double)editable.getXoff())/mBitmap.getWidth() );
// action.setValue( "topyoff", (cropRect.top + (double)editable.getYoff())/mBitmap.getHeight() );
} else {
filter.setBottomText( (String) editable.getText(), (double) editable.getTextSize() / mBitmap.getWidth() );
filter.setBottomOffset( ( cropRect.left + (double) editable.getXoff() ) / mBitmap.getWidth(),
( cropRect.top + (double) editable.getYoff() ) / mBitmap.getHeight() );
// action.setValue( "bottomtext", (String) editable.getText() );
// action.setValue( "bottomsize", (double)editable.getTextSize()/mBitmap.getWidth() );
// action.setValue( "bottomxoff", (cropRect.left + (double)editable.getXoff())/mBitmap.getWidth() );
// action.setValue( "bottomyoff", (cropRect.top + (double)editable.getYoff())/mBitmap.getHeight() );
}
filter.setTextScale( matrixValues[Matrix.MSCALE_X] );
// action.setValue( "scale", matrixValues[Matrix.MSCALE_X] );
// action.setValue( "textsize", editable.getTextSize() );
mCanvas.restoreToCount( saveCount );
mImageView.invalidate();
}
onPreviewChanged( mPreview, 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 );
}
/*
* (non-Javadoc)
*
* @see android.view.View.OnClickListener#onClick(android.view.View)
*/
@Override
public void onClick( View v ) {
if ( v == editTopButton ) {
onTopClick( topHv );
} else if ( v == editBottomButton ) {
onTopClick( bottomHv );
} else if ( v == clearButtonTop ) {
clearEditView( topHv );
endEditView( topHv );
} else if ( v == clearButtonBottom ) {
clearEditView( bottomHv );
endEditView( bottomHv );
}
}
/**
* In top editable text click
*
* @param view
* the view
*/
public void onTopClick( final DrawableHighlightView view ) {
mLogger.info( "onTopClick", view );
if ( view != null ) if ( view.getContent() instanceof EditableDrawable ) {
beginEditView( view );
}
}
/**
* Extract a value form the matrix
*
* @param m
* the m
* @return the matrix values
*/
public static float[] getMatrixValues( Matrix m ) {
float[] values = new float[9];
m.getValues( values );
return values;
}
/**
* Creates and places the top editable text
*/
private void onAddTopText() {
final Matrix mImageMatrix = mImageView.getImageViewMatrix();
final int width = (int) ( mBitmap.getWidth() );
final int height = (int) ( mBitmap.getHeight() );
final MemeTextDrawable text = new MemeTextDrawable( "", (float) mBitmap.getHeight() / 7.f, mTypeface );
text.setTextColor( Color.WHITE );
text.setTextStrokeColor( Color.BLACK );
text.setContentSize( width, height );
topHv = new DrawableHighlightView( mImageView, text );
topHv.setAlignModeV( DrawableHighlightView.AlignModeV.Top );
final int cropHeight = text.getIntrinsicHeight();
final int x = 0;
final int y = 0;
final Matrix matrix = new Matrix( mImageMatrix );
matrix.invert( matrix );
final float[] pts = new float[] { x, y, x + width, y + cropHeight };
MatrixUtils.mapPoints( matrix, pts );
final RectF cropRect = new RectF( pts[0], pts[1], pts[2], pts[3] );
addEditable( topHv, mImageMatrix, cropRect );
}
/**
* Create and place the bottom editable text.
*/
private void onAddBottomText() {
final Matrix mImageMatrix = mImageView.getImageViewMatrix();
final int width = (int) ( mBitmap.getWidth() );
final int height = (int) ( mBitmap.getHeight() );
final MemeTextDrawable text = new MemeTextDrawable( "", (float) mBitmap.getHeight() / 7.0f, mTypeface );
text.setTextColor( Color.WHITE );
text.setTextStrokeColor( Color.BLACK );
text.setContentSize( width, height );
bottomHv = new DrawableHighlightView( mImageView, text );
bottomHv.setAlignModeV( DrawableHighlightView.AlignModeV.Bottom );
final int cropHeight = text.getIntrinsicHeight();
final int x = 0;
final int y = 0;
final Matrix matrix = new Matrix( mImageMatrix );
matrix.invert( matrix );
final float[] pts = new float[] { x, y + height - cropHeight - ( height / 30 ), x + width, y + height - ( height / 30 ) };
MatrixUtils.mapPoints( matrix, pts );
final RectF cropRect = new RectF( pts[0], pts[1], pts[2], pts[3] );
addEditable( bottomHv, mImageMatrix, cropRect );
}
/**
* Adds the editable.
*
* @param hv
* the hv
* @param imageMatrix
* the image matrix
* @param cropRect
* the crop rect
*/
private void addEditable( DrawableHighlightView hv, Matrix imageMatrix, RectF cropRect ) {
final ImageViewDrawableOverlay image = (ImageViewDrawableOverlay) mImageView;
hv.setRotateAndScale( true );
hv.showAnchors( false );
hv.drawOutlineFill( false );
hv.drawOutlineStroke( false );
hv.setup( imageMatrix, null, cropRect, false );
hv.getOutlineFillPaint().setXfermode( new PorterDuffXfermode( android.graphics.PorterDuff.Mode.SRC_ATOP ) );
hv.setMinSize( 10 );
hv.setOutlineFillColor( new ColorStateList( new int[][]{ {android.R.attr.state_active } }, new int[]{0} ) );
hv.setOutlineStrokeColor( new ColorStateList( new int[][]{ {android.R.attr.state_active } }, new int[]{0} ) );
image.addHighlightView( hv );
}
abstract class MyTextWatcher implements TextWatcher {
public DrawableHighlightView view;
}
private final MyTextWatcher mEditTextWatcher = new MyTextWatcher() {
@Override
public void afterTextChanged( final Editable s ) {}
@Override
public void beforeTextChanged( final CharSequence s, final int start, final int count, final int after ) {}
@Override
public void onTextChanged( final CharSequence s, final int start, final int before, final int count ) {
mLogger.info( "onTextChanged", view );
if ( ( view != null ) && ( view.getContent() instanceof EditableDrawable ) ) {
final EditableDrawable editable = (EditableDrawable) view.getContent();
if ( !editable.isEditing() ) return;
editable.setText( s.toString() );
if ( topHv.equals( view ) ) {
editTopButton.setText( s );
clearButtonTop.setVisibility( s != null && s.length() > 0 ? View.VISIBLE : View.INVISIBLE );
} else if ( bottomHv.equals( view ) ) {
editBottomButton.setText( s );
clearButtonBottom.setVisibility( s != null && s.length() > 0 ? View.VISIBLE : View.INVISIBLE );
}
view.forceUpdate();
setIsChanged( true );
}
}
};
/*
* (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( DrawableHighlightView newFocus, DrawableHighlightView oldFocus ) {
mLogger.info( "onFocusChange", newFocus, oldFocus );
if ( oldFocus != null ) {
if ( newFocus == null ) {
endEditView( oldFocus );
}
}
}
/**
* Terminates an edit view.
*
* @param hv
* the hv
*/
private void endEditView( DrawableHighlightView hv ) {
EditableDrawable text = (EditableDrawable) hv.getContent();
mLogger.info( "endEditView", text.isEditing() );
if ( text.isEditing() ) {
text.endEdit();
endEditText( hv );
}
CharSequence value = text.getText();
if ( topHv.equals( hv ) ) {
editTopButton.setText( value );
clearButtonTop.setVisibility( value != null && value.length() > 0 ? View.VISIBLE : View.INVISIBLE );
} else if ( bottomHv.equals( hv ) ) {
editBottomButton.setText( value );
clearButtonBottom.setVisibility( value != null && value.length() > 0 ? View.VISIBLE : View.INVISIBLE );
}
}
/**
* Begins an edit view.
*
* @param hv
* the hv
*/
private void beginEditView( DrawableHighlightView hv ) {
mLogger.info( "beginEditView" );
final EditableDrawable text = (EditableDrawable) hv.getContent();
if ( hv == topHv ) {
endEditView( bottomHv );
} else {
endEditView( topHv );
}
if ( !text.isEditing() ) {
text.beginEdit();
beginEditText( hv );
}
}
private void clearEditView( DrawableHighlightView hv ) {
final MemeTextDrawable text = (MemeTextDrawable) hv.getContent();
text.setText( "" );
text.invalidateSelf();
hv.forceUpdate();
}
/*
* (non-Javadoc)
*
* @see
* com.aviary.android.feather.widget.ImageViewDrawableOverlay.OnDrawableEventListener#onDown(com.aviary.android.feather.widget
* .DrawableHighlightView)
*/
@Override
public void onDown( DrawableHighlightView view ) {
}
/*
* (non-Javadoc)
*
* @see
* com.aviary.android.feather.widget.ImageViewDrawableOverlay.OnDrawableEventListener#onMove(com.aviary.android.feather.widget
* .DrawableHighlightView)
*/
@Override
public void onMove( DrawableHighlightView view ) {}
/*
* (non-Javadoc)
*
* @see
* com.aviary.android.feather.widget.ImageViewDrawableOverlay.OnDrawableEventListener#onClick(com.aviary.android.feather.widget
* .DrawableHighlightView)
*/
@Override
public void onClick( DrawableHighlightView view ) {
if ( view != null ) {
if ( view.getContent() instanceof EditableDrawable ) {
beginEditView( view );
}
}
}
/**
* Begin edit text.
*
* @param view
* the view
*/
private void beginEditText( final DrawableHighlightView view ) {
mLogger.info( "beginEditText", view );
EditText editText = null;
if ( view == topHv ) {
editText = editTopText;
} else if ( view == bottomHv ) {
editText = editBottomText;
}
if ( editText != null ) {
mEditTextWatcher.view = null;
editText.removeTextChangedListener( mEditTextWatcher );
final EditableDrawable editable = (EditableDrawable) view.getContent();
final String oldText = (String) editable.getText();
editText.setText( oldText );
editText.setSelection( editText.length() );
editText.setImeOptions( EditorInfo.IME_ACTION_DONE );
editText.requestFocusFromTouch();
Handler handler = new Handler();
ResultReceiver receiver = new ResultReceiver( handler );
if ( !mInputManager.showSoftInput( editText, 0, receiver ) ) {
mInputManager.toggleSoftInput( InputMethodManager.SHOW_FORCED, 0 ); // TODO: verify
}
mEditTextWatcher.view = view;
editText.setOnEditorActionListener( this );
editText.addTextChangedListener( mEditTextWatcher );
( (ImageViewDrawableOverlay) mImageView ).setSelectedHighlightView( view );
( (EditableDrawable) view.getContent() ).setText( ( (EditableDrawable) view.getContent() ).getText() );
view.forceUpdate();
}
}
/**
* End edit text.
*
* @param view
* the view
*/
private void endEditText( final DrawableHighlightView view ) {
mLogger.info( "endEditText", view );
mEditTextWatcher.view = null;
EditText editText = null;
if ( view == topHv )
editText = editTopText;
else if ( view == bottomHv ) editText = editBottomText;
if ( editText != null ) {
editText.removeTextChangedListener( mEditTextWatcher );
if ( mInputManager.isActive( editText ) ) {
mInputManager.hideSoftInputFromWindow( editText.getWindowToken(), 0 );
}
editText.clearFocus();
}
// this will send the focus to the bottom panel
// but also creating a bad visual effect
//mOptionView.requestFocus();
}
/**
* Creates the type face used for meme.
*/
private void createTypeFace() {
try {
mTypeface = TypefaceUtils.createFromAsset( getContext().getBaseContext().getAssets(), fontName );
} catch ( Exception e ) {
mTypeface = Typeface.DEFAULT;
}
}
@Override
public void onLayoutChanged( boolean changed, int left, int top, int right, int bottom ) {
if ( changed ) {
final Matrix mImageMatrix = mImageView.getImageViewMatrix();
float[] matrixValues = getMatrixValues( mImageMatrix );
final float w = mBitmap.getWidth();
final float h = mBitmap.getHeight();
final float scale = matrixValues[Matrix.MSCALE_X];
if ( topHv != null ) {
MemeTextDrawable text = (MemeTextDrawable) topHv.getContent();
text.setContentSize( w * scale, h * scale );
}
if ( bottomHv != null ) {
MemeTextDrawable text = (MemeTextDrawable) bottomHv.getContent();
text.setContentSize( w * scale, h * scale );
}
}
}
}