commit 8d5a8e06bc3136b108509a8cf685d4d301c17ab5
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Sat May 11 23:33:00 2019 +0100

    Simplify the way applications can get notifications when an effect finishes.
    
    Now, instead of the 'DistortedEffects.(de)registerForNotifications()' 2 APIs, we call a single 'Effect.notifyWhenFinished()'.

diff --git a/src/main/java/org/distorted/examples/deform/DeformRenderer.java b/src/main/java/org/distorted/examples/deform/DeformRenderer.java
index 52c094c..25cc4f1 100644
--- a/src/main/java/org/distorted/examples/deform/DeformRenderer.java
+++ b/src/main/java/org/distorted/examples/deform/DeformRenderer.java
@@ -35,7 +35,6 @@ import org.distorted.library.mesh.MeshBase;
 import org.distorted.library.mesh.MeshFlat;
 
 import org.distorted.library.message.EffectListener;
-import org.distorted.library.message.EffectMessage;
 import org.distorted.library.type.Dynamic3D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
@@ -87,8 +86,6 @@ class DeformRenderer implements GLSurfaceView.Renderer , EffectListener
       mMove       = new Static3D(0,0,0);
       mTouchPoint = new Static3D(0,0,0);
 
-      mEffects.registerForMessages(this);
-
       // DISTORT
       Dynamic3D releasedDistortDynamic = new Dynamic3D(NUM_VECTORS*500, 0.5f);
       releasedDistortDynamic.setMode(Dynamic3D.MODE_PATH);
@@ -169,12 +166,9 @@ class DeformRenderer implements GLSurfaceView.Renderer , EffectListener
 // keep aborting the 'released' effects, otherwise we are quickly going to run out of room in
 // effect queues.
 
-   public void effectMessage(final EffectMessage em, final long effectID, final long objectID)
+   public void effectFinished(final long effectID)
      {
-     switch(em)
-        {
-        case EFFECT_FINISHED: mEffects.abortById(effectID); break;
-        }
+     mEffects.abortById(effectID);
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -305,6 +299,7 @@ class DeformRenderer implements GLSurfaceView.Renderer , EffectListener
                        }
                      vDistort[NUM_VECTORS-1].set(0,0,0);
                      mEffects.apply(mReleasedDistort);
+                     mReleasedDistort.notifyWhenFinished(this);
                      break;
        case DEFORM : for(int i=1; i<NUM_VECTORS-1; i++)
                        {
@@ -312,6 +307,7 @@ class DeformRenderer implements GLSurfaceView.Renderer , EffectListener
                        }
                      vDeform[NUM_VECTORS-1].set(0,0,0);
                      mEffects.apply(mReleasedDeform);
+                     mReleasedDeform.notifyWhenFinished(this);
                      break;
        case SHEAR  : for(int i=1; i<NUM_VECTORS-1; i++)
                        {
@@ -319,6 +315,7 @@ class DeformRenderer implements GLSurfaceView.Renderer , EffectListener
                        }
                      vShear[NUM_VECTORS-1].set(0,0,0);
                      mEffects.apply(mReleasedShear);
+                     mReleasedShear.notifyWhenFinished(this);
                      break;
        }
      }
diff --git a/src/main/java/org/distorted/examples/effectqueue/EffectQueueActivity.java b/src/main/java/org/distorted/examples/effectqueue/EffectQueueActivity.java
index cdb3264..ee9af0e 100644
--- a/src/main/java/org/distorted/examples/effectqueue/EffectQueueActivity.java
+++ b/src/main/java/org/distorted/examples/effectqueue/EffectQueueActivity.java
@@ -50,7 +50,19 @@ public class EffectQueueActivity extends Activity implements AdapterView.OnItemS
   private int mPosID, mPosName, mPosType;
   private TableLayout mLayoutList;
 
-  private HashMap<Long,TableRow> mMap = new HashMap<>();
+  private class EffectRow
+    {
+    Effect effect;
+    TableRow row;
+
+    EffectRow(Effect e, TableRow tr)
+      {
+      effect = e;
+      row    = tr;
+      }
+    }
+
+  private ArrayList<EffectRow> mList = new ArrayList<>();
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -173,6 +185,19 @@ public class EffectQueueActivity extends Activity implements AdapterView.OnItemS
       Long currEffect = (Long)mID.getItemAtPosition(mPosID);
       EffectQueueSurfaceView v = findViewById(R.id.effects2dSurfaceView);
       v.getRenderer().getEffects().abortById(currEffect);
