commit 1cd323dd685f5188c65f7794729fff544e6cce1a
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Sat Oct 2 00:08:31 2021 +0200

    Move yet more code to objectlib.
    It's almost possible to move the PreRender to objectlib now.

diff --git a/src/main/java/org/distorted/main/RubikObjectStateActioner.java b/src/main/java/org/distorted/main/RubikObjectStateActioner.java
new file mode 100644
index 00000000..a6aea001
--- /dev/null
+++ b/src/main/java/org/distorted/main/RubikObjectStateActioner.java
@@ -0,0 +1,240 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2019 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is free software: you can redistribute it and/or modify                            //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Magic Cube is distributed in the hope that it will be useful,                                 //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.main;
+
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+
+import com.google.android.play.core.review.ReviewInfo;
+import com.google.android.play.core.review.ReviewManager;
+import com.google.android.play.core.review.ReviewManagerFactory;
+import com.google.android.play.core.tasks.OnCompleteListener;
+import com.google.android.play.core.tasks.OnFailureListener;
+import com.google.android.play.core.tasks.Task;
+import com.google.firebase.analytics.FirebaseAnalytics;
+
+import org.distorted.objectlib.helpers.ObjectStateActioner;
+import org.distorted.objectlib.helpers.TwistyActivity;
+import org.distorted.objectlib.main.ObjectType;
+
+import org.distorted.dialogs.RubikDialogNewRecord;
+import org.distorted.dialogs.RubikDialogSolved;
+import org.distorted.network.RubikScores;
+import org.distorted.screens.RubikScreenPlay;
+import org.distorted.screens.RubikScreenSolving;
+import org.distorted.screens.ScreenList;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class RubikObjectStateActioner implements ObjectStateActioner
+{
+  private boolean mIsNewRecord;
+  private long mNewRecord;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void analyticsReport(TwistyActivity act, String message, String name, long timeBegin)
+    {
+    long elapsed = System.currentTimeMillis() - timeBegin;
+    String msg = message+" startTime: "+timeBegin+" elapsed: "+elapsed+" name: "+name;
+
+    if( BuildConfig.DEBUG )
+       {
+       android.util.Log.d("pre", msg);
+       }
+    else
+      {
+      RubikActivity ract = (RubikActivity)act;
+      FirebaseAnalytics analytics = ract.getAnalytics();
+
+      if( analytics!=null )
+        {
+        Bundle bundle = new Bundle();
+        bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, msg);
+        analytics.logEvent(FirebaseAnalytics.Event.SHARE, bundle);
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void reportRecord(TwistyActivity act, String debug, int scrambleNum)
+    {
+    RubikActivity ract = (RubikActivity)act;
+    RubikScreenPlay play = (RubikScreenPlay) ScreenList.PLAY.getScreenClass();
+    RubikScores scores = RubikScores.getInstance();
+
+    int object      = play.getObject();
+    int level       = play.getLevel();
+    ObjectType list = ObjectType.getObject(object);
+    String name     = scores.getName();
+
+    String record = list.name()+" level "+level+" time "+mNewRecord+" isNew: "+mIsNewRecord+" scrambleNum: "+scrambleNum;
+
+    if( BuildConfig.DEBUG )
+       {
+       android.util.Log.e("pre", debug);
+       android.util.Log.e("pre", name);
+       android.util.Log.e("pre", record);
+       }
+    else
+      {
+      FirebaseAnalytics analytics = ract.getAnalytics();
+
+      if( analytics!=null )
+        {
+        Bundle bundle = new Bundle();
+        bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, debug);
+        bundle.putString(FirebaseAnalytics.Param.CHARACTER, name);
+        bundle.putString(FirebaseAnalytics.Param.LEVEL, record);
+        analytics.logEvent(FirebaseAnalytics.Event.LEVEL_UP, bundle);
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void requestReview(TwistyActivity act)
+    {
+    final RubikScores scores = RubikScores.getInstance();
+    int numWins = scores.incrementNumWins();
+
+    if( numWins==7 || numWins==30 || numWins==100 || numWins==200)
+      {
+      final long timeBegin = System.currentTimeMillis();
+      final ReviewManager manager = ReviewManagerFactory.create(act);
+      Task<ReviewInfo> request = manager.requestReviewFlow();
+
+      request.addOnCompleteListener(new OnCompleteListener<ReviewInfo>()
+        {
+        @Override
+        public void onComplete (@NonNull Task<ReviewInfo> task)
+          {
+          if (task.isSuccessful())
+            {
+            final String name = scores.getName();
+            ReviewInfo reviewInfo = task.getResult();
+            Task<Void> flow = manager.launchReviewFlow(act, reviewInfo);
+
+            flow.addOnFailureListener(new OnFailureListener()
+              {
+              @Override
+              public void onFailure(Exception e)
+                {
+                analyticsReport(act,"Failed", name, timeBegin);
+                }
+              });
+
+            flow.addOnCompleteListener(new OnCompleteListener<Void>()
+              {
+              @Override
+              public void onComplete(@NonNull Task<Void> task)
+                {
+                analyticsReport(act,"Complete", name, timeBegin);
+                }
+              });
+            }
+          else
+            {
+            String name = scores.getName();
+            analyticsReport(act,"Not Successful", name, timeBegin);
+            }
+          }
+        });
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   public void onWinEffectFinished(TwistyActivity act, String debug, int scrambleNum)
+     {
+     if( ScreenList.getCurrentScreen()== ScreenList.SOLV )
+       {
+       RubikActivity ract = (RubikActivity)act;
+       Bundle bundle = new Bundle();
+       bundle.putLong("time", mNewRecord );
+
+       reportRecord(act,debug,scrambleNum);
+       requestReview(act);
+
+       if( mIsNewRecord )
+         {
+         RubikDialogNewRecord dialog = new RubikDialogNewRecord();
+         dialog.setArguments(bundle);
+         dialog.show( act.getSupportFragmentManager(), RubikDialogNewRecord.getDialogTag() );
+         }
+       else
+         {
+         RubikDialogSolved dialog = new RubikDialogSolved();
+         dialog.setArguments(bundle);
+         dialog.show( act.getSupportFragmentManager(), RubikDialogSolved.getDialogTag() );
+         }
+
+       act.runOnUiThread(new Runnable()
+         {
+         @Override
+         public void run()
+           {
+           ScreenList.switchScreen( ract, ScreenList.DONE);
+           }
+         });
+       }
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   public void onScrambleEffectFinished(TwistyActivity act)
+     {
+     RubikActivity ract = (RubikActivity)act;
+
+     RubikScores.getInstance().incrementNumPlays();
+
+     act.runOnUiThread(new Runnable()
+       {
+       @Override
+       public void run()
+         {
+         ScreenList.switchScreen( ract, ScreenList.READ);
+         }
+       });
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   public void onSolved()
+     {
+     if( ScreenList.getCurrentScreen()== ScreenList.SOLV )
+        {
+        RubikScreenSolving solving = (RubikScreenSolving) ScreenList.SOLV.getScreenClass();
+        mNewRecord = solving.getRecord();
+
+        if( mNewRecord< 0 )
+          {
+          mNewRecord = -mNewRecord;
+          mIsNewRecord = false;
+          }
+        else
+          {
+          mIsNewRecord = true;
+          }
+        }
+     }
+}
diff --git a/src/main/java/org/distorted/main/RubikPreRender.java b/src/main/java/org/distorted/main/RubikPreRender.java
index cc2246e1..92636f5f 100644
--- a/src/main/java/org/distorted/main/RubikPreRender.java
+++ b/src/main/java/org/distorted/main/RubikPreRender.java
@@ -22,33 +22,16 @@ package org.distorted.main;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
-import android.os.Bundle;
-
-import androidx.annotation.NonNull;
-
-import com.google.android.play.core.review.ReviewInfo;
-import com.google.android.play.core.review.ReviewManager;
-import com.google.android.play.core.review.ReviewManagerFactory;
-import com.google.android.play.core.tasks.OnCompleteListener;
-import com.google.android.play.core.tasks.OnFailureListener;
-import com.google.android.play.core.tasks.Task;
-import com.google.firebase.analytics.FirebaseAnalytics;
 
+import org.distorted.objectlib.helpers.ObjectStateActioner;
 import org.distorted.objectlib.main.TwistyObject;
 import org.distorted.objectlib.main.ObjectType;
-
-import org.distorted.dialogs.RubikDialogNewRecord;
-import org.distorted.dialogs.RubikDialogSolved;
 import org.distorted.objectlib.effects.BaseEffect;
 import org.distorted.objectlib.effects.EffectController;
 import org.distorted.objectlib.effects.scramble.ScrambleEffect;
 import org.distorted.objectlib.helpers.BlockController;
 import org.distorted.objectlib.helpers.MovesFinished;
 import org.distorted.objectlib.helpers.TwistyPreRender;
-import org.distorted.network.RubikScores;
-import org.distorted.screens.RubikScreenPlay;
-import org.distorted.screens.ScreenList;
-import org.distorted.screens.RubikScreenSolving;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -63,8 +46,6 @@ public class RubikPreRender implements EffectController, TwistyPreRender
   private ObjectType mNextObject;
   private long mRotationFinishedID;
   private final long[] mEffectID;
-  private boolean mIsNewRecord;
-  private long mNewRecord;
   private int mScreenWidth;
   private SharedPreferences mPreferences;
   private int[][] mNextMoves;
@@ -76,15 +57,17 @@ public class RubikPreRender implements EffectController, TwistyPreRender
   private long mAddRotationID, mRemoveRotationID;
   private int mCubit, mFace, mNewColor;
   private int mNearestAngle;
-  private String mDebug;
   private long mDebugStartTime;
   private final BlockController mBlockController;
+  private final ObjectStateActioner mActioner;
+  private String mDebug;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  RubikPreRender(RubikSurfaceView view)
+  RubikPreRender(RubikSurfaceView view, ObjectStateActioner actioner)
     {
     mView = view;
+    mActioner = actioner;
 
     mFinishRotation       = false;
     mRemoveRotation       = false;
@@ -100,13 +83,13 @@ public class RubikPreRender implements EffectController, TwistyPreRender
     mOldObject = null;
     mNewObject = null;
 
+    mDebug = "";
+
     mScreenWidth = 0;
     mScrambleObjectNum = 0;
 
     mEffectID = new long[BaseEffect.Type.LENGTH];
 
-    mDebug = "";
-
     RubikActivity act = (RubikActivity)mView.getContext();
     mBlockController = new BlockController(act);
     unblockEverything();
@@ -162,22 +145,7 @@ public class RubikPreRender implements EffectController, TwistyPreRender
 
     if( solved && !mIsSolved )
       {
-      if( ScreenList.getCurrentScreen()== ScreenList.SOLV )
-        {
-        RubikScreenSolving solving = (RubikScreenSolving) ScreenList.SOLV.getScreenClass();
-        mNewRecord = solving.getRecord();
-
-        if( mNewRecord< 0 )
-          {
-          mNewRecord = -mNewRecord;
-          mIsNewRecord = false;
-          }
-        else
-          {
-          mIsNewRecord = true;
-          }
-        }
-
+      mActioner.onSolved();
       unblockEverything();
       doEffectNow( BaseEffect.Type.WIN );
       }
@@ -279,7 +247,6 @@ public class RubikPreRender implements EffectController, TwistyPreRender
     mScrambleObject = false;
     mIsSolved       = false;
     blockEverything(BlockController.RUBIK_PLACE_3);
-    RubikScores.getInstance().incrementNumPlays();
     doEffectNow( BaseEffect.Type.SCRAMBLE );
     }
 
@@ -333,119 +300,6 @@ public class RubikPreRender implements EffectController, TwistyPreRender
     mView.setQuat();
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void reportRecord()
-    {
-    RubikScreenPlay play = (RubikScreenPlay) ScreenList.PLAY.getScreenClass();
-    RubikScores scores = RubikScores.getInstance();
-
-    int object      = play.getObject();
-    int level       = play.getLevel();
-    ObjectType list = ObjectType.getObject(object);
-    String name     = scores.getName();
-
-    String record = list.name()+" level "+level+" time "+mNewRecord+" isNew: "+mIsNewRecord+" scrambleNum: "+mScrambleObjectNum;
-
-    if( BuildConfig.DEBUG )
-       {
-       android.util.Log.e("pre", mDebug);
-       android.util.Log.e("pre", name);
-       android.util.Log.e("pre", record);
-       }
-    else
-      {
-      final RubikActivity act = (RubikActivity)mView.getContext();
-      FirebaseAnalytics analytics = act.getAnalytics();
-
-      if( analytics!=null )
-        {
-        Bundle bundle = new Bundle();
-        bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, mDebug);
-        bundle.putString(FirebaseAnalytics.Param.CHARACTER, name);
-        bundle.putString(FirebaseAnalytics.Param.LEVEL, record);
-        analytics.logEvent(FirebaseAnalytics.Event.LEVEL_UP, bundle);
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void requestReview()
-    {
-    final RubikScores scores = RubikScores.getInstance();
-    int numWins = scores.incrementNumWins();
-
-    if( numWins==7 || numWins==30 || numWins==100 || numWins==200)
-      {
-      final long timeBegin = System.currentTimeMillis();
-      final RubikActivity act = (RubikActivity)mView.getContext();
-      final ReviewManager manager = ReviewManagerFactory.create(act);
-      Task<ReviewInfo> request = manager.requestReviewFlow();
-
-      request.addOnCompleteListener(new OnCompleteListener<ReviewInfo>()
-        {
-        @Override
-        public void onComplete (@NonNull Task<ReviewInfo> task)
-          {
-          if (task.isSuccessful())
-            {
-            final String name = scores.getName();
-            ReviewInfo reviewInfo = task.getResult();
-            Task<Void> flow = manager.launchReviewFlow(act, reviewInfo);
-
-            flow.addOnFailureListener(new OnFailureListener()
-              {
-              @Override
-              public void onFailure(Exception e)
-                {
-                analyticsReport(act,"Failed", name, timeBegin);
-                }
-              });
-
-            flow.addOnCompleteListener(new OnCompleteListener<Void>()
-              {
-              @Override
-              public void onComplete(@NonNull Task<Void> task)
-                {
-                analyticsReport(act,"Complete", name, timeBegin);
-                }
-              });
-            }
-          else
-            {
-            String name = scores.getName();
-            analyticsReport(act,"Not Successful", name, timeBegin);
-            }
-          }
-        });
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void analyticsReport(RubikActivity act, String message, String name, long timeBegin)
-    {
-    long elapsed = System.currentTimeMillis() - timeBegin;
-    String msg = message+" startTime: "+timeBegin+" elapsed: "+elapsed+" name: "+name;
-
-    if( BuildConfig.DEBUG )
-       {
-       android.util.Log.d("pre", msg);
-       }
-    else
-      {
-      FirebaseAnalytics analytics = act.getAnalytics();
-
-      if( analytics!=null )
-        {
-        Bundle bundle = new Bundle();
-        bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, msg);
-        analytics.logEvent(FirebaseAnalytics.Event.SHARE, bundle);
-        }
-      }
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -720,51 +574,14 @@ public class RubikPreRender implements EffectController, TwistyPreRender
 
           if( i==BaseEffect.Type.SCRAMBLE.ordinal() )
             {
-            final RubikActivity act = (RubikActivity)mView.getContext();
-
-            act.runOnUiThread(new Runnable()
-              {
-              @Override
-              public void run()
-                {
-                ScreenList.switchScreen( act, ScreenList.READ);
-                }
-              });
+            RubikActivity act = (RubikActivity)mView.getContext();
+            mActioner.onScrambleEffectFinished(act);
             }
 
           if( i==BaseEffect.Type.WIN.ordinal() )
             {
-            if( ScreenList.getCurrentScreen()== ScreenList.SOLV )
-              {
-              final RubikActivity act = (RubikActivity)mView.getContext();
-              Bundle bundle = new Bundle();
-              bundle.putLong("time", mNewRecord );
-
-              reportRecord();
-              requestReview();
-
-              if( mIsNewRecord )
-                {
-                RubikDialogNewRecord dialog = new RubikDialogNewRecord();
-                dialog.setArguments(bundle);
-                dialog.show( act.getSupportFragmentManager(), RubikDialogNewRecord.getDialogTag() );
-                }
-              else
-                {
-                RubikDialogSolved dialog = new RubikDialogSolved();
-                dialog.setArguments(bundle);
-                dialog.show( act.getSupportFragmentManager(), RubikDialogSolved.getDialogTag() );
-                }
-
-              act.runOnUiThread(new Runnable()
-                {
-                @Override
-                public void run()
-                  {
-                  ScreenList.switchScreen( act, ScreenList.DONE);
-                  }
-                });
-              }
+            RubikActivity act = (RubikActivity)mView.getContext();
+            mActioner.onWinEffectFinished(act,mDebug,mScrambleObjectNum);
             }
 
           break;
diff --git a/src/main/java/org/distorted/main/RubikSurfaceView.java b/src/main/java/org/distorted/main/RubikSurfaceView.java
index ac73d640..738e8b37 100644
--- a/src/main/java/org/distorted/main/RubikSurfaceView.java
+++ b/src/main/java/org/distorted/main/RubikSurfaceView.java
@@ -65,6 +65,7 @@ public class RubikSurfaceView extends GLSurfaceView
 
     private RubikRenderer mRenderer;
     private RubikPreRender mPreRender;
+    private RubikObjectStateActioner mActioner;
     private Movement mMovement;
     private boolean mDragging, mBeginningRotation, mContinuingRotation;
     private int mScreenWidth, mScreenHeight, mScreenMin;
@@ -534,8 +535,9 @@ public class RubikSurfaceView extends GLSurfaceView
         mFirstIndex =0;
         mLastIndex  =0;
 
+        mActioner  = new RubikObjectStateActioner();
         mRenderer  = new RubikRenderer(this);
-        mPreRender = new RubikPreRender(this);
+        mPreRender = new RubikPreRender(this,mActioner);
 
         RubikActivity act = (RubikActivity)context;
         DisplayMetrics dm = new DisplayMetrics();
diff --git a/src/main/java/org/distorted/tutorials/TutorialObjectStateActioner.java b/src/main/java/org/distorted/tutorials/TutorialObjectStateActioner.java
new file mode 100644
index 00000000..27a0fc0e
--- /dev/null
+++ b/src/main/java/org/distorted/tutorials/TutorialObjectStateActioner.java
@@ -0,0 +1,32 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2019 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is free software: you can redistribute it and/or modify                            //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Magic Cube is distributed in the hope that it will be useful,                                 //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.tutorials;
+
+import org.distorted.objectlib.helpers.ObjectStateActioner;
+import org.distorted.objectlib.helpers.TwistyActivity;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class TutorialObjectStateActioner implements ObjectStateActioner
+{
+   public void onWinEffectFinished(TwistyActivity act, String debug, int scrambleNum) { }
+   public void onScrambleEffectFinished(TwistyActivity act) { }
+   public void onSolved() { }
+}
diff --git a/src/main/java/org/distorted/tutorials/TutorialPreRender.java b/src/main/java/org/distorted/tutorials/TutorialPreRender.java
index 146470ac..7cf2d0b3 100644
--- a/src/main/java/org/distorted/tutorials/TutorialPreRender.java
+++ b/src/main/java/org/distorted/tutorials/TutorialPreRender.java
@@ -22,12 +22,12 @@ package org.distorted.tutorials;
 import android.content.Context;
 import android.content.res.Resources;
 
+import org.distorted.objectlib.helpers.ObjectStateActioner;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.main.TwistyObject;
 import org.distorted.objectlib.helpers.BlockController;
 import org.distorted.objectlib.helpers.MovesFinished;
 import org.distorted.objectlib.helpers.TwistyPreRender;
-
 import org.distorted.objectlib.effects.BaseEffect;
 import org.distorted.objectlib.effects.EffectController;
 
@@ -52,12 +52,14 @@ public class TutorialPreRender implements EffectController, TwistyPreRender
   private int mNearestAngle;
   private int mScrambleObjectNum;
   private final BlockController mBlockController;
+  private final ObjectStateActioner mActioner;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  TutorialPreRender(TutorialSurfaceView view)
+  TutorialPreRender(TutorialSurfaceView view, ObjectStateActioner actioner)
     {
     mView = view;
+    mActioner = actioner;
 
     mFinishRotation = false;
     mRemoveRotation = false;
diff --git a/src/main/java/org/distorted/tutorials/TutorialSurfaceView.java b/src/main/java/org/distorted/tutorials/TutorialSurfaceView.java
index ff187aa5..b017ef2b 100644
--- a/src/main/java/org/distorted/tutorials/TutorialSurfaceView.java
+++ b/src/main/java/org/distorted/tutorials/TutorialSurfaceView.java
@@ -49,6 +49,7 @@ public class TutorialSurfaceView extends GLSurfaceView
     private final Static4D CAMERA_POINT = new Static4D(0, 0, 0, 0);
     private TutorialRenderer mRenderer;
     private TutorialPreRender mPreRender;
+    private TutorialObjectStateActioner mActioner;
     private Movement mMovement;
     private boolean mDragging, mBeginningRotation, mContinuingRotation;
     private int mScreenWidth, mScreenHeight, mScreenMin;
@@ -569,8 +570,9 @@ public class TutorialSurfaceView extends GLSurfaceView
         mFirstIndex =0;
         mLastIndex  =0;
 
+        mActioner  = new TutorialObjectStateActioner();
         mRenderer  = new TutorialRenderer(this);
-        mPreRender = new TutorialPreRender(this);
+        mPreRender = new TutorialPreRender(this,mActioner);
 
         TutorialActivity act = (TutorialActivity)context;
         DisplayMetrics dm = new DisplayMetrics();
