package com.aviary.android.feather.effects;
import it.sephiroth.android.library.imagezoom.ImageViewTouch;
import java.util.List;
import java.util.concurrent.Callable;
import org.json.JSONException;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import com.aviary.android.feather.Constants;
import com.aviary.android.feather.R;
import com.aviary.android.feather.library.content.FeatherIntent;
import com.aviary.android.feather.library.filters.EffectFilter;
import com.aviary.android.feather.library.filters.FilterLoaderFactory;
import com.aviary.android.feather.library.filters.FilterLoaderFactory.Filters;
import com.aviary.android.feather.library.filters.INativeFilter;
import com.aviary.android.feather.library.filters.NativeFilter;
import com.aviary.android.feather.library.moa.MoaResult;
import com.aviary.android.feather.library.opengl.GLUtils;
import com.aviary.android.feather.library.services.EffectContext;
import com.aviary.android.feather.library.services.ImageCacheService;
import com.aviary.android.feather.library.services.PluginService.PluginError;
import com.aviary.android.feather.library.threading.Future;
import com.aviary.android.feather.library.threading.FutureListener;
import com.aviary.android.feather.opengl.AviaryGLSurfaceView;
import com.aviary.android.feather.opengl.AviaryGLSurfaceView.GLRendererListener;
import com.aviary.android.feather.utils.UIUtils;
import com.aviary.android.feather.widget.ImageSwitcher;
public class EffectsPanel extends BordersPanel {
/** enable/disable opengl for this panel */
private boolean mOpenGlEnabled;
/** opengl view */
private AviaryGLSurfaceView mOpenGlView;
/** enable/disable fast preview. */
private boolean mEnableFastPreview = false;
private Bitmap mSmallPreview;
private static final int PREVIEW_SCALE_FACTOR = 4;
public EffectsPanel( EffectContext context ) {
super( context, FeatherIntent.PluginType.TYPE_FILTER );
checkOpenGL( context );
mIapNotificationIconId = R.drawable.feather_effects_popup_icon;
}
@Override
public void onCreate( Bitmap bitmap ) {
super.onCreate( bitmap );
mLogger.info( "OpenGL enabled: " + mOpenGlEnabled );
mLogger.info( "FastPreview enabled: "+ mEnableFastPreview );
if ( mOpenGlEnabled ) {
mOpenGlView = (AviaryGLSurfaceView) getContentView().findViewById( R.id.imagegl );
mOpenGlView.setOnGlRendererListener( mGLRendererListener );
}
}
@Override
protected void onPostActivate() {
if ( !mOpenGlEnabled ) {
super.onPostActivate();
}
}
@Override
public void onDeactivate() {
super.onDeactivate();
if ( mOpenGlEnabled ) {
mOpenGlView.setOnGlRendererListener( null );
mOpenGlView.setVisibility( View.GONE );
}
}
@Override
protected void onDispose() {
super.onDispose();
if ( mSmallPreview != null && !mSmallPreview.isRecycled() ) {
mLogger.log( "Dispose the small preview" );
mSmallPreview.recycle();
}
mSmallPreview = null;
}
@Override
protected void onGenerateResult() {
mLogger.log( "onGenerateResult" );
if ( !mOpenGlEnabled ) {
super.onGenerateResult();
} else {
// opengl-enabled
if ( mIsRendering ) {
FutureListener<Boolean> listener = new FutureListener<Boolean>() {
@Override
public void onFutureDone( Future<Boolean> arg0 ) {
mLogger.log( "write bitmap completed!" );
getHandler().post( new Runnable() {
@Override
public void run() {
onComplete( mPreview, mActions );
}
} );
}
};
mOpenGlView.writeBitmap( mPreview, listener );
} else {
super.onGenerateResult();
}
}
}
@Override
protected void onProgressEnd() {
if ( !mOpenGlEnabled && !mEnableFastPreview ) {
super.onProgressModalEnd();
} else {
super.onProgressEnd();
}
}
@Override
protected void onProgressStart() {
if ( !mOpenGlEnabled && !mEnableFastPreview ) {
super.onProgressModalStart();
} else {
super.onProgressStart();
}
}
@Override
protected FramesListAdapter createListAdapter( Context context, List<EffectPack> result ) {
return new EffectsListAdapter( context, R.layout.feather_effect_thumb, R.layout.feather_effect_external_thumb, R.layout.feather_stickers_pack_divider_empty,
R.layout.feather_getmore_stickers_thumb, R.layout.feather_getmore_stickers_thumb_inverted, result );
}
@Override
protected RenderTask createRenderTask( int position ) {
return new EffectsRenderTask( position );
}
@Override
public View makeView() {
View view = super.makeView();
if ( mEnableFastPreview ) {
( (ImageViewTouch) view ).setScrollEnabled( false );
( (ImageViewTouch) view ).setScaleEnabled( false );
}
return view;
}
@Override
protected View generateContentView( LayoutInflater inflater ) {
if( !mOpenGlEnabled ) {
mEnableFastPreview = Constants.getFastPreviewEnabled();
}
if( !mOpenGlEnabled )
return super.generateContentView( inflater );
else
return inflater.inflate( R.layout.feather_native_effects_content_gl, null );
}
@Override
public void onClosing() {
if ( mOpenGlEnabled ) {
// TODO: unload opengl stuff here...
}
super.onClosing();
}
@Override
protected void initContentImage( ImageSwitcher imageView ) {
if ( !mOpenGlEnabled ) {
Animation inAnimation = null;
Animation outAnimation = null;
if ( mEnableFastPreview ) {
try {
mSmallPreview = Bitmap.createBitmap( mBitmap.getWidth() / PREVIEW_SCALE_FACTOR, mBitmap.getHeight() / PREVIEW_SCALE_FACTOR, Config.ARGB_8888 );
inAnimation = AnimationUtils.loadAnimation( getContext().getBaseContext(), android.R.anim.fade_in );
outAnimation = AnimationUtils.loadAnimation( getContext().getBaseContext(), android.R.anim.fade_out );
} catch ( OutOfMemoryError e ) {
mEnableFastPreview = false;
}
}
imageView.setSwitchEnabled( mEnableFastPreview );
imageView.setFactory( this );
if( mEnableFastPreview ){
imageView.setImageBitmap( mBitmap, true, null, Float.MAX_VALUE );
if( null != inAnimation && null != outAnimation ) {
imageView.setInAnimation( inAnimation );
imageView.setOutAnimation( outAnimation );
}
} else {
imageView.setImageBitmap( mBitmap, true, getContext().getCurrentImageViewMatrix(), Float.MAX_VALUE );
}
imageView.setAnimateFirstView( false );
}
}
private void checkOpenGL( final EffectContext context ) {
// check if OpenGL 2.0 is available for this application
if ( GLUtils.getGlEsEnabled( context.getBaseContext() ) && getPluginType() == FeatherIntent.PluginType.TYPE_FILTER ) {
mOpenGlEnabled = GLUtils.getGlEsVersion( context.getBaseContext() ) >= 0x20000;
} else {
mOpenGlEnabled = false;
}
}
@Override
protected INativeFilter loadNativeFilter( final EffectPack pack, int position, final CharSequence label, boolean hires ) {
EffectFilter filter = (EffectFilter) FilterLoaderFactory.get( Filters.EFFECTS );
filter.setEffectName( label );
return filter;
}
@Override
protected INativeFilter loadNativeFilterForThumbnail( final EffectPack pack, int position, final CharSequence label ) {
return loadNativeFilter( pack, position, label, false );
}
@Override
protected CharSequence[] getOptionalEffectsLabels() {
return null;
}
@Override
protected CharSequence[] getOptionalEffectsNames() {
return null;
}
private void renderFirstTime( String label ) {
mLogger.log( "renderFirstTime: " + label );
FutureListener<Boolean> listener = new FutureListener<Boolean>() {
@Override
public void onFutureDone( Future<Boolean> arg0 ) {
mLogger.log( "renderedFirstTime!" );
mOpenGlView.requestRender();
contentReady();
}
};
mOpenGlView.executeEffect( label, mPreview, true, listener );
}
GLRendererListener mGLRendererListener = new GLRendererListener() {
boolean mInitializedFirstTime;
@Override
public void OnSurfaceCreated() {
mLogger.info( "onSurfaceCreated" );
}
@Override
public void OnSurfaceChanged( boolean changed, int width, int height ) {
mLogger.info( "onSurfaceChanged: " + changed, width, height );
if ( !mInitializedFirstTime ) {
renderFirstTime( "original" );
// register for plugins updates
mPluginService.registerOnUpdateListener( EffectsPanel.this );
updateInstalledPacks( true );
} else {
//setSelectedEffect( mHList.getItemAt( mSelectedPosition ), mSelectedPosition, true );
mHList.setSelectedPosition( mSelectedPosition, false );
}
mInitializedFirstTime = true;
}
};
protected class EffectsRenderTask extends RenderTask {
private Object mOpenGlCompleted = new Object();
FutureListener<Boolean> mOpenGlBackgroundListener = new FutureListener<Boolean>() {
@Override
public void onFutureDone( Future<Boolean> arg0 ) {
mLogger.info( "mOpenGlBackgroundListener::onFutureDone" );
synchronized ( mOpenGlCompleted ) {
mOpenGlCompleted.notify();
}
}
};
public EffectsRenderTask( int position ) {
super( position );
}
@Override
protected MoaResult initPreview( INativeFilter filter ) {
if( !mOpenGlEnabled && mEnableFastPreview ) {
try {
mLogger.log( "initPreview: " + mSmallPreview );
return filter.prepare( mBitmap, mSmallPreview, mSmallPreview.getWidth(), mSmallPreview.getHeight() );
} catch ( JSONException e ) {
e.printStackTrace();
}
}
return null;
}
@Override
public void doFullPreviewInBackground( final String effectName ) {
mLogger.log( "doFullPreviewInBackground: " + effectName );
if( mOpenGlEnabled ) {
mOpenGlView.executeEffect( effectName, mPreview, true, mOpenGlBackgroundListener ); // <-- listener here!
synchronized ( mOpenGlCompleted ) {
try {
mOpenGlCompleted.wait();
} catch ( InterruptedException e ) {
e.printStackTrace();
}
}
} else {
super.doFullPreviewInBackground( effectName );
}
}
@Override
public Bitmap doInBackground( EffectPack... params ) {
mLogger.info( "doInBackground" );
Bitmap result = super.doInBackground( params );
if( mOpenGlEnabled ){
return mPreview;
}
return result;
}
@Override
protected void onRestoreOriginalBitmap() {
mLogger.info( "onRestoreOriginalBitmap" );
if( mOpenGlEnabled ) {
mOpenGlView.requestRender();
} else {
super.onRestoreOriginalBitmap();
}
}
@Override
protected void onApplyNewBitmap( Bitmap result ) {
mLogger.info( "onApplyNewBitmap" );
if( mOpenGlEnabled ) {
mOpenGlView.requestRender();
} else {
super.onApplyNewBitmap( result );
}
}
}
class EffectsListAdapter extends FramesListAdapter {
public EffectsListAdapter( Context context, int mainResId, int externalResId, int dividerResId, int altResId, int altResId2, List<EffectPack> objects ) {
super( context, mainResId, externalResId, dividerResId, altResId, altResId2, objects );
}
@Override
protected Callable<Bitmap> createExternalContentCallable( String iconUrl ) {
return new ExternalEffectsThumbnailCallable( iconUrl, mCacheService, mExternalFolderIcon, this.getContext().getResources(), R.drawable.feather_iap_dialog_image_na );
}
@Override
protected Callable<Bitmap> createContentCallable( EffectPack item, int position, String effectName ) {
return new FilterThumbnailCallable( loadNativeFilterForThumbnail( item, position, effectName ), effectName, mThumbBitmap, item.mStatus == PluginError.NoError, updateArrowBitmap );
}
@Override
protected BitmapDrawable getExternalBackgroundDrawable( Context context ) {
return (BitmapDrawable) context.getResources().getDrawable( R.drawable.feather_effects_pack_background );
}
}
/**
* Render the passed effect in a thumbnail
*
* @author alessandro
*
*/
static class FilterThumbnailCallable implements Callable<Bitmap> {
INativeFilter mFilter;
String mEffectName;
Bitmap srcBitmap;
Bitmap invalidBitmap;
boolean isValid;
public FilterThumbnailCallable( INativeFilter filter, String effectName, Bitmap bitmap, boolean valid, Bitmap invalid_bitmap ) {
mEffectName = effectName;
mFilter = filter;
srcBitmap = bitmap;
isValid = valid;
invalidBitmap = invalid_bitmap;
}
@Override
public Bitmap call() throws Exception {
if ( null == mFilter ) {
mFilter = new NativeFilter( "undefined" );
}
actionsForRoundedThumbnail( isValid, mFilter );
Bitmap result = mFilter.execute( srcBitmap, null, 1, 1 );
if ( !isValid ) {
addUpdateArrow( result );
}
return result;
}
void addUpdateArrow( Bitmap bitmap ) {
if ( null != invalidBitmap && !invalidBitmap.isRecycled() ) {
final double w = Math.floor( bitmap.getWidth() * 0.75 );
final double h = Math.floor( bitmap.getHeight() * 0.75 );
final int paddingx = (int) ( bitmap.getWidth() - w ) / 2;
final int paddingy = (int) ( bitmap.getHeight() - h ) / 2;
Paint paint = new Paint( Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG );
Rect src = new Rect( 0, 0, invalidBitmap.getWidth(), invalidBitmap.getHeight() );
Rect dst = new Rect( paddingx, paddingy, paddingx + (int) w, paddingy + (int) h );
Canvas canvas = new Canvas( bitmap );
canvas.drawBitmap( invalidBitmap, src, dst, paint );
}
}
}
static class ExternalEffectsThumbnailCallable extends ExternalFramesThumbnailCallable {
public ExternalEffectsThumbnailCallable( String uri, ImageCacheService cacheService, BitmapDrawable folderBackground, Resources resources, int fallbackResId ) {
super( uri, cacheService, folderBackground, resources, fallbackResId );
}
@SuppressWarnings("deprecation")
@Override
Bitmap generateBitmap( Bitmap icon ) {
return UIUtils.drawFolderBitmap( mFolder, new BitmapDrawable( icon ), 1.3f );
}
}
}