+
+      int numRows = mList.size();
+
+      for(int i=0; i<numRows; i++)
+        {
+        Effect effect = mList.get(i).effect;
+
+        if( effect.getID() == currEffect )
+          {
+          effectRemoved(i);
+          break;
+          }
+        }
       }
     catch(IndexOutOfBoundsException ex)
       {
@@ -196,6 +221,20 @@ public class EffectQueueActivity extends Activity implements AdapterView.OnItemS
       default: name = EffectName.CONTRAST     ;
       }
 
+      int numRows = mList.size();
+
+      for(int i=0; i<numRows; i++)
+        {
+        Effect effect = mList.get(i).effect;
+
+        if( effect.getName() == name )
+          {
+          effectRemoved(i);
+          i--;
+          numRows--;
+          }
+        }
+
     EffectQueueSurfaceView v = findViewById(R.id.effects2dSurfaceView);
     v.getRenderer().getEffects().abortByName(name);
     }
@@ -213,6 +252,20 @@ public class EffectQueueActivity extends Activity implements AdapterView.OnItemS
       default: type = EffectType.MATRIX;
       }
 
+    int numRows = mList.size();
+
+    for(int i=0; i<numRows; i++)
+      {
+      Effect effect = mList.get(i).effect;
+
+      if( effect.getType() == type )
+        {
+        effectRemoved(i);
+        i--;
+        numRows--;
+        }
+      }
+
     EffectQueueSurfaceView v = findViewById(R.id.effects2dSurfaceView);
     v.getRenderer().getEffects().abortByType(type);
     }
@@ -223,6 +276,9 @@ public class EffectQueueActivity extends Activity implements AdapterView.OnItemS
     {
     if( success )  // we really added a new effect
       {
+      EffectQueueSurfaceView v = findViewById(R.id.effects2dSurfaceView);
+      effect.notifyWhenFinished(v.getRenderer());
+
       mAdapterID.add(effect.getID());
       mAdapterID.notifyDataSetChanged();
 
@@ -249,7 +305,7 @@ public class EffectQueueActivity extends Activity implements AdapterView.OnItemS
       b4.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT));
       tr.addView(b4);
 
-      mMap.put(effect.getID(),tr);
+      mList.add(new EffectRow(effect,tr));
 
       mLayoutList.addView(tr, new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT, TableLayout.LayoutParams.WRAP_CONTENT));
       }
