package com.aviary.android.feather.effects; import java.io.InputStream; import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Point; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.ViewTreeObserver.OnScrollChangedListener; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.ViewFlipper; import com.aviary.android.feather.Constants; import com.aviary.android.feather.FilterManager.FeatherContext; import com.aviary.android.feather.R; import com.aviary.android.feather.async_tasks.AsyncImageManager; import com.aviary.android.feather.effects.BordersPanel.ViewHolder; import com.aviary.android.feather.effects.SimpleStatusMachine.OnStatusChangeListener; import com.aviary.android.feather.graphics.PluginDividerDrawable; import com.aviary.android.feather.graphics.RepeatableHorizontalDrawable; import com.aviary.android.feather.library.content.FeatherIntent; import com.aviary.android.feather.library.content.FeatherIntent.PluginType; import com.aviary.android.feather.library.filters.StickerFilter; import com.aviary.android.feather.library.graphics.drawable.FeatherDrawable; import com.aviary.android.feather.library.graphics.drawable.StickerDrawable; import com.aviary.android.feather.library.moa.MoaActionFactory; import com.aviary.android.feather.library.moa.MoaActionList; import com.aviary.android.feather.library.plugins.FeatherExternalPack; import com.aviary.android.feather.library.plugins.FeatherInternalPack; import com.aviary.android.feather.library.plugins.FeatherPack; import com.aviary.android.feather.library.plugins.PluginManager; import com.aviary.android.feather.library.plugins.PluginManager.ExternalPlugin; import com.aviary.android.feather.library.plugins.PluginManager.IPlugin; import com.aviary.android.feather.library.plugins.PluginManager.InternalPlugin; import com.aviary.android.feather.library.plugins.UpdateType; import com.aviary.android.feather.library.services.ConfigService; import com.aviary.android.feather.library.services.DragControllerService; import com.aviary.android.feather.library.services.DragControllerService.DragListener; import com.aviary.android.feather.library.services.DragControllerService.DragSource; import com.aviary.android.feather.library.services.EffectContext; import com.aviary.android.feather.library.services.ImageCacheService; import com.aviary.android.feather.library.services.ImageCacheService.SimpleCachedRemoteBitmap; import com.aviary.android.feather.library.services.PluginService; import com.aviary.android.feather.library.services.PluginService.OnUpdateListener; import com.aviary.android.feather.library.services.PluginService.PluginError; import com.aviary.android.feather.library.services.PluginService.StickerType; import com.aviary.android.feather.library.services.PreferenceService; import com.aviary.android.feather.library.services.drag.DragView; import com.aviary.android.feather.library.services.drag.DropTarget; import com.aviary.android.feather.library.services.drag.DropTarget.DropTargetListener; import com.aviary.android.feather.library.tracking.Tracker; import com.aviary.android.feather.library.utils.BitmapUtils; import com.aviary.android.feather.library.utils.IOUtils; import com.aviary.android.feather.library.utils.ImageLoader; import com.aviary.android.feather.library.utils.MatrixUtils; import com.aviary.android.feather.library.utils.PackageManagerUtils; import com.aviary.android.feather.library.utils.UIConfiguration; import com.aviary.android.feather.utils.UIUtils; import com.aviary.android.feather.widget.ArrayAdapterExtended; import com.aviary.android.feather.widget.DrawableHighlightView; import com.aviary.android.feather.widget.DrawableHighlightView.OnDeleteClickListener; import com.aviary.android.feather.widget.EffectThumbLayout; import com.aviary.android.feather.widget.HorizontalFixedListView.OnItemDragListener; import com.aviary.android.feather.widget.HorizontalVariableListView; import com.aviary.android.feather.widget.HorizontalVariableListView.OnItemClickedListener; import com.aviary.android.feather.widget.IapDialog; import com.aviary.android.feather.widget.IapDialog.OnCloseListener; import com.aviary.android.feather.widget.IapNotificationLayout; import com.aviary.android.feather.widget.ImageViewDrawableOverlay; public class StickersPanel extends AbstractContentPanel implements OnUpdateListener, OnStatusChangeListener, OnScrollChangedListener, OnItemClickedListener, DragListener, DragSource, DropTargetListener, OnItemSelectedListener { private static final int STATUS_NULL = SimpleStatusMachine.INVALID_STATUS; private static final int STATUS_PACKS = 1; private static final int STATUS_STICKERS = 2; private static final int STATUS_IAP = 3; private static final int THUMBNAIL_INSET = 8; /** panel's status */ private SimpleStatusMachine mStatus; /** This panel is executing some animations */ private volatile boolean mIsAnimating; /** horizontal listview for stickers packs */ private HorizontalVariableListView mListPacks; /** horizontal listview for stickers items */ private HorizontalVariableListView mListStickers; /** view flipper for switching between lists */ private ViewFlipper mViewFlipper; /** external packs availability */ private boolean mExternalPacksEnabled; /** dialog used to alert the user about changes in the installed plugins */ private AlertDialog mUpdateDialog; /** thumbnail cache manager */ private AsyncImageManager mImageManager; /** canvas used to draw stickers */ private Canvas mCanvas; /** default width of rolls elements */ private int mPacksCellWidth; /** installed plugins */ private List<String> mInstalledPackages; /** total number of available plugins */ private int mAvailablePacks = 0; /** required services */ private PluginService mPluginService; private ConfigService mConfigService; private PreferenceService mPreferenceService; private ImageCacheService mCacheService; private DragControllerService mDragControllerService; /** should display the iap notification popup? */ private boolean mShowIapNotificationAndValue; /** max number of featured elements to display */ private int mFeaturedCount; /** iap dialog for inline previews */ private IapDialog mIapDialog; /** the notification popup */ private IapNotificationLayout mIapNotificationPopup; /** the iap notification already shown */ private boolean mIapPopupShown; /** hlist scrolled */ private boolean mScrollChanged; /** the current selected sticker pack */ private IPlugin mPlugin; private MoaActionList mActionList; private StickerFilter mCurrentFilter; /** sticker configurations */ private int mStickerHvEllipse, mStickerHvStrokeWidth, mStickerHvMinSize; private int mStickerHvPadding;; private ColorStateList mStickerHvStrokeColorStateList; private ColorStateList mStickerHvFillColorStateList; /** sticker thumbnail for the horizontal list final size */ private int mThumbSize; /** default title for featured divider */ private String mFeaturedDefaultTitle; private int mItemsGapPixelSize = 4; public StickersPanel( EffectContext context ) { super( context ); } @SuppressWarnings("deprecation") @Override public void onCreate( Bitmap bitmap ) { super.onCreate( bitmap ); mStatus = new SimpleStatusMachine(); // determine if the external packs are enabled mExternalPacksEnabled = Constants.getExternalStickersEnabled(); // init layout components mListPacks = (HorizontalVariableListView) getOptionView().findViewById( R.id.list_packs ); mListStickers = (HorizontalVariableListView) getOptionView().findViewById( R.id.list_stickers ); mViewFlipper = (ViewFlipper) getOptionView().findViewById( R.id.flipper ); mImageView = (ImageViewDrawableOverlay) getContentView().findViewById( R.id.overlay ); // init services mPluginService = getContext().getService( PluginService.class ); mConfigService = getContext().getService( ConfigService.class ); mPreferenceService = getContext().getService( PreferenceService.class ); mCacheService = getContext().getService( ImageCacheService.class ); // TODO: only for testing // mCacheService.deleteCache(); // setup the main horizontal listview mListPacks.setGravity( Gravity.BOTTOM ); mListPacks.setOverScrollMode( View.OVER_SCROLL_ALWAYS ); mListPacks.setEdgeGravityY( Gravity.BOTTOM ); // setup the stickers listview mListStickers.setGravity( Gravity.BOTTOM ); mListStickers.setOverScrollMode( View.OVER_SCROLL_ALWAYS ); mListStickers.setEdgeGravityY( Gravity.BOTTOM ); // setup the main imageview ( (ImageViewDrawableOverlay) mImageView ).setForceSingleSelection( false ); ( (ImageViewDrawableOverlay) mImageView ).setDropTargetListener( this ); ( (ImageViewDrawableOverlay) mImageView ).setScaleWithContent( true ); // create the default action list mActionList = MoaActionFactory.actionList(); // load the configuration for the sticker drawable mStickerHvEllipse = mConfigService.getInteger( R.integer.feather_sticker_highlight_ellipse ); mStickerHvStrokeWidth = mConfigService.getInteger( R.integer.feather_sticker_highlight_stroke_width ); mStickerHvStrokeColorStateList = mConfigService.getColorStateList( R.color.feather_sticker_color_stroke_selector ); mStickerHvFillColorStateList = mConfigService.getColorStateList( R.color.feather_sticker_color_fill_selector ); mStickerHvMinSize = mConfigService.getInteger( R.integer.feather_sticker_highlight_minsize ); mStickerHvPadding = mConfigService.getInteger( R.integer.feather_sticker_highlight_padding ); mFeaturedCount = mConfigService.getInteger( R.integer.feather_featured_count ); mFeaturedDefaultTitle = mConfigService.getString( R.string.feather_featured ); // update the background drawable View content = getOptionView().findViewById( R.id.background ); content.setBackgroundDrawable( RepeatableHorizontalDrawable.createFromView( content ) ); mImageManager = new AsyncImageManager(); // create the preview for the main imageview createAndConfigurePreview(); if ( android.os.Build.VERSION.SDK_INT > 8 ) { DragControllerService dragger = getContext().getService( DragControllerService.class ); dragger.addDropTarget( (DropTarget) mImageView ); dragger.setMoveTarget( mImageView ); dragger.setDragListener( this ); // TODO: remember to activate this! // dragger.activate(); setDragController( dragger ); } } @Override public void onActivate() { super.onActivate(); mImageView.setImageBitmap( mPreview, true, getContext().getCurrentImageViewMatrix(), UIConfiguration.IMAGE_VIEW_MAX_ZOOM ); //mPacksCellWidth = mConfigService.getDimensionPixelSize( R.dimen.feather_sticker_pack_cell_width ); //mPacksCellWidth = (int) ( ( Constants.SCREEN_WIDTH / UIUtils.getScreenOptimalColumnsPixels( mPacksCellWidth ) ) ); mPacksCellWidth = (int) ( ( getOptionView().findViewById( R.id.background ).getHeight() - getOptionView().findViewById( R.id.bottom_background_overlay ).getHeight() ) * 0.8 ); mItemsGapPixelSize = mConfigService.getDimensionPixelSize( R.dimen.feather_stickers_panel_items_gap ); mInstalledPackages = Collections.synchronizedList( new ArrayList<String>() ); mListPacks.setOnScrollListener( this ); mListPacks.setOnItemClickedListener( this ); mListPacks.setOnItemSelectedListener( this ); // register to status change mStatus.setOnStatusChangeListener( this ); if ( mExternalPacksEnabled ) { mPluginService.registerOnUpdateListener( this ); mStatus.setStatus( STATUS_PACKS ); } else { updateInstalledPacks( true ); } getContentView().setVisibility( View.VISIBLE ); contentReady(); } @Override public boolean onBackPressed() { mLogger.info( "onBackPressed" ); if ( mIsAnimating ) return true; if ( mStatus.getCurrentStatus() == STATUS_IAP ) { mStatus.setStatus( STATUS_PACKS ); mListPacks.setSelectedPosition( HorizontalVariableListView.INVALID_POSITION, true ); return true; } // we're in the packs status if ( mStatus.getCurrentStatus() == STATUS_PACKS ) { if ( stickersOnScreen() ) { askToLeaveWithoutApply(); return true; } return false; } // we're in the stickers status if ( mStatus.getCurrentStatus() == STATUS_STICKERS ) { if ( mExternalPacksEnabled ) { mStatus.setStatus( STATUS_PACKS ); if ( null != mPlugin ) { Tracker.recordTag( mPlugin.getLabel( FeatherIntent.PluginType.TYPE_STICKER ) + ": Cancelled" ); } return true; } else { // ok we still have a sticker in there if ( stickersOnScreen() ) { askToLeaveWithoutApply(); return true; } return false; } } return super.onBackPressed(); } @Override public boolean onCancel() { mLogger.info( "onCancel" ); // if there's an active sticker on screen // then ask if we really want to exit this panel // and discard changes if ( stickersOnScreen() ) { askToLeaveWithoutApply(); return true; } return super.onCancel(); } @Override public void onDeactivate() { super.onDeactivate(); // disable the drag controller if ( null != getDragController() ) { getDragController().deactivate(); getDragController().removeDropTarget( (DropTarget) mImageView ); getDragController().setDragListener( null ); } setDragController( null ); mPluginService.removeOnUpdateListener( this ); mStatus.setOnStatusChangeListener( null ); mListPacks.setOnScrollListener( null ); mListPacks.setOnItemClickedListener( null ); mListPacks.setOnItemSelectedListener( null ); mListStickers.setOnItemClickedListener( null ); mListStickers.setOnItemDragListener( null ); } @Override public void onDestroy() { super.onDestroy(); mCurrentFilter = null; mActionList = null; } @Override protected void onDispose() { super.onDispose(); if ( null != mImageManager ) { mImageManager.clearCache(); mImageManager.shutDownNow(); } if ( null != mInstalledPackages ) { mInstalledPackages.clear(); } mPlugin = null; mCacheService = null; mCanvas = null; } @Override protected void onGenerateResult() { onApplyCurrent(); super.onGenerateResult( mActionList ); } @Override public void onConfigurationChanged( Configuration newConfig, Configuration oldConfig ) { // TODO: To be verified super.onConfigurationChanged( newConfig, oldConfig ); mImageManager.clearCache(); if ( mStatus.getCurrentStatus() == STATUS_NULL || mStatus.getCurrentStatus() == STATUS_PACKS ) { updateInstalledPacks( false ); } else if ( mStatus.getCurrentStatus() == STATUS_STICKERS ) { loadStickers(); } else if( mStatus.getCurrentStatus() == STATUS_IAP ) { if ( mIapDialog != null ) { ViewGroup parent = (ViewGroup) mIapDialog.getParent(); if( null != parent ) { ExternalPlugin currentPlugin = mIapDialog.getPlugin(); int index = parent.indexOfChild( mIapDialog ); parent.removeView( mIapDialog ); mIapDialog = (IapDialog) UIUtils.getLayoutInflater().inflate( R.layout.feather_iap_dialog, parent, false ); mIapDialog.setLayoutAnimation( null ); parent.addView( mIapDialog, index ); mIapDialog.setPlugin( currentPlugin, FeatherIntent.PluginType.TYPE_STICKER, getContext().getBaseContext() ); } } updateInstalledPacks( false ); } } @Override protected View generateContentView( LayoutInflater inflater ) { return inflater.inflate( R.layout.feather_stickers_content, null ); } @Override protected ViewGroup generateOptionView( LayoutInflater inflater, ViewGroup parent ) { return (ViewGroup) inflater.inflate( R.layout.feather_stickers2_panel, null ); } // ///////////////////////// // OnStatusChangeListener // // ///////////////////////// @Override public void OnStatusChanged( int oldStatus, int newStatus ) { mLogger.info( "OnStatusChange: " + oldStatus + " >> " + newStatus ); switch ( newStatus ) { case STATUS_PACKS: // deactivate listeners for the stickers list mListStickers.setOnItemClickedListener( null ); mListStickers.setOnItemDragListener( null ); if ( oldStatus == STATUS_NULL ) { updateInstalledPacks( true ); } else if ( oldStatus == STATUS_STICKERS ) { mViewFlipper.setDisplayedChild( 1 ); restoreToolbarTitle(); if ( getDragController() != null ) { getDragController().deactivate(); } } else if ( oldStatus == STATUS_IAP ) { // only using back button mPlugin = null; removeIapDialog(); setApplyEnabled( true ); } break; case STATUS_STICKERS: if ( oldStatus == STATUS_PACKS ) { loadStickers(); } else if ( oldStatus == STATUS_IAP ) { removeIapDialog(); loadStickers(); setApplyEnabled( true ); } else if( oldStatus == STATUS_NULL ) { loadStickers(); } setToolbarTitle( mPlugin.getLabel( FeatherIntent.PluginType.TYPE_STICKER ) ); if ( getDragController() != null ) { getDragController().activate(); } break; case STATUS_IAP: mIapDialog = createIapDialog(); updateIapDialog(); setApplyEnabled( false ); break; default: mLogger.error( "unmanaged status change: " + oldStatus + " >> " + newStatus ); onGenericError( "unmanaged status change: " + oldStatus + " >> " + newStatus ); break; } } @Override public void OnStatusUpdated( int status ) { mLogger.info( "OnStatusUpdated: " + status ); switch ( status ) { case STATUS_IAP: updateIapDialog(); break; } } // /////////////////////////// // OnUpdateListener methods // // /////////////////////////// @Override public void onUpdate( Bundle delta ) { mLogger.info( "onUpdate" ); if ( !isActive() || !mExternalPacksEnabled ) return; if ( !validDelta( delta ) ) { mLogger.log( "Suppress the alert, no stickers in the delta bundle" ); return; } if ( mUpdateDialog != null && mUpdateDialog.isShowing() ) { mLogger.log( "dialog is already there, skip new alerts" ); return; } final int status = mStatus.getCurrentStatus(); AlertDialog dialog = null; if ( status == STATUS_NULL || status == STATUS_PACKS ) { // PACKS dialog = new AlertDialog.Builder( getContext().getBaseContext() ).setMessage( R.string.sticker_pack_updated_1 ).setPositiveButton( android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which ) { updateInstalledPacks( false ); } } ).create(); } else if ( status == STATUS_STICKERS ) { // STICKERS if ( stickersOnScreen() ) { dialog = new AlertDialog.Builder( getContext().getBaseContext() ).setMessage( R.string.sticker_pack_updated_3 ).setPositiveButton( android.R.string.yes, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which ) { onApplyCurrent(); mStatus.setStatus( STATUS_PACKS ); updateInstalledPacks( false ); } } ).setNegativeButton( android.R.string.no, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which ) { onClearCurrent( true ); mStatus.setStatus( STATUS_PACKS ); updateInstalledPacks( false ); } } ).create(); } else { dialog = new AlertDialog.Builder( getContext().getBaseContext() ).setMessage( R.string.sticker_pack_updated_2 ).setPositiveButton( android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which ) { mStatus.setStatus( STATUS_PACKS ); updateInstalledPacks( false ); } } ).create(); } } else if ( status == STATUS_IAP ) { // IAP dialog = new AlertDialog.Builder( getContext().getBaseContext() ).setMessage( R.string.sticker_pack_updated_2 ).setPositiveButton( android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which ) { mStatus.setStatus( STATUS_PACKS ); mListPacks.setSelectedPosition( HorizontalVariableListView.INVALID_POSITION, true ); updateInstalledPacks( false ); } } ).create(); } if ( dialog != null ) { mUpdateDialog = dialog; mUpdateDialog.setCancelable( false ); mUpdateDialog.show(); } } // ////////////////////////// // OnScrollChangedListener // @Override public void onScrollChanged() { mListPacks.setOnScrollListener( null ); mScrollChanged = true; if ( mExternalPacksEnabled ) { hideIapPopup( 0 ); } } // /////////////////////////// // Iap Notification methods // // /////////////////////////// /** * Based on various condition show the IAP notification popup */ private void showIapPopup() { // available packs must be > 0 // external packs must be enabled // popup never shown before if ( !mShowIapNotificationAndValue ) return; if ( mIapPopupShown ) return; mIapPopupShown = true; if ( mScrollChanged ) return; if ( !isActive() || getContext() == null || getHandler() == null ) return; if ( mIapNotificationPopup == null ) { if ( null != getContext().getBaseContext() ) { ViewGroup container = ( (FeatherContext) getContext().getBaseContext() ).activatePopupContainer(); UIUtils.getLayoutInflater().inflate( R.layout.feather_iap_notification_popup, container, true ); mIapNotificationPopup = (IapNotificationLayout) container.findViewById( R.id.iap_popup ); } } if ( mIapNotificationPopup == null ) return; Rect r = new Rect( 0, 0, 0, 200 ); Point offset = new Point(); try { getOptionView().findViewById( R.id.flipper ).getGlobalVisibleRect( r, offset ); } catch ( Throwable t ) { t.printStackTrace(); } mIapNotificationPopup.setPadding( 0, 0, 0, r.height() ); mIapNotificationPopup.setIcon( R.drawable.feather_stickers_popup_icon ); mIapNotificationPopup.setText( String.valueOf( mAvailablePacks ) ); // then finally post a delayed execution of the animation mIapNotificationPopup.show(); } /** * Hide the IAP notification popup * * @param view * @param delayMillis */ private void hideIapPopup( final long delayMillis ) { mLogger.info( "hideIapPopup: " + delayMillis ); if ( !isActive() || getHandler() == null ) return; if ( mIapNotificationPopup == null || mIapNotificationPopup.getParent() == null ) return; if ( mIapNotificationPopup.getVisibility() == View.GONE ) return; mIapNotificationPopup.hide( delayMillis ); } // ////////////////////// // OnItemClickListener // // ////////////////////// @Override public boolean onItemClick( AdapterView<?> parent, View view, int position, long id ) { Log.i( "stickers", "onItemClick: " + position ); if ( !isActive() ) return false; if ( mStatus.getCurrentStatus() == STATUS_PACKS || mStatus.getCurrentStatus() == STATUS_IAP ) { StickerEffectPack item = (StickerEffectPack) mListPacks.getAdapter().getItem( position ); // "get more" button if ( null == item ) { if ( position == 0 ) { Tracker.recordTag( "LeftGetMoreStickers : Selected" ); } else { Tracker.recordTag( "RightGetMoreStickers : Selected" ); } getContext().searchPlugin( FeatherIntent.PluginType.TYPE_STICKER ); return false; } if ( null != item ) { if ( item.isDivider ) { return false; } else if ( item.isExternal ) { // open the IAP Dialog only if current build is > froyo and app memory is >= 32 if ( android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.FROYO && Constants.getApplicationMaxMemory() >= 32 ) { mPlugin = (ExternalPlugin) item.mPluginRef; mStatus.setStatus( STATUS_IAP ); Tracker.recordTag( "Unpurchased(" + mPlugin.getLabel( FeatherIntent.PluginType.TYPE_STICKER ) + ") : Opened" ); return true; } else { // external plugin - download from the play store Tracker.recordTag( "Unpurchased(" + item.mTitle + ") : StoreButtonClicked" ); getContext().downloadPlugin( item.mPackageName.toString(), FeatherIntent.PluginType.TYPE_STICKER ); return false; } } else { // internal plugin mPlugin = (InternalPlugin) item.mPluginRef; if ( null != mPlugin ) { mStatus.setStatus( STATUS_STICKERS ); Tracker.recordTag( mPlugin.getLabel( FeatherIntent.PluginType.TYPE_STICKER ) + ": Opened" ); } return true; } } } return false; } // ///////////////////////// // OnItemSelectedListener // // ///////////////////////// @Override public void onItemSelected( AdapterView<?> parent, View view, int position, long id ) { mLogger.info( "onItemSelected: " + position ); } @Override public void onNothingSelected( AdapterView<?> parent ) { mLogger.info( "onNothingSelected" ); if ( mStatus.getCurrentStatus() == STATUS_IAP ) { mStatus.setStatus( STATUS_PACKS ); } } // //////////////////////// // Drag and Drop methods // // //////////////////////// /** * Starts the drag and drop operation * * @param parent * - the parent list * @param view * - the current view clicked * @param position * - the position in the list * @param id * - the item id * @param nativeClick * - it's a native click * @return */ private boolean startDrag( AdapterView<?> parent, View view, int position, long id, boolean animate ) { mLogger.info( "startDrag" ); if ( android.os.Build.VERSION.SDK_INT < 9 ) return false; if ( parent == null || view == null || parent.getAdapter() == null ) { return false; } if ( mStatus.getCurrentStatus() != STATUS_STICKERS ) return false; if ( mPlugin == null || !( mPlugin instanceof InternalPlugin ) ) return false; if ( null != view ) { View image = view.findViewById( R.id.image ); if ( null != image ) { final String dragInfo = (String) parent.getAdapter().getItem( position ); int size = mThumbSize; Bitmap bitmap; try { bitmap = ImageLoader.getPluginItemBitmap( (InternalPlugin) mPlugin, dragInfo, FeatherIntent.PluginType.TYPE_STICKER, StickerType.Small, size, size ); int offsetx = Math.abs( image.getWidth() - bitmap.getWidth() ) / 2; int offsety = Math.abs( image.getHeight() - bitmap.getHeight() ) / 2; return getDragController().startDrag( image, bitmap, offsetx, offsety, StickersPanel.this, dragInfo, DragControllerService.DRAG_ACTION_MOVE, animate ); } catch ( Exception e ) { e.printStackTrace(); } return getDragController().startDrag( image, StickersPanel.this, dragInfo, DragControllerService.DRAG_ACTION_MOVE, animate ); } } return false; } @Override public void setDragController( DragControllerService controller ) { mDragControllerService = controller; } @Override public DragControllerService getDragController() { return mDragControllerService; } @Override public void onDropCompleted( View arg0, boolean arg1 ) { mLogger.info( "onDropCompleted" ); mListStickers.setIsDragging( false ); } @Override public boolean onDragEnd() { mLogger.info( "onDragEnd" ); mListStickers.setIsDragging( false ); return false; } @Override public void onDragStart( DragSource arg0, Object arg1, int arg2 ) { mLogger.info( "onDragStart" ); mListStickers.setIsDragging( true ); } @Override public boolean acceptDrop( DragSource source, int x, int y, int xOffset, int yOffset, DragView dragView, Object dragInfo ) { return source == this; } @Override public void onDrop( DragSource source, int x, int y, int xOffset, int yOffset, DragView dragView, Object dragInfo ) { mLogger.info( "onDrop. source=" + source + ", dragInfo=" + dragInfo ); if ( dragInfo != null && dragInfo instanceof String ) { String sticker = (String) dragInfo; onApplyCurrent(); float scaleFactor = dragView.getScaleFactor(); float w = dragView.getWidth(); float h = dragView.getHeight(); int width = (int) ( w / scaleFactor ); int height = (int) ( h / scaleFactor ); int targetX = (int) ( x - xOffset ); int targetY = (int) ( y - yOffset ); RectF rect = new RectF( targetX, targetY, targetX + width, targetY + height ); addSticker( sticker, rect ); } } // ///////////////////////// // Stickers panel methods // // ///////////////////////// /** * bundle contains a list of all updates applications. if one meets the criteria ( is a sticker apk ) then return true * * @param bundle * - the bundle delta * @return true if bundle contains a valid filter package */ private boolean validDelta( Bundle bundle ) { if ( null != bundle ) { if ( bundle.containsKey( "delta" ) ) { try { @SuppressWarnings("unchecked") ArrayList<UpdateType> updates = (ArrayList<UpdateType>) bundle.getSerializable( "delta" ); if ( null != updates ) { for ( UpdateType update : updates ) { if ( FeatherIntent.PluginType.isSticker( update.getPluginType() ) ) { return true; } if ( FeatherIntent.ACTION_PLUGIN_REMOVED.equals( update.getAction() ) ) { // if it's removed check against current listed packs if ( mInstalledPackages.contains( update.getPackageName() ) ) { return true; } } } return false; } } catch ( ClassCastException e ) { return true; } } } return true; } /** * Ask to leave without apply changes. * */ void askToLeaveWithoutApply() { new AlertDialog.Builder( getContext().getBaseContext() ).setTitle( R.string.attention ).setMessage( R.string.tool_leave_question ).setPositiveButton( android.R.string.yes, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which ) { getContext().cancel(); } } ).setNegativeButton( android.R.string.no, null ).show(); } /** * Initialize the preview bitmap and canvas. */ private void createAndConfigurePreview() { if ( mPreview != null && !mPreview.isRecycled() ) { mPreview.recycle(); mPreview = null; } mPreview = BitmapUtils.copy( mBitmap, Bitmap.Config.ARGB_8888 ); mCanvas = new Canvas( mPreview ); } /** * Update the installed plugins */ protected void updateInstalledPacks( boolean firstTime ) { mIsAnimating = true; if ( mViewFlipper.getDisplayedChild() != 0 ) { mViewFlipper.setDisplayedChild( 0 ); } new PluginInstallTask().execute(); } private IapDialog createIapDialog() { ViewGroup container = ( (FeatherContext) getContext().getBaseContext() ).activatePopupContainer(); IapDialog dialog = (IapDialog) container.findViewById( R.id.main_iap_dialog ); if ( dialog == null ) { UIUtils.getLayoutInflater().inflate( R.layout.feather_iap_dialog, container, true ); dialog = (IapDialog) container.findViewById( R.id.main_iap_dialog ); dialog.setFocusable( true ); dialog.setOnCloseListener( new OnCloseListener() { @Override public void onClose() { mStatus.setStatus( STATUS_PACKS ); mListPacks.setSelectedPosition( HorizontalVariableListView.INVALID_POSITION, true ); } } ); } return dialog; } private void updateIapDialog() { final ExternalPlugin plugin = (ExternalPlugin) mPlugin; if ( null != mIapDialog && null != plugin ) { mIapDialog.setPlugin( plugin, FeatherIntent.PluginType.TYPE_STICKER, getContext().getBaseContext() ); } } private boolean removeIapDialog() { if ( null != mIapDialog ) { mIapDialog.setOnCloseListener( null ); mIapDialog.hide(); mIapDialog = null; return true; } return false; } /** * Loads the list of available stickers for the current selected pack */ protected void loadStickers() { mLogger.info( "loadStickers" ); if ( mExternalPacksEnabled ) { hideIapPopup( 0 ); } if ( mViewFlipper.getDisplayedChild() != 2 ) { mViewFlipper.setDisplayedChild( 2 ); } if ( mPlugin != null || !( mPlugin instanceof InternalPlugin ) ) { String[] list = ( (InternalPlugin) mPlugin ).listStickers(); getOptionView().post( new LoadStickersRunner( list ) ); } else { onGenericError( "Sorry, there was an error opening the pack" ); } } /** * Add a new sticker to the canvas. * * @param drawable * - the drawable name */ private void addSticker( String drawable, RectF position ) { if ( mPlugin == null || !( mPlugin instanceof InternalPlugin ) ) { return; } final InternalPlugin plugin = (InternalPlugin) mPlugin; onApplyCurrent(); final boolean rotateAndResize = true; InputStream stream = null; try { stream = plugin.getStickerStream( drawable, StickerType.Small ); } catch ( Exception e ) { e.printStackTrace(); onGenericError( "Failed to load the selected sticker" ); return; } if ( stream != null ) { StickerDrawable d = new StickerDrawable( plugin.getResources(), stream, drawable, plugin.getLabel( FeatherIntent.PluginType.TYPE_STICKER ).toString() ); d.setAntiAlias( true ); IOUtils.closeSilently( stream ); // adding the required action ApplicationInfo info = PackageManagerUtils.getApplicationInfo( getContext().getBaseContext(), mPlugin.getPackageName() ); if ( info != null ) { String sourceDir = plugin.getSourceDir( PluginType.TYPE_STICKER ); if ( null == sourceDir ) { sourceDir = ""; mLogger.error( "Cannot find the source dir" ); } mCurrentFilter = new StickerFilter( sourceDir, drawable ); mCurrentFilter.setSize( d.getBitmapWidth(), d.getBitmapHeight() ); mCurrentFilter.setExternal( 0 ); Tracker.recordTag( drawable + ": Selected" ); addSticker( d, rotateAndResize, position ); } else { onGenericError( "Sorry I'm not able to load the selected sticker" ); } } } /** * Adds the sticker. * * @param drawable * - the drawable * @param rotateAndResize * - allow rotate and resize */ private void addSticker( FeatherDrawable drawable, boolean rotateAndResize, RectF positionRect ) { mLogger.info( "addSticker: " + drawable + ", rotate: " + rotateAndResize + ", position: " + positionRect ); setIsChanged( true ); DrawableHighlightView hv = new DrawableHighlightView( mImageView, drawable ); hv.setOnDeleteClickListener( new OnDeleteClickListener() { @Override public void onDeleteClick() { onClearCurrent( true ); } } ); Matrix mImageMatrix = mImageView.getImageViewMatrix(); int cropWidth, cropHeight; int x, y; final int width = mImageView.getWidth(); final int height = mImageView.getHeight(); // width/height of the sticker if ( positionRect != null ) { cropWidth = (int) positionRect.width(); cropHeight = (int) positionRect.height(); } else { cropWidth = drawable.getIntrinsicWidth(); cropHeight = drawable.getIntrinsicHeight(); } final int cropSize = Math.max( cropWidth, cropHeight ); final int screenSize = Math.min( mImageView.getWidth(), mImageView.getHeight() ); if ( cropSize > screenSize ) { float ratio; float widthRatio = (float) mImageView.getWidth() / cropWidth; float heightRatio = (float) mImageView.getHeight() / cropHeight; if ( widthRatio < heightRatio ) { ratio = widthRatio; } else { ratio = heightRatio; } cropWidth = (int) ( (float) cropWidth * ( ratio / 2 ) ); cropHeight = (int) ( (float) cropHeight * ( ratio / 2 ) ); if ( positionRect == null ) { int w = mImageView.getWidth(); int h = mImageView.getHeight(); positionRect = new RectF( w / 2 - cropWidth / 2, h / 2 - cropHeight / 2, w / 2 + cropWidth / 2, h / 2 + cropHeight / 2 ); } positionRect.inset( ( positionRect.width() - cropWidth ) / 2, ( positionRect.height() - cropHeight ) / 2 ); } if ( positionRect != null ) { x = (int) positionRect.left; y = (int) positionRect.top; } else { x = ( width - cropWidth ) / 2; y = ( height - cropHeight ) / 2; } Matrix matrix = new Matrix( mImageMatrix ); matrix.invert( matrix ); float[] pts = new float[] { x, y, x + cropWidth, y + cropHeight }; MatrixUtils.mapPoints( matrix, pts ); RectF cropRect = new RectF( pts[0], pts[1], pts[2], pts[3] ); Rect imageRect = new Rect( 0, 0, width, height ); hv.setRotateAndScale( rotateAndResize ); hv.setup( mImageMatrix, imageRect, cropRect, false ); hv.drawOutlineFill( true ); hv.drawOutlineStroke( true ); hv.setPadding( mStickerHvPadding ); hv.setOutlineStrokeColor( mStickerHvStrokeColorStateList ); hv.setOutlineFillColor( mStickerHvFillColorStateList ); hv.setOutlineEllipse( mStickerHvEllipse ); hv.setMinSize( mStickerHvMinSize ); Paint stroke = hv.getOutlineStrokePaint(); stroke.setStrokeWidth( mStickerHvStrokeWidth ); hv.getOutlineFillPaint().setXfermode( new PorterDuffXfermode( android.graphics.PorterDuff.Mode.SRC_ATOP ) ); ( (ImageViewDrawableOverlay) mImageView ).addHighlightView( hv ); ( (ImageViewDrawableOverlay) mImageView ).setSelectedHighlightView( hv ); } /** * Flatten the current sticker within the preview bitmap. No more changes will be possible on this sticker. */ private void onApplyCurrent() { mLogger.info( "onApplyCurrent" ); if ( !stickersOnScreen() ) return; final DrawableHighlightView hv = ( (ImageViewDrawableOverlay) mImageView ).getHighlightViewAt( 0 ); if ( hv != null ) { final StickerDrawable stickerDrawable = ( (StickerDrawable) hv.getContent() ); RectF cropRect = hv.getCropRectF(); Rect rect = new Rect( (int) cropRect.left, (int) cropRect.top, (int) cropRect.right, (int) cropRect.bottom ); Matrix rotateMatrix = hv.getCropRotationMatrix(); Matrix matrix = new Matrix( mImageView.getImageMatrix() ); if ( !matrix.invert( matrix ) ) {} int saveCount = mCanvas.save( Canvas.MATRIX_SAVE_FLAG ); mCanvas.concat( rotateMatrix ); stickerDrawable.setDropShadow( false ); hv.getContent().setBounds( rect ); hv.getContent().draw( mCanvas ); mCanvas.restoreToCount( saveCount ); mImageView.invalidate(); if ( mCurrentFilter != null ) { final int w = mBitmap.getWidth(); final int h = mBitmap.getHeight(); mCurrentFilter.setTopLeft( cropRect.left / w, cropRect.top / h ); mCurrentFilter.setBottomRight( cropRect.right / w, cropRect.bottom / h ); mCurrentFilter.setRotation( Math.toRadians( hv.getRotation() ) ); int dw = stickerDrawable.getBitmapWidth(); int dh = stickerDrawable.getBitmapHeight(); float scalew = cropRect.width() / dw; float scaleh = cropRect.height() / dh; mCurrentFilter.setCenter( cropRect.centerX() / w, cropRect.centerY() / h ); mCurrentFilter.setScale( scalew, scaleh ); mActionList.add( mCurrentFilter.getActions().get( 0 ) ); Tracker.recordTag( stickerDrawable.getPackLabel() + ": Applied" ); mCurrentFilter = null; } } onClearCurrent( false ); onPreviewChanged( mPreview, false ); } /** * Remove the current sticker. * * @param removed * - true if the current sticker is being removed, otherwise it was flattened */ private void onClearCurrent( boolean removed ) { mLogger.info( "onClearCurrent. removed=" + removed ); if ( stickersOnScreen() ) { final ImageViewDrawableOverlay image = (ImageViewDrawableOverlay) mImageView; final DrawableHighlightView hv = image.getHighlightViewAt( 0 ); onClearCurrent( hv, removed ); } } /** * Removes the current active sticker. * * @param hv * - the {@link DrawableHighlightView} of the active sticker * @param removed * - current sticker is removed */ private void onClearCurrent( DrawableHighlightView hv, boolean removed ) { mLogger.info( "onClearCurrent. hv=" + hv + ", removed=" + removed ); if ( mCurrentFilter != null ) { mCurrentFilter = null; } if ( null != hv ) { FeatherDrawable content = hv.getContent(); if ( removed ) { if ( content instanceof StickerDrawable ) { String name = ( (StickerDrawable) content ).getStickerName(); String packname = ( (StickerDrawable) content ).getPackLabel(); Tracker.recordTag( name + ": Cancelled" ); Tracker.recordTag( packname + ": Cancelled" ); } } } hv.setOnDeleteClickListener( null ); ( (ImageViewDrawableOverlay) mImageView ).removeHightlightView( hv ); ( (ImageViewDrawableOverlay) mImageView ).invalidate(); } /** * Return true if there's at least one active sticker on screen. * * @return true, if successful */ private boolean stickersOnScreen() { final ImageViewDrawableOverlay image = (ImageViewDrawableOverlay) mImageView; return image.getHighlightCount() > 0; } /** * The PluginInstallTask is completed * * @param result */ private void onStickersPackListUpdated( List<StickerEffectPack> result ) { mLogger.info( "onStickersPackListUpdated: " + result.size() ); if ( mExternalPacksEnabled ) { StickerPacksAdapter adapter = new StickerPacksAdapter( getContext().getBaseContext(), R.layout.feather_sticker_pack2, R.layout.feather_sticker_pack2_external, R.layout.feather_stickers_pack_divider_empty, R.layout.feather_getmore_stickers_thumb, R.layout.feather_getmore_stickers_thumb_inverted, result ); mListPacks.setAdapter( adapter ); if ( mViewFlipper.getDisplayedChild() != 1 ) { mViewFlipper.setDisplayedChild( 1 ); } showIapPopup(); if ( mInstalledPackages.size() < 1 && mExternalPacksEnabled ) { // show the dialog popup if ( !mPreferenceService.containsValue( this.getClass().getSimpleName() + "-install-first-time" ) ) { OnClickListener listener = new OnClickListener() { @Override public void onClick( DialogInterface dialog, int which ) { getContext().downloadPlugin( PluginService.FREE_STICKERS_PACKAGENAME, FeatherIntent.PluginType.TYPE_STICKER ); dialog.dismiss(); } }; AlertDialog dialog = new AlertDialog.Builder( getContext().getBaseContext() ).setMessage( R.string.feather_stickers_dialog_first_time ).setPositiveButton( android.R.string.ok, listener ) .setNegativeButton( android.R.string.cancel, null ).create(); mPreferenceService.putBoolean( this.getClass().getSimpleName() + "-install-first-time", true ); dialog.show(); } } } else { if( result.size() > 0 ) { mPlugin = (InternalPlugin) result.get( 0 ).mPluginRef; mStatus.setStatus( STATUS_STICKERS ); } } } class PluginInstallTask extends AsyncTask<Void, Void, List<StickerEffectPack>> { @Override protected void onPreExecute() { super.onPreExecute(); mImageManager.clearCache(); } @Override protected List<StickerEffectPack> doInBackground( Void... params ) { long sharedUpdateTime = 0, lastUpdateTime = 0; FeatherInternalPack installedPacks[] = null; FeatherPack availablePacks[] = null; if ( getContext() == null ) { return null; } if ( null != mPreferenceService && mExternalPacksEnabled ) sharedUpdateTime = mPreferenceService.getLong( this.getClass().getName() + "-plugins-update-date", 0 ); if ( null != mPluginService ) lastUpdateTime = mPluginService.getLastUpdateTime(); final Context context = getContext().getBaseContext(); List<StickerEffectPack> result = Collections.synchronizedList( new ArrayList<StickerEffectPack>() ); if ( null != context ) { if ( mExternalPacksEnabled ) { while ( !mPluginService.isUpdated() ) { try { Thread.sleep( 50 ); } catch ( InterruptedException e ) { e.printStackTrace(); } mLogger.log( "waiting for plugin service..." ); } installedPacks = mPluginService.getInstalled( context, FeatherIntent.PluginType.TYPE_STICKER ); availablePacks = mPluginService.getAvailable( FeatherIntent.PluginType.TYPE_STICKER ); } else { installedPacks = new FeatherInternalPack[] { FeatherInternalPack.getDefault( getContext().getBaseContext() ) }; availablePacks = new FeatherExternalPack[] {}; } mAvailablePacks = availablePacks.length; } // List of the available plugins online mInstalledPackages.clear(); if ( mExternalPacksEnabled ) { result.add( null ); } // cycle the installed "internal" packages if ( null != context && installedPacks != null ) { for ( FeatherPack pack : installedPacks ) { if ( pack instanceof FeatherInternalPack ) { InternalPlugin plugin = (InternalPlugin) PluginManager.create( getContext().getBaseContext(), pack ); final CharSequence packagename = plugin.getPackageName(); final CharSequence label = plugin.getLabel( FeatherIntent.PluginType.TYPE_STICKER ); final StickerEffectPack effectPack = new StickerEffectPack( packagename, label, PluginError.NoError, plugin, false ); mInstalledPackages.add( packagename.toString() ); if ( isActive() ) { result.add( effectPack ); } } } } // cycle the available "external" packs if ( mExternalPacksEnabled && context != null ) { // Add divider if necessary if ( installedPacks != null && availablePacks != null ) { if ( availablePacks.length > 0 && installedPacks.length > 0 ) { result.add( new StickerEffectPack( mFeaturedDefaultTitle ) ); } } if ( availablePacks != null ) { int index = 0; for ( FeatherPack pack : availablePacks ) { if ( index >= mFeaturedCount ) break; ExternalPlugin plugin = (ExternalPlugin) PluginManager.create( context, pack ); final CharSequence packagename = plugin.getPackageName(); final CharSequence label = plugin.getLabel( FeatherIntent.PluginType.TYPE_STICKER ); final StickerEffectPack effectPack = new StickerEffectPack( packagename, label, PluginError.NoError, plugin, true ); if ( isActive() ) { result.add( effectPack ); } index++; } } } // add ending "get more" if necessary if ( mInstalledPackages != null && mInstalledPackages.size() > 0 && mExternalPacksEnabled ) { if ( mExternalPacksEnabled ) { result.add( null ); } } // display the notification popup? if ( mExternalPacksEnabled && context != null ) { mLogger.log( "shared update time: " + sharedUpdateTime ); mLogger.log( "last update time: " + lastUpdateTime ); if ( sharedUpdateTime != lastUpdateTime ) { if ( mPreferenceService != null ) mPreferenceService.putLong( this.getClass().getName() + "-plugins-update-date", lastUpdateTime ); mShowIapNotificationAndValue = mAvailablePacks > 0; } else { mShowIapNotificationAndValue = false; } mLogger.log( "mShowIapNotificationAndValue: " + mShowIapNotificationAndValue ); } return result; } @Override protected void onPostExecute( List<StickerEffectPack> result ) { super.onPostExecute( result ); mIsAnimating = false; onStickersPackListUpdated( result ); } } /** * Sticker pack listview adapter class * * @author alessandro * */ class StickerPacksAdapter extends ArrayAdapterExtended<StickerEffectPack> { static final int TYPE_GET_MORE_FIRST = 0; static final int TYPE_GET_MORE_LAST = 1; static final int TYPE_NORMAL = 2; static final int TYPE_EXTERNAL = 3; static final int TYPE_DIVIDER = 4; private int mLayoutResId; private int mExternalLayoutResId; private int mAltLayoutResId; private int mAltLayout2ResId; private int mDividerLayoutResId; private int mDefaultHeight; private LayoutInflater mLayoutInflater; private BitmapDrawable mFolderIcon; private BitmapDrawable mExternalFolderIcon; public StickerPacksAdapter( Context context, int mainResId, int externalResId, int dividerResId, int altResId, int altResId2, List<StickerEffectPack> objects ) { super( context, mainResId, objects ); mLayoutResId = mainResId; mExternalLayoutResId = externalResId; mAltLayoutResId = altResId; mAltLayout2ResId = altResId2; mDividerLayoutResId = dividerResId; mLayoutInflater = UIUtils.getLayoutInflater(); mFolderIcon = (BitmapDrawable) context.getResources().getDrawable( R.drawable.feather_sticker_pack_background ); mExternalFolderIcon = (BitmapDrawable) context.getResources().getDrawable( R.drawable.feather_sticker_pack_background ); mDefaultHeight = getOptionView().findViewById( R.id.background ).getHeight() - getOptionView().findViewById( R.id.bottom_background_overlay ).getHeight(); } @Override public int getViewTypeCount() { return 5; } @Override public int getItemViewType( int position ) { if ( !mExternalPacksEnabled ) return TYPE_NORMAL; StickerEffectPack item = getItem( position ); if ( null == item ) { if ( position == 0 ) return TYPE_GET_MORE_FIRST; else return TYPE_GET_MORE_LAST; } if ( item.isDivider ) return TYPE_DIVIDER; if ( item.isExternal ) return TYPE_EXTERNAL; return TYPE_NORMAL; } @Override public View getView( final int position, final View convertView, final ViewGroup parent ) { mLogger.log( "getView: " + position ); View view; ViewHolder holder = null; int type = getItemViewType( position ); int layoutWidth = mPacksCellWidth; int layoutHeight = LayoutParams.MATCH_PARENT; if ( convertView == null ) { holder = new ViewHolder(); if ( type == TYPE_GET_MORE_FIRST ) { view = mLayoutInflater.inflate( mAltLayoutResId, parent, false ); layoutHeight = mDefaultHeight; } else if ( type == TYPE_GET_MORE_LAST ) { view = mLayoutInflater.inflate( mAltLayout2ResId, parent, false ); layoutHeight = mDefaultHeight; // hide the last "get more" button if there's no need View lastChild = parent.getChildAt( parent.getChildCount() - 1 ); if ( null != lastChild ) { if ( lastChild.getRight() < parent.getRight() ) { layoutWidth = 0; } } } else if ( type == TYPE_NORMAL ) { // INSTALLED packs view = mLayoutInflater.inflate( mLayoutResId, parent, false ); holder.text = (TextView) view.findViewById( R.id.text ); holder.image = (ImageView) view.findViewById( R.id.image ); LayoutParams params = holder.image.getLayoutParams(); params.width = params.height = mPacksCellWidth; holder.image.setLayoutParams( params ); holder.image.requestLayout(); view.setTag( holder ); layoutHeight = LayoutParams.WRAP_CONTENT; layoutWidth = mPacksCellWidth + mItemsGapPixelSize; } else if ( type == TYPE_EXTERNAL ) { // EXTERNAL PACKS view = mLayoutInflater.inflate( mExternalLayoutResId, parent, false ); holder.text = (TextView) view.findViewById( R.id.text ); holder.image = (ImageView) view.findViewById( R.id.image ); view.setTag( holder ); LayoutParams params = holder.image.getLayoutParams(); params.width = params.height = mPacksCellWidth; holder.image.setLayoutParams( params ); holder.image.requestLayout(); layoutWidth = mPacksCellWidth + mItemsGapPixelSize; layoutHeight = LayoutParams.WRAP_CONTENT; } else { // TYPE_DIVIDER view = mLayoutInflater.inflate( mDividerLayoutResId, parent, false ); ImageView image = (ImageView) view.findViewById( R.id.image ); Drawable drawable = image.getDrawable(); if( null != drawable ) { PluginDividerDrawable d = new PluginDividerDrawable( drawable, mFeaturedDefaultTitle ); image.setImageDrawable( d ); } layoutWidth = EffectThumbLayout.LayoutParams.WRAP_CONTENT; layoutHeight = mDefaultHeight; } view.setLayoutParams( new EffectThumbLayout.LayoutParams( layoutWidth, layoutHeight ) ); } else { view = convertView; holder = (ViewHolder) view.getTag(); } if ( type == TYPE_NORMAL ) { StickerEffectPack item = getItem( position ); holder.text.setText( item.mTitle ); InternalPlugin plugin = (InternalPlugin) item.mPluginRef; StickerPackThumbnailCallable executor = new StickerPackThumbnailCallable( plugin, mFolderIcon ); mImageManager.execute( executor, plugin.getPackageName(), holder.image, STATUS_PACKS ); } else if ( type == TYPE_EXTERNAL ) { StickerEffectPack item = getItem( position ); holder.text.setText( item.mTitle ); ExternalPlugin plugin = (ExternalPlugin) item.mPluginRef; mLogger.log( item.mTitle + " is free? " + plugin.isFree() ); ExternalThumbnailCallable executor = new ExternalThumbnailCallable( plugin.getIconUrl(), mCacheService, mExternalFolderIcon, this.getContext().getResources(), R.drawable.feather_iap_dialog_image_na ); mImageManager.execute( executor, plugin.getPackageName(), holder.image, STATUS_PACKS ); } else if ( type == TYPE_DIVIDER ) { // do nothing... } else { // get more if ( mShowIapNotificationAndValue ) { TextView totalText = (TextView) view.findViewById( R.id.text01 ); totalText.setText( String.valueOf( mAvailablePacks ) ); } } return view; } } /** * Retrieve and draw the internal plugin Icon * * @author alessandro * */ static class StickerPackThumbnailCallable implements Callable<Bitmap> { InternalPlugin mPlugin; BitmapDrawable mFolder; public StickerPackThumbnailCallable( InternalPlugin plugin, BitmapDrawable drawable ) { mPlugin = plugin; mFolder = drawable; } @Override public Bitmap call() throws Exception { Drawable icon = mPlugin.getIcon( FeatherIntent.PluginType.TYPE_STICKER ); if ( null != icon ) { return ( (BitmapDrawable) UIUtils.drawFolderIcon( mFolder, icon, 1.7f ) ).getBitmap(); } else { return mFolder.getBitmap(); } } } /** * Download the remote icon or re-use the one from the current cache * * @author alessandro * */ static class ExternalThumbnailCallable implements Callable<Bitmap> { String mUri; BitmapDrawable mFolder; SoftReference<ImageCacheService> cacheServiceRef; SoftReference<Resources> resourcesRef; int mDefaultIconResId; public ExternalThumbnailCallable( final String uri, ImageCacheService cacheService, final BitmapDrawable folderBackground, Resources resources, int defaultIconResId ) { mUri = uri; mFolder = folderBackground; cacheServiceRef = new SoftReference<ImageCacheService>( cacheService ); resourcesRef = new SoftReference<Resources>( resources ); mDefaultIconResId = defaultIconResId; } @SuppressWarnings("deprecation") @Override public Bitmap call() throws Exception { if( null == mUri || mUri.length() < 1 ) { return mFolder.getBitmap(); } BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Config.RGB_565; Bitmap bitmap = null; ImageCacheService cache = cacheServiceRef.get(); if( null == cache ) { return mFolder.getBitmap(); } SimpleCachedRemoteBitmap request; try { request = cache.requestRemoteBitmap( PluginService.CONTENT_DEFAULT_URL + "/" + mUri ); bitmap = request.getBitmap( options ); } catch( Exception e ){} // fallback icon if( null == bitmap ) { if( null != resourcesRef.get() ) { try { bitmap = BitmapFactory.decodeResource( resourcesRef.get(), mDefaultIconResId ); } catch( Throwable t ) {} } } if ( null != bitmap ) { try { Bitmap result = UIUtils.drawFolderBitmap( mFolder, new BitmapDrawable( bitmap ), 1.7f ); bitmap.recycle(); bitmap = null; return result; } catch ( Throwable e ) { return mFolder.getBitmap(); } } else { return mFolder.getBitmap(); } } } /** * Sticker pack element * * @author alessandro * */ static class StickerEffectPack { CharSequence mPackageName; CharSequence mTitle; PluginError mPluginStatus; IPlugin mPluginRef; boolean isExternal; boolean isDivider; public StickerEffectPack( final String label ) { isDivider = true; mPluginStatus = PluginError.NoError; mTitle = label; } public StickerEffectPack( CharSequence packageName, CharSequence title, PluginError status, IPlugin plugin, boolean external ) { mPackageName = packageName; mPluginStatus = status; mPluginRef = plugin; mTitle = title; isExternal = external; isDivider = false; } @Override protected void finalize() throws Throwable { mPluginRef = null; super.finalize(); } } // // Stickers list adapter // class StickersAdapter extends ArrayAdapter<String> { private LayoutInflater mLayoutInflater; private int mStickerResourceId; private int mDefaultHeight; /** * Instantiates a new stickers adapter. * * @param context * the context * @param textViewResourceId * the text view resource id * @param objects * the objects */ public StickersAdapter( Context context, int textViewResourceId, String[] objects ) { super( context, textViewResourceId, objects ); mLogger.info( "StickersAdapter. size: " + objects.length ); mStickerResourceId = textViewResourceId; mLayoutInflater = UIUtils.getLayoutInflater(); mDefaultHeight = getOptionView().findViewById( R.id.background ).getHeight() - getOptionView().findViewById( R.id.bottom_background_overlay ).getHeight(); mThumbSize = mDefaultHeight - ( THUMBNAIL_INSET * 2 ); mLogger.log( "default height: " + mDefaultHeight ); mLogger.log( "thumb size: " + mThumbSize ); } @Override public View getView( int position, View convertView, ViewGroup parent ) { View view; if ( null == convertView ) { view = mLayoutInflater.inflate( mStickerResourceId, null ); LayoutParams params = new LayoutParams( mDefaultHeight, mDefaultHeight ); LinearLayout.LayoutParams params2 = new LinearLayout.LayoutParams( mThumbSize, mThumbSize ); view.findViewById( R.id.image ).setLayoutParams( params2 ); view.setLayoutParams( params ); } else { view = convertView; } ImageView image = (ImageView) view.findViewById( R.id.image ); final String sticker = getItem( position ); StickerThumbnailCallable executor = new StickerThumbnailCallable( (InternalPlugin) mPlugin, sticker, mThumbSize ); mImageManager.execute( executor, sticker, image, STATUS_STICKERS ); return view; } } /** * Downloads and renders the sticker thumbnail * * @author alessandro * */ static class StickerThumbnailCallable implements Callable<Bitmap> { InternalPlugin mPlugin; int mFinalSize; String mUrl; public StickerThumbnailCallable( final InternalPlugin plugin, final String srcUrl, final int size ) { mPlugin = plugin; mFinalSize = size; mUrl = srcUrl; } @Override public Bitmap call() throws Exception { try { return ImageLoader.getPluginItemBitmap( mPlugin, mUrl, FeatherIntent.PluginType.TYPE_STICKER, StickerType.Preview, mFinalSize, mFinalSize ); } catch ( NameNotFoundException e ) { return ImageLoader.getPluginItemBitmap( mPlugin, mUrl, FeatherIntent.PluginType.TYPE_STICKER, StickerType.Small, mFinalSize, mFinalSize ); } catch ( Exception e ) { e.printStackTrace(); return null; } } } // // Runnable for loading all the stickers from a pack // private class LoadStickersRunner implements Runnable { String[] mlist; LoadStickersRunner( String[] list ) { mlist = list; } @Override public void run() { mIsAnimating = true; if ( mListStickers.getHeight() == 0 ) { mOptionView.post( this ); return; } StickersAdapter adapter = new StickersAdapter( getContext().getBaseContext(), R.layout.feather_sticker_thumb, mlist ); mListStickers.setAdapter( adapter ); // setting the drag tolerance to the list view height mListStickers.setDragTolerance( mListStickers.getHeight() ); // activate drag and drop only for android 2.3+ if ( android.os.Build.VERSION.SDK_INT > 8 ) { mListStickers.setDragScrollEnabled( true ); mListStickers.setOnItemDragListener( new OnItemDragListener() { @Override public boolean onItemStartDrag( AdapterView<?> parent, View view, int position, long id ) { return startDrag( parent, view, position, id, false ); } } ); } mListStickers.setLongClickable( false ); mListStickers.setOnItemClickedListener( new OnItemClickedListener() { @Override public boolean onItemClick( AdapterView<?> parent, View view, int position, long id ) { final Object obj = parent.getAdapter().getItem( position ); final String sticker = (String) obj; addSticker( sticker, null ); return true; } } ); mIsAnimating = false; mlist = null; } } }