package com.aviary.android.feather.effects;
import java.util.HashSet;
import org.json.JSONException;
import android.R.attr;
import android.content.Context;
import android.content.res.Configuration;
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.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.os.AsyncTask;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.aviary.android.feather.R;
import com.aviary.android.feather.graphics.CropCheckboxDrawable;
import com.aviary.android.feather.graphics.DefaultGalleryCheckboxDrawable;
import com.aviary.android.feather.library.filters.CropFilter;
import com.aviary.android.feather.library.filters.FilterLoaderFactory;
import com.aviary.android.feather.library.filters.FilterLoaderFactory.Filters;
import com.aviary.android.feather.library.moa.MoaActionList;
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.ReflectionUtils;
import com.aviary.android.feather.library.utils.ReflectionUtils.ReflectionException;
import com.aviary.android.feather.library.utils.SystemUtils;
import com.aviary.android.feather.utils.UIUtils;
import com.aviary.android.feather.widget.AdapterView;
import com.aviary.android.feather.widget.CropImageView;
import com.aviary.android.feather.widget.Gallery;
import com.aviary.android.feather.widget.Gallery.OnItemsScrollListener;
import com.aviary.android.feather.widget.HighlightView;
// TODO: Auto-generated Javadoc
/**
* The Class CropPanel.
*/
public class CropPanel extends AbstractContentPanel {
Gallery mGallery;
String[] mCropNames, mCropValues;
View mSelected;
int mSelectedPosition = 0;
boolean mIsPortrait = true;
final static int noImage = 0;
HashSet<Integer> nonInvertOptions = new HashSet<Integer>();
/* whether to use inversion and photo size detection */
boolean strict = false;
/** Whether or not the proportions are inverted */
boolean isChecked = false;
/**
* Instantiates a new crop panel.
*
* @param context
* the context
*/
public CropPanel( EffectContext context ) {
super( context );
}
private void invertRatios( String[] names, String[] values ) {
for ( int i = 0; i < names.length; i++ ) {
if ( names[i].contains( ":" ) ) {
String temp = names[i];
String[] splitted = temp.split( "[:]" );
String mNewOptionName = splitted[1] + ":" + splitted[0];
names[i] = mNewOptionName;
}
if ( values[i].contains( ":" ) ) {
String temp = values[i];
String[] splitted = temp.split( "[:]" );
String mNewOptionValue = splitted[1] + ":" + splitted[0];
values[i] = mNewOptionValue;
}
}
}
private void populateInvertOptions( HashSet<Integer> options, String[] cropValues ) {
for ( int i = 0; i < cropValues.length; i++ ) {
String value = cropValues[i];
String[] values = value.split( ":" );
int x = Integer.parseInt( values[0] );
int y = Integer.parseInt( values[1] );
if ( x == y ) {
options.add( i );
}
}
}
/*
* (non-Javadoc)
*
* @see com.aviary.android.feather.effects.AbstractEffectPanel#onCreate(android.graphics.Bitmap)
*/
@Override
public void onCreate( Bitmap bitmap ) {
super.onCreate( bitmap );
ConfigService config = getContext().getService( ConfigService.class );
mFilter = FilterLoaderFactory.get( Filters.CROP );
mCropNames = config.getStringArray( R.array.feather_crop_names );
mCropValues = config.getStringArray( R.array.feather_crop_values );
strict = config.getBoolean( R.integer.feather_crop_invert_policy );
if ( !strict ) {
if ( bitmap.getHeight() > bitmap.getWidth() ) {
mIsPortrait = true;
} else {
mIsPortrait = false;
}
// configure options that will not invert
populateInvertOptions( nonInvertOptions, mCropValues );
if ( mIsPortrait ) {
invertRatios( mCropNames, mCropValues );
}
}
mSelectedPosition = config.getInteger( R.integer.feather_crop_selected_value );
mImageView = (CropImageView) getContentView().findViewById( R.id.crop_image_view );
mImageView.setDoubleTapEnabled( false );
int minAreaSize = config.getInteger( R.integer.feather_crop_min_size );
( (CropImageView) mImageView ).setMinCropSize( minAreaSize );
mGallery = (Gallery) getOptionView().findViewById( R.id.gallery );
mGallery.setCallbackDuringFling( false );
mGallery.setSpacing( 0 );
mGallery.setAdapter( new GalleryAdapter( getContext().getBaseContext(), mCropNames ) );
mGallery.setSelection( mSelectedPosition, false, true );
}
/*
* (non-Javadoc)
*
* @see com.aviary.android.feather.effects.AbstractEffectPanel#onActivate()
*/
@Override
public void onActivate() {
super.onActivate();
int position = mGallery.getSelectedItemPosition();
final double ratio = calculateAspectRatio( position, false );
disableHapticIsNecessary( mGallery );
setIsChanged( true );
contentReady();
mGallery.setOnItemsScrollListener( new OnItemsScrollListener() {
@Override
public void onScrollFinished( AdapterView<?> parent, View view, int position, long id ) {
if ( !isActive() ) return;
if ( position == mSelectedPosition ) {
if ( !strict && !nonInvertOptions.contains( position ) ) {
isChecked = !isChecked;
CropImageView cview = (CropImageView) mImageView;
double currentAspectRatio = cview.getAspectRatio();
HighlightView hv = cview.getHighlightView();
if ( !cview.getAspectRatioIsFixed() && hv != null ) {
currentAspectRatio = (double) hv.getDrawRect().width() / (double) hv.getDrawRect().height();
}
double invertedAspectRatio = 1 / currentAspectRatio;
cview.setAspectRatio( invertedAspectRatio, cview.getAspectRatioIsFixed() );
invertRatios( mCropNames, mCropValues );
mGallery.invalidateViews();
}
} else {
double ratio = calculateAspectRatio( position, false );
setCustomRatio( ratio, ratio != 0 );
}
updateSelection( view, position );
}
@Override
public void onScrollStarted( AdapterView<?> parent, View view, int position, long id ) {}
@Override
public void onScroll( AdapterView<?> parent, View view, int position, long id ) {}
} );
getOptionView().getHandler().post( new Runnable() {
@Override
public void run() {
createCropView( ratio, ratio != 0 );
updateSelection( (View) mGallery.getSelectedView(), mGallery.getSelectedItemPosition() );
}
} );
}
/**
* Calculate aspect ratio.
*
* @param position
* the position
* @param inverted
* the inverted
* @return the double
*/
private double calculateAspectRatio( int position, boolean inverted ) {
String value = mCropValues[position];
String[] values = value.split( ":" );
if ( values.length == 2 ) {
int aspectx = Integer.parseInt( inverted ? values[1] : values[0] );
int aspecty = Integer.parseInt( inverted ? values[0] : values[1] );
if ( aspectx == -1 ) {
aspectx = inverted ? mBitmap.getHeight() : mBitmap.getWidth();
}
if ( aspecty == -1 ) {
aspecty = inverted ? mBitmap.getWidth() : mBitmap.getHeight();
}
double ratio = 0;
if ( aspectx != 0 && aspecty != 0 ) {
ratio = (double) aspectx / (double) aspecty;
}
return ratio;
}
return 0;
}
/*
* (non-Javadoc)
*
* @see com.aviary.android.feather.effects.AbstractEffectPanel#onDestroy()
*/
@Override
public void onDestroy() {
mImageView.clear();
( (CropImageView) mImageView ).setOnHighlightSingleTapUpConfirmedListener( null );
super.onDestroy();
}
/*
* (non-Javadoc)
*
* @see com.aviary.android.feather.effects.AbstractEffectPanel#onDeactivate()
*/
@Override
public void onDeactivate() {
super.onDeactivate();
}
/**
* Creates the crop view.
*
* @param aspectRatio
* the aspect ratio
*/
private void createCropView( double aspectRatio, boolean isFixed ) {
( (CropImageView) mImageView ).setImageBitmap( mBitmap, aspectRatio, isFixed );
}
/**
* Sets the custom ratio.
*
* @param aspectRatio
* the aspect ratio
* @param isFixed
* the is fixed
*/
private void setCustomRatio( double aspectRatio, boolean isFixed ) {
( (CropImageView) mImageView ).setAspectRatio( aspectRatio, isFixed );
}
/**
* Update selection.
*
* @param newSelection
* the new selection
* @param position
* the position
*/
protected void updateSelection( View newSelection, int position ) {
if ( mSelected != null ) {
String label = (String) mSelected.getTag();
if ( label != null ) {
View textview = mSelected.findViewById( R.id.text );
if ( null != textview ) {
( (TextView) textview ).setText( getString( label ) );
}
View arrow = mSelected.findViewById( R.id.invertCropArrow );
if ( null != arrow ) {
arrow.setVisibility( View.INVISIBLE );
}
}
mSelected.setSelected( false );
}
mSelected = newSelection;
mSelectedPosition = position;
if ( mSelected != null ) {
mSelected = newSelection;
mSelected.setSelected( true );
View arrow = mSelected.findViewById( R.id.invertCropArrow );
if ( null != arrow && !nonInvertOptions.contains( position ) && !strict ) {
arrow.setVisibility( View.VISIBLE );
arrow.setSelected( isChecked );
} else {
arrow.setVisibility( View.INVISIBLE );
}
}
}
/*
* (non-Javadoc)
*
* @see com.aviary.android.feather.effects.AbstractEffectPanel#onGenerateResult()
*/
@Override
protected void onGenerateResult() {
Rect crop_rect = ( (CropImageView) mImageView ).getHighlightView().getCropRect();
GenerateResultTask task = new GenerateResultTask( crop_rect );
task.execute( mBitmap );
}
/**
* Generate bitmap.
*
* @param bitmap
* the bitmap
* @param cropRect
* the crop rect
* @return the bitmap
*/
@SuppressWarnings("unused")
private Bitmap generateBitmap( Bitmap bitmap, Rect cropRect ) {
Bitmap croppedImage;
int width = cropRect.width();
int height = cropRect.height();
croppedImage = Bitmap.createBitmap( width, height, Bitmap.Config.RGB_565 );
Canvas canvas = new Canvas( croppedImage );
Rect dstRect = new Rect( 0, 0, width, height );
canvas.drawBitmap( mBitmap, cropRect, dstRect, null );
return croppedImage;
}
/**
* The Class GenerateResultTask.
*/
class GenerateResultTask extends AsyncTask<Bitmap, Void, Bitmap> {
/** The m crop rect. */
Rect mCropRect;
MoaActionList mActionList;
/**
* Instantiates a new generate result task.
*
* @param rect
* the rect
*/
public GenerateResultTask( Rect rect ) {
mCropRect = rect;
}
/*
* (non-Javadoc)
*
* @see android.os.AsyncTask#onPreExecute()
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
onProgressModalStart();
}
/*
* (non-Javadoc)
*
* @see android.os.AsyncTask#doInBackground(Params[])
*/
@Override
protected Bitmap doInBackground( Bitmap... arg0 ) {
final Bitmap bitmap = arg0[0];
MoaPointParameter topLeft = new MoaPointParameter();
topLeft.setValue( (double) mCropRect.left / bitmap.getWidth(), (double) mCropRect.top / bitmap.getHeight() );
MoaPointParameter size = new MoaPointParameter();
size.setValue( (double) mCropRect.width() / bitmap.getWidth(), (double) mCropRect.height() / bitmap.getHeight() );
( (CropFilter) mFilter ).setTopLeft( topLeft );
( (CropFilter) mFilter ).setSize( size );
mActionList = (MoaActionList) ( (CropFilter) mFilter ).getActions().clone();
try {
return ( (CropFilter) mFilter ).execute( arg0[0], null, 1, 1 );
} catch ( JSONException e ) {
e.printStackTrace();
}
return arg0[0];
}
/*
* (non-Javadoc)
*
* @see android.os.AsyncTask#onPostExecute(java.lang.Object)
*/
@Override
protected void onPostExecute( Bitmap result ) {
super.onPostExecute( result );
onProgressModalEnd();
( (CropImageView) mImageView ).setImageBitmap( result, ( (CropImageView) mImageView ).getAspectRatio(),
( (CropImageView) mImageView ).getAspectRatioIsFixed() );
( (CropImageView) mImageView ).setHighlightView( null );
onComplete( result, mActionList );
}
}
@Override
protected View generateContentView( LayoutInflater inflater ) {
View view = inflater.inflate( R.layout.feather_crop_content, null );
if ( SystemUtils.isHoneyComb() ) {
// Honeycomb bug with canvas clip
try {
ReflectionUtils.invokeMethod( view, "setLayerType", new Class[] { int.class, Paint.class }, 1, null );
} catch ( ReflectionException e ) {}
}
return view;
}
@Override
protected ViewGroup generateOptionView( LayoutInflater inflater, ViewGroup parent ) {
return (ViewGroup) inflater.inflate( R.layout.feather_crop_panel, parent, false );
}
@Override
public Matrix getContentDisplayMatrix() {
return mImageView.getDisplayMatrix();
}
@Override
public void onConfigurationChanged( Configuration newConfig, Configuration oldConfig ) {
super.onConfigurationChanged( newConfig, oldConfig );
}
private String getString( String input ) {
int id = getContext().getBaseContext().getResources()
.getIdentifier( input, "string", getContext().getBaseContext().getPackageName() );
if ( id > 0 ) {
return getContext().getBaseContext().getResources().getString( id );
}
return input;
}
class GalleryAdapter extends BaseAdapter {
private String[] mStrings;
LayoutInflater mLayoutInflater;
Resources mRes;
private static final int VALID_POSITION = 0;
private static final int INVALID_POSITION = 1;
/**
* Instantiates a new gallery adapter.
*
* @param context
* the context
* @param values
* the values
*/
public GalleryAdapter( Context context, String[] values ) {
mLayoutInflater = UIUtils.getLayoutInflater();
mStrings = values;
mRes = getContext().getBaseContext().getResources();
}
/*
* (non-Javadoc)
*
* @see android.widget.Adapter#getCount()
*/
@Override
public int getCount() {
return mStrings.length;
}
public void updateStrings() {
}
/*
* (non-Javadoc)
*
* @see android.widget.Adapter#getItem(int)
*/
@Override
public Object getItem( int position ) {
return mStrings[position];
}
/*
* (non-Javadoc)
*
* @see android.widget.Adapter#getItemId(int)
*/
@Override
public long getItemId( int position ) {
return 0;
}
/*
* (non-Javadoc)
*
* @see android.widget.BaseAdapter#getViewTypeCount()
*/
@Override
public int getViewTypeCount() {
return 2;
}
/*
* (non-Javadoc)
*
* @see android.widget.BaseAdapter#getItemViewType(int)
*/
@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( final int position, View convertView, ViewGroup parent ) {
int type = getItemViewType( position );
final View view;
if ( convertView == null ) {
if ( type == VALID_POSITION ) {
// use the default crop checkbox view
view = mLayoutInflater.inflate( R.layout.feather_crop_button, mGallery, false );
StateListDrawable st = new StateListDrawable();
Drawable unselectedBackground = new CropCheckboxDrawable( mRes, false, null, 1.0f, 0, 0 );
Drawable selectedBackground = new CropCheckboxDrawable( mRes, true, null, 1.0f, 0, 0 );
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_checkbox_button, mGallery, false );
Drawable unselectedBackground = new DefaultGalleryCheckboxDrawable( mRes, false );
view.setBackgroundDrawable( unselectedBackground );
}
} else {
view = convertView;
}
view.setSelected( mSelectedPosition == position );
if ( type == VALID_POSITION ) {
Object item = getItem( position );
view.setTag( item );
if ( null != item ) {
TextView text = (TextView) view.findViewById( R.id.text );
String textValue = "";
if ( position >= 0 && position < mStrings.length ) textValue = mStrings[position];
if ( null != text ) text.setText( getString( textValue ) );
View arrow = view.findViewById( R.id.invertCropArrow );
int targetVisibility;
if ( mSelectedPosition == position && !strict ) {
mSelected = view;
if ( null != arrow && !nonInvertOptions.contains( position ) ) {
targetVisibility = View.VISIBLE;
} else {
targetVisibility = View.INVISIBLE;
}
} else {
targetVisibility = View.INVISIBLE;
}
if( null != arrow ){
arrow.setVisibility( targetVisibility );
arrow.setSelected( isChecked );
}
}
}
return view;
}
}
}