@@ -261,27 +317,21 @@ public class EffectQueueActivity extends Activity implements AdapterView.OnItemS
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void effectRemoved(final long id)
+  private void effectRemoved(int index)
     {
-    runOnUiThread(new Runnable()
-      {
-      public void run()
-        {
-        mAdapterID.remove(id);
-        mAdapterID.notifyDataSetChanged();
+    EffectRow er = mList.remove(index);
 
-        TableRow row = mMap.remove(id);
+    mAdapterID.remove(er.effect.getID());
+    mAdapterID.notifyDataSetChanged();
 
-        if( row!=null )
-          {
-          mLayoutList.removeView(row);
-          }
-        else
-          {
-          android.util.Log.e("EFFECTS2D", "Impossible: id="+id+" not in the map!");
-          }
-        }
-      });
+    if( er.row!=null )
+      {
+      mLayoutList.removeView(er.row);
+      }
+    else
+      {
+      android.util.Log.e("EFFECTQUEUE", "Impossible: id="+er.effect.getID()+" not in the map!");
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -292,7 +342,20 @@ public class EffectQueueActivity extends Activity implements AdapterView.OnItemS
       {
       public void run()
         {
-        TableRow row = mMap.get(id);
+        TableRow row = null;
+
+        int numRows = mList.size();
+
+        for(int i=0; i<numRows; i++)
+          {
+          EffectRow er = mList.get(i);
+
+          if( er.effect.getID() == id )
+            {
+            row = er.row;
+            break;
+            }
+          }
 
         if( row!=null )
           {
diff --git a/src/main/java/org/distorted/examples/effectqueue/EffectQueueRenderer.java b/src/main/java/org/distorted/examples/effectqueue/EffectQueueRenderer.java
index 70abc41..6cf4ef2 100644
--- a/src/main/java/org/distorted/examples/effectqueue/EffectQueueRenderer.java
+++ b/src/main/java/org/distorted/examples/effectqueue/EffectQueueRenderer.java
@@ -40,7 +40,6 @@ import org.distorted.library.main.DistortedScreen;
 import org.distorted.library.mesh.MeshFlat;
 import org.distorted.library.main.DistortedTexture;
 import org.distorted.library.message.EffectListener;
-import org.distorted.library.message.EffectMessage;
 import org.distorted.library.type.Static3D;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -82,7 +81,6 @@ class EffectQueueRenderer implements GLSurfaceView.Renderer, EffectListener
     mTexture = new DistortedTexture(texWidth,texHeight);
     mEffects = new DistortedEffects();
     mEffects.apply(scaleEffect);
-    mEffects.registerForMessages(this);
 
     mScreen = new DistortedScreen();
     }
@@ -148,17 +146,10 @@ class EffectQueueRenderer implements GLSurfaceView.Renderer, EffectListener
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// the library sending messages to us. This is running on a library 'MessageSender' thread.
 
-  public void effectMessage(final EffectMessage em, final long effectID, final long objectID)
+  public void effectFinished(final long effectID)
     {
     EffectQueueActivity act = (EffectQueueActivity)mView.getContext();
-
-    switch(em)
-      {
-      case EFFECT_REMOVED : act.effectRemoved(effectID) ; break;
-      case EFFECT_FINISHED: act.effectFinished(effectID); break;
-      default             : break;
-      }
+    act.effectFinished(effectID);
     }
   }
diff --git a/src/main/java/org/distorted/examples/listener/ListenerRenderer.java b/src/main/java/org/distorted/examples/listener/ListenerRenderer.java
index 284f3f8..8d12ad9 100644
--- a/src/main/java/org/distorted/examples/listener/ListenerRenderer.java
+++ b/src/main/java/org/distorted/examples/listener/ListenerRenderer.java
@@ -41,7 +41,6 @@ import org.distorted.library.type.Dynamic3D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.library.message.EffectListener;
-import org.distorted.library.message.EffectMessage;
 
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -68,7 +67,6 @@ class ListenerRenderer implements GLSurfaceView.Renderer,EffectListener
       {
       mView = v;
       mEffects = new DistortedEffects();
-      mEffects.registerForMessages(this);
       mScreen = new DistortedScreen();
       mRnd = new Random(0);
 
@@ -92,24 +90,22 @@ class ListenerRenderer implements GLSurfaceView.Renderer,EffectListener
       dDistort.add(new Static3D(0,0,     0));
       dDistort.add(new Static3D(0,0,height));
 
-      return mEffects.apply( new VertexEffectDistort(dDistort, new Static3D(pointx,pointy,0), new Static4D(0,0,0,radius)) );
+      VertexEffectDistort distort = new VertexEffectDistort(dDistort, new Static3D(pointx,pointy,0), new Static4D(0,0,0,radius));
+      distort.notifyWhenFinished(this);
+
+      return mEffects.apply(distort);
       }
    
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// the library sending messages to us. This is running on a library 'MessageSender' thread.
 
-   public void effectMessage(final EffectMessage em, final long effectID, final long objectID)
+   public void effectFinished(final long effectID)
      {
-     switch(em)
-        {
-        case EFFECT_FINISHED: mEffects.abortById(effectID);
-
-                              if( !addNewBubble() )
-                                {
-                                android.util.Log.e("Listener", "failed to add new bubble - this should never happen!");
-                                }
-                              break;
-        }
+     mEffects.abortById(effectID);
+
+     if( !addNewBubble() )
+       {
+       android.util.Log.e("Listener", "failed to add new bubble - this should never happen!");
+       }
      }
    
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -128,7 +124,7 @@ class ListenerRenderer implements GLSurfaceView.Renderer,EffectListener
        int w = (height*bmpWidth)/bmpHeight;
        float factor = (float)height/bmpHeight;
 
-       mMove.set((width-w)/2,0,0);
+       mMove.set((width-w)*0.5f,0,0);
        mScale.set(factor,factor,factor);
        }
      else
@@ -136,7 +132,7 @@ class ListenerRenderer implements GLSurfaceView.Renderer,EffectListener
        int h = (width*bmpHeight)/bmpWidth;
        float factor = (float)width/bmpWidth;
 
-       mMove.set(0,(height-h)/2,0);
+       mMove.set(0,(height-h)*0.5f,0);
        mScale.set(factor,factor,factor);
        }
       
diff --git a/src/main/java/org/distorted/examples/movingglow/MovingGlowRenderer.java b/src/main/java/org/distorted/examples/movingglow/MovingGlowRenderer.java
index 262a25a..fe5d305 100644
--- a/src/main/java/org/distorted/examples/movingglow/MovingGlowRenderer.java
+++ b/src/main/java/org/distorted/examples/movingglow/MovingGlowRenderer.java
@@ -37,7 +37,6 @@ import org.distorted.library.main.DistortedTexture;
 import org.distorted.library.mesh.MeshBase;
 import org.distorted.library.mesh.MeshFlat;
 import org.distorted.library.message.EffectListener;
-import org.distorted.library.message.EffectMessage;
 import org.distorted.library.type.Dynamic1D;
 import org.distorted.library.type.Dynamic4D;
 import org.distorted.library.type.Static1D;
@@ -93,7 +92,6 @@ class MovingGlowRenderer implements GLSurfaceView.Renderer,EffectListener
       for(int j=0; j<NUM_LEAVES; j++)
         {
         mLeafEffects[j] = new DistortedEffects();
-        mLeafEffects[j].registerForMessages(this);
         mLeafEffects[j].apply( new MatrixEffectRotate(new Static1D(j*(360/NUM_LEAVES)), axis, center) );
         mLeafEffects[j].apply(leafMove);
         mLeafEffects[j].apply( new FragmentEffectChroma(chromaLevel, new Static3D(colors[3*j],colors[3*j+1], colors[3*j+2])) );
@@ -141,22 +139,19 @@ class MovingGlowRenderer implements GLSurfaceView.Renderer,EffectListener
      {
      mGlowing = leaf;
      mLeafEffects[leaf].apply(mGlow[leaf]);
+     mGlow[leaf].notifyWhenFinished(this);
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // Glow finished. Make the next Leaf glow.
 
-   public void effectMessage(final EffectMessage em, final long effectID, final long objectID)
+   public void effectFinished(final long effectID)
      {
-     switch(em)
-       {
-       case EFFECT_FINISHED: mLeafEffects[mGlowing].abortById(effectID);
+     mLeafEffects[mGlowing].abortById(effectID);
 
-                             int glowing = mGlowing+1;
-                             if( glowing>=NUM_LEAVES ) glowing = 0;
-                             makeGlow(glowing);
-                             break;
-       }
+     int glowing = mGlowing+1;
+     if( glowing>=NUM_LEAVES ) glowing = 0;
+     makeGlow(glowing);
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/examples/rubik/RubikCube.java b/src/main/java/org/distorted/examples/rubik/RubikCube.java
index d75226b..a9a6360 100644
--- a/src/main/java/org/distorted/examples/rubik/RubikCube.java
+++ b/src/main/java/org/distorted/examples/rubik/RubikCube.java
@@ -63,6 +63,7 @@ class RubikCube
     private DistortedEffects[][][] mEffects;
     private Static3D[][][] mRotationAxis;
     private Dynamic1D[][][] mRotationAngle;
+    private MatrixEffectRotate[][][] mRotate;
     private DistortedTexture mTexture;
     private int mSize;
 
@@ -79,6 +80,7 @@ class RubikCube
       mEffects        = new DistortedEffects[mSize][mSize][mSize];
       mRotationAxis   = new Static3D[mSize][mSize][mSize];
       mRotationAngle  = new Dynamic1D[mSize][mSize][mSize];
+      mRotate         = new MatrixEffectRotate[mSize][mSize][mSize];
 
       Static3D[][][] cubeVectors = new Static3D[mSize][mSize][mSize];
 
@@ -134,13 +136,14 @@ class RubikCube
 
               mRotationAngle[x][y][z].add(new Static1D(0.0f));
               mRotationAngle[x][y][z].add(new Static1D(0.0f));
+              mRotate[x][y][z] = new MatrixEffectRotate( mRotationAngle[x][y][z], mRotationAxis[x][y][z], center);
 
               mEffects[x][y][z] = new DistortedEffects();
               mEffects[x][y][z].apply(sinkEffect);
               mEffects[x][y][z].apply(moveEffect);
               mEffects[x][y][z].apply(scaleEffect);
               mEffects[x][y][z].apply(quatEffect);
-              mEffects[x][y][z].apply( new MatrixEffectRotate( mRotationAngle[x][y][z], mRotationAxis[x][y][z], center));
+              mEffects[x][y][z].apply( mRotate[x][y][z] );
               mEffects[x][y][z].apply( new MatrixEffectMove(cubeVectors[x][y][z]) );
               }
             }
@@ -224,7 +227,7 @@ class RubikCube
                if( first )
                  {
                  first = false;
-                 mEffects[x][y][z].registerForMessages(listener);
+                 mRotate[x][y][z].notifyWhenFinished(listener);
                  }
                }
              }
diff --git a/src/main/java/org/distorted/examples/rubik/RubikRenderer.java b/src/main/java/org/distorted/examples/rubik/RubikRenderer.java
index f61623e..81f10a0 100644
--- a/src/main/java/org/distorted/examples/rubik/RubikRenderer.java
+++ b/src/main/java/org/distorted/examples/rubik/RubikRenderer.java
@@ -25,7 +25,6 @@ import org.distorted.library.effect.VertexEffectSink;
 import org.distorted.library.main.DistortedLibrary;
 import org.distorted.library.main.DistortedScreen;
 import org.distorted.library.message.EffectListener;
-import org.distorted.library.message.EffectMessage;
 import org.distorted.library.type.Static3D;
 
 import javax.microedition.khronos.egl.EGLConfig;
@@ -84,17 +83,12 @@ class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// EffectListener. The library sends a message to us when it's time to create a new cube.
 
-   public void effectMessage(final EffectMessage em, final long effectID, final long objectID)
+   public void effectFinished(final long effectID)
      {
-     switch(em)
-        {
-        case EFFECT_FINISHED: mNextCubeSize++;
-                              if( mNextCubeSize> MAX_CUBE_SIZE ) mNextCubeSize = MIN_CUBE_SIZE;
-                              mChangeCubeSizeNow = true;
-                              break;
-        }
+     mNextCubeSize++;
+     if( mNextCubeSize> MAX_CUBE_SIZE ) mNextCubeSize = MIN_CUBE_SIZE;
+     mChangeCubeSizeNow = true;
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/examples/starwars/StarWarsRenderer.java b/src/main/java/org/distorted/examples/starwars/StarWarsRenderer.java
index d2874fb..8b0b393 100644
--- a/src/main/java/org/distorted/examples/starwars/StarWarsRenderer.java
+++ b/src/main/java/org/distorted/examples/starwars/StarWarsRenderer.java
@@ -40,7 +40,6 @@ import org.distorted.library.type.Dynamic3D;
 import org.distorted.library.type.Static1D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.message.EffectListener;
-import org.distorted.library.message.EffectMessage;
 import org.distorted.library.main.DistortedLibrary;
 import org.distorted.library.main.DistortedTexture;
 import org.distorted.library.main.DistortedEffects;
@@ -113,7 +112,7 @@ class StarWarsRenderer implements GLSurfaceView.Renderer, EffectListener
   private DistortedScreen mScreen;
   private MeshFlat mQuad;
 
-  private long gffaID, logoID, crawlID;
+  private long alphaEffectID, scaleEffectID, moveEffectID;
   private Random mRnd = new Random(0);
   private int mWidth, mHeight;
 
@@ -245,13 +244,16 @@ class StarWarsRenderer implements GLSurfaceView.Renderer, EffectListener
     di.add(new Static1D(1.0f));
     di.add(new Static1D(1.0f));
     di.add(new Static1D(0.0f));
-    
+
+    FragmentEffectAlpha alpha = new FragmentEffectAlpha(di);
+    alpha.notifyWhenFinished(this);
+    alphaEffectID = alpha.getID();
+
     mGFFAEffects.apply( new MatrixEffectMove(new Static3D(w/5,2*h/3,0)) );
     mGFFAEffects.apply( new MatrixEffectScale(scale) );
-    mGFFAEffects.apply( new FragmentEffectAlpha(di) );
+    mGFFAEffects.apply( alpha );
       
     mScreen.attach(mGFFATexture, mGFFAEffects, mQuad);
-    mGFFAEffects.registerForMessages(this);
     }
     
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -321,10 +323,6 @@ class StarWarsRenderer implements GLSurfaceView.Renderer, EffectListener
     ///// create Stars ///////////////////
     if( mStarTexture==null ) mStarTexture = new DistortedTexture(bitmapStar.getWidth(),bitmapStar.getHeight());
     mStarTexture.setTexture(bitmapStar);
-
-    gffaID = mGFFAEffects.getID();
-    logoID = mLogoEffects.getID();
-    crawlID= mCrawlEffects.getID();
     }
  
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -370,77 +368,77 @@ class StarWarsRenderer implements GLSurfaceView.Renderer, EffectListener
     }
     
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// the library sending messages to us. This is running on a library 'MessageSender' thread.
 
-  public void effectMessage(final EffectMessage em, final long effectID, final long objectID)
+  public void effectFinished(final long effectID)
     {
-    if( em==EffectMessage.EFFECT_FINISHED )
+    if( effectID == alphaEffectID )
       {
-      if( objectID == gffaID )
-        {
-        mScreen.detach(mGFFAEffects);
-        mGFFATexture.markForDeletion();
+      mScreen.detach(mGFFAEffects);
+      mGFFATexture.markForDeletion();
 
-        int screenW=mScreen.getWidth();
-        int screenH=mScreen.getHeight();
+      int screenW=mScreen.getWidth();
+      int screenH=mScreen.getHeight();
         
-        int logoW = mLogoTexture.getWidth();
-        int logoH = mLogoTexture.getHeight();
+      int logoW = mLogoTexture.getWidth();
+      int logoH = mLogoTexture.getHeight();
       
-        int initSize= (int)(3.0f*screenW/logoW);
-        int finaSize= (int)(0.1f*screenW/logoW);
+      int initSize= (int)(3.0f*screenW/logoW);
+      int finaSize= (int)(0.1f*screenW/logoW);
       
-        Dynamic3D di = new Dynamic3D(10000,0.5f);
-        di.add(new Static3D(initSize,initSize,1));
-        di.add(new Static3D(finaSize,finaSize,1));
-
-        mLogoEffects.apply( new MatrixEffectMove(new Static3D(screenW/2,screenH/2,0)) );
-        mLogoEffects.apply( new MatrixEffectScale(di) );
-        mLogoEffects.apply( new MatrixEffectMove(new Static3D(-logoW/2,-logoH/2,0)) );
+      Dynamic3D di = new Dynamic3D(10000,0.5f);
+      di.add(new Static3D(initSize,initSize,1));
+      di.add(new Static3D(finaSize,finaSize,1));
+      MatrixEffectScale scale = new MatrixEffectScale(di);
+      scale.notifyWhenFinished(this);
+      scaleEffectID = scale.getID();
+
+      mLogoEffects.apply( new MatrixEffectMove(new Static3D(screenW/2,screenH/2,0)) );
+      mLogoEffects.apply( scale );
+      mLogoEffects.apply( new MatrixEffectMove(new Static3D(-logoW/2,-logoH/2,0)) );
       
-        mScreen.attach(mLogoTexture, mLogoEffects,mQuad);
-        mLogoEffects.registerForMessages(this);
-        }
-      else if( objectID==logoID )
-        {
-        mScreen.detach(mLogoEffects);
-        mLogoTexture.markForDeletion();
+      mScreen.attach(mLogoTexture, mLogoEffects,mQuad);
+      }
+    else if( effectID == scaleEffectID )
+      {
+      mScreen.detach(mLogoEffects);
+      mLogoTexture.markForDeletion();
         
-        int crawlW = mCrawlTexture.getWidth();
-        int crawlH = mCrawlTexture.getHeight();
-        int screenW= mScreen.getWidth();
-        int backH  = mCrawlBackgroundTexture.getHeight();
-        float scale= (float)screenW/crawlW;
-
-        mCrawlBackgroundEffects.apply( new MatrixEffectRotate(new Static1D(CRAWL_ANGLE), new Static3D(1,0,0), new Static3D(screenW/2,0,0)) );
-
-        final int transpDist = 5;
-        Static3D center = new Static3D( screenW/2 , transpDist*backH , 0 );
-        Static3D region = new Static3D( transpDist*backH , transpDist*backH , transpDist*backH );
-        mCrawlBackgroundEffects.apply( new FragmentEffectAlpha(new Static1D(1-transpDist*0.6f), center, region, true) );
-
-        Dynamic3D di = new Dynamic3D(70000,0.5f);
-        di.add(new Static3D(screenW/2,-scale*crawlH/2        , 0));
-        di.add(new Static3D(screenW/2,+scale*crawlH/2 + backH, 0));
-
-        mCrawlEffects.apply( new MatrixEffectMove(di) );
-        mCrawlEffects.apply( new MatrixEffectScale(new Static3D(scale,scale,scale)) );
-        mCrawlEffects.apply( new MatrixEffectMove(new Static3D(-crawlW/2,-crawlH/2,0)) );
+      int crawlW = mCrawlTexture.getWidth();
+      int crawlH = mCrawlTexture.getHeight();
+      int screenW= mScreen.getWidth();
+      int backH  = mCrawlBackgroundTexture.getHeight();
+      float scale= (float)screenW/crawlW;
+
+      mCrawlBackgroundEffects.apply( new MatrixEffectRotate(new Static1D(CRAWL_ANGLE), new Static3D(1,0,0), new Static3D(screenW/2,0,0)) );
+
+      final int transpDist = 5;
+      Static3D center = new Static3D( screenW/2 , transpDist*backH , 0 );
+      Static3D region = new Static3D( transpDist*backH , transpDist*backH , transpDist*backH );
+      mCrawlBackgroundEffects.apply( new FragmentEffectAlpha(new Static1D(1-transpDist*0.6f), center, region, true) );
+
+      Dynamic3D di = new Dynamic3D(70000,0.5f);
+      di.add(new Static3D(screenW/2,-scale*crawlH/2        , 0));
+      di.add(new Static3D(screenW/2,+scale*crawlH/2 + backH, 0));
+      MatrixEffectMove move = new MatrixEffectMove(di);
+      move.notifyWhenFinished(this);
+      moveEffectID = move.getID();
+
+      mCrawlEffects.apply( move );
+      mCrawlEffects.apply( new MatrixEffectScale(new Static3D(scale,scale,scale)) );
+      mCrawlEffects.apply( new MatrixEffectMove(new Static3D(-crawlW/2,-crawlH/2,0)) );
         
-        mBackground = mScreen.attach(mCrawlBackgroundTexture, mCrawlBackgroundEffects,mQuad);
-        mBackground.attach(mCrawlTexture, mCrawlEffects,mQuad);
-        mBackground.glDisable(GLES31.GL_DEPTH_TEST);
-        mBackground.glDepthMask(false);
-        mCrawlEffects.registerForMessages(this);
-        }
-      else if( objectID==crawlID )
-        {
-        mScreen.detach(mBackground);
-        mBackground.detach(mCrawlEffects);
-        mCrawlTexture.markForDeletion();
-        mCrawlBackgroundEffects.abortAllEffects();
-        mCrawlBackgroundTexture.markForDeletion();
-        }
+      mBackground = mScreen.attach(mCrawlBackgroundTexture, mCrawlBackgroundEffects,mQuad);
+      mBackground.attach(mCrawlTexture, mCrawlEffects,mQuad);
+      mBackground.glDisable(GLES31.GL_DEPTH_TEST);
+      mBackground.glDepthMask(false);
+      }
+    else if( effectID == moveEffectID )
+      {
+      mScreen.detach(mBackground);
+      mBackground.detach(mCrawlEffects);
+      mCrawlTexture.markForDeletion();
+      mCrawlBackgroundEffects.abortAllEffects();
+      mCrawlBackgroundTexture.markForDeletion();
       }
     }
   }
