commit 6e18bd321c03fad3e702712e046f9dbb9afc0de5
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Tue Apr 30 14:13:14 2019 +0100

    Turn the Rubik app into a MemoryTest.

diff --git a/src/main/java/org/distorted/examples/projection/ProjectionActivity.java b/src/main/java/org/distorted/examples/projection/ProjectionActivity.java
index 222d7f0..be9ed1c 100644
--- a/src/main/java/org/distorted/examples/projection/ProjectionActivity.java
+++ b/src/main/java/org/distorted/examples/projection/ProjectionActivity.java
@@ -43,11 +43,11 @@ public class ProjectionActivity extends Activity implements OnSeekBarChangeListe
       super.onCreate(savedState);
       setContentView(R.layout.projectionlayout);
 
-      textF = (TextView)findViewById(R.id.projectionTextFOV);
-      textN = (TextView)findViewById(R.id.projectionTextNear);
+      textF = findViewById(R.id.projectionTextFOV);
+      textN = findViewById(R.id.projectionTextNear);
 
-      SeekBar bar1 = (SeekBar)findViewById(R.id.projectionSeekFOV);
-      SeekBar bar2 = (SeekBar)findViewById(R.id.projectionSeekNear);
+      SeekBar bar1 = findViewById(R.id.projectionSeekFOV);
+      SeekBar bar2 = findViewById(R.id.projectionSeekNear);
 
       bar1.setOnSeekBarChangeListener(this);
       bar2.setOnSeekBarChangeListener(this);
@@ -69,7 +69,7 @@ public class ProjectionActivity extends Activity implements OnSeekBarChangeListe
     @Override
     protected void onPause() 
       {
-      GLSurfaceView mView = (GLSurfaceView) this.findViewById(R.id.surfaceViewProjection);
+      GLSurfaceView mView = findViewById(R.id.surfaceViewProjection);
       mView.onPause();
 
       Distorted.onPause();
@@ -83,7 +83,7 @@ public class ProjectionActivity extends Activity implements OnSeekBarChangeListe
       {
       super.onResume();
       
-      GLSurfaceView mView = (GLSurfaceView) this.findViewById(R.id.surfaceViewProjection);
+      GLSurfaceView mView = findViewById(R.id.surfaceViewProjection);
       mView.onResume();    
       }
  
@@ -101,7 +101,7 @@ public class ProjectionActivity extends Activity implements OnSeekBarChangeListe
     public void onProgressChanged(SeekBar bar, int progress, boolean fromUser) 
       {
       float ret;
-      ProjectionSurfaceView view = (ProjectionSurfaceView)findViewById(R.id.surfaceViewProjection);
+      ProjectionSurfaceView view = findViewById(R.id.surfaceViewProjection);
       ProjectionRenderer renderer = view.getRenderer();
 
       switch (bar.getId()) 
diff --git a/src/main/java/org/distorted/examples/rubik/RubikActivity.java b/src/main/java/org/distorted/examples/rubik/RubikActivity.java
index f1cfb63..9096be4 100644
--- a/src/main/java/org/distorted/examples/rubik/RubikActivity.java
+++ b/src/main/java/org/distorted/examples/rubik/RubikActivity.java
@@ -20,12 +20,10 @@
 package org.distorted.examples.rubik;
 
 import android.app.Activity;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
+import android.app.ActivityManager;
 import android.opengl.GLSurfaceView;
 import android.os.Bundle;
-import android.support.v4.content.ContextCompat;
-import android.view.View;
+import android.widget.TextView;
 
 import org.distorted.examples.R;
 import org.distorted.library.main.Distorted;
@@ -34,9 +32,7 @@ import org.distorted.library.main.Distorted;
 
 public class RubikActivity extends Activity
 {
-            static final int DEFAULT_SIZE  = 3;
-    private static final int SMALLEST_SIZE = 2;
-    private static final int[] button_ids  = {R.id.rubikSize2, R.id.rubikSize3, R.id.rubikSize4};
+    private TextView mText;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -46,7 +42,8 @@ public class RubikActivity extends Activity
       super.onCreate(icicle);
       setContentView(R.layout.rubiklayout);
 
-      markButton(DEFAULT_SIZE);
+      mText = findViewById(R.id.rubikText);
+      setText(1);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -81,54 +78,21 @@ public class RubikActivity extends Activity
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    public void Scramble(View v)
+    void setText(int numCube)
       {
-      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      view.scrambleCube();
+      long availMemory = returnAvailableMemory();
+      int  availMemoryInMB = (int)(availMemory/1000000);
+      mText.setText(getString(R.string.rubik_placeholder, numCube, availMemoryInMB));
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    public void Credits(View v)
-      {
-      android.util.Log.e("rubik", "credits...");
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public void setSize(View v)
-      {
-      int size=0, id = v.getId();
-
-      for(int b=0; b<button_ids.length; b++)
-        if( button_ids[b] == id )
-          {
-          size = b+SMALLEST_SIZE;
-          break;
-          }
-
-      markButton(size);
-
-      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      view.setNewCubeSize(size);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   private void markButton(int size)
+   private long returnAvailableMemory()
      {
-     for(int b=0; b<button_ids.length; b++)
-       {
-       Drawable d = findViewById(button_ids[b]).getBackground();
+     ActivityManager activityManager = (ActivityManager)getSystemService(ACTIVITY_SERVICE);
+     ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
+     activityManager.getMemoryInfo(memoryInfo);
 
-       if( size == b+SMALLEST_SIZE )
-         {
-         d.setColorFilter(ContextCompat.getColor(this,R.color.red), PorterDuff.Mode.MULTIPLY);
-         }
-       else
-         {
-         d.clearColorFilter();
-         }
-       }
+     return memoryInfo.availMem;
      }
 }
diff --git a/src/main/java/org/distorted/examples/rubik/RubikCube.java b/src/main/java/org/distorted/examples/rubik/RubikCube.java
index 361d712..95be7c6 100644
--- a/src/main/java/org/distorted/examples/rubik/RubikCube.java
+++ b/src/main/java/org/distorted/examples/rubik/RubikCube.java
@@ -19,6 +19,8 @@
 
 package org.distorted.examples.rubik;
 
+import java.util.Random;
+
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
@@ -43,7 +45,11 @@ import org.distorted.library.type.Static4D;
 
 class RubikCube
 {
-    private static final int POST_ROTATION_MILLISEC = 500;
+    private static final int VECTX = 0;  //
+    private static final int VECTY = 1;  // don't change this
+    private static final int VECTZ = 2;  //
+
+    private static final int ROTATION_MILLISEC = 1500;
     private static final int TEXTURE_SIZE = 100;
 
     private static final Static3D VectX = new Static3D(1,0,0);
@@ -53,37 +59,27 @@ class RubikCube
     private DistortedNode[][][] mNodes;
     private MeshCubes[][][] mCubes;
     private DistortedEffects[][][] mEffects;
-    private Static4D[][][] mQuatScramble;
     private Static3D[][][] mRotationAxis;
     private Dynamic1D[][][] mRotationAngle;
-    private Static3D[][][] mCurrentPosition;
-    private Static1D mRotationAngleStatic, mRotationAngleMiddle, mRotationAngleFinal;
     private DistortedTexture mTexture;
-    private DistortedEffects mEffectsListeningForNow;
 
-    private int mRotAxis, mRotRow;
     private int mSize;
 
+    private Random mRnd = new Random(0);
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    RubikCube(int size, Static3D move, Static3D scale, Static4D quatC, Static4D quatA)
+    RubikCube(int size, Static3D move, Static3D scale)
       {
       mSize = size;
 
-      mRotationAngleStatic = new Static1D(0);
-      mRotationAngleMiddle = new Static1D(0);
-      mRotationAngleFinal  = new Static1D(0);
-
-      mRotAxis= RubikSurfaceView.VECTX;
       mTexture = new DistortedTexture(TEXTURE_SIZE,TEXTURE_SIZE);
 
       mNodes          = new DistortedNode[mSize][mSize][mSize];
       mCubes          = new MeshCubes[mSize][mSize][mSize];
       mEffects        = new DistortedEffects[mSize][mSize][mSize];
-      mQuatScramble   = new Static4D[mSize][mSize][mSize];
       mRotationAxis   = new Static3D[mSize][mSize][mSize];
       mRotationAngle  = new Dynamic1D[mSize][mSize][mSize];
-      mCurrentPosition= new Static3D[mSize][mSize][mSize];
 
       Static3D[][][] cubeVectors = new Static3D[mSize][mSize][mSize];
 
@@ -93,8 +89,7 @@ class RubikCube
       VertexEffectSink        sinkEffect = new VertexEffectSink( new Static1D(getSinkStrength()), center, region );
       MatrixEffectMove        moveEffect = new MatrixEffectMove(move);
       MatrixEffectScale      scaleEffect = new MatrixEffectScale(scale);
-      MatrixEffectQuaternion quatCEffect = new MatrixEffectQuaternion(quatC, center);
-      MatrixEffectQuaternion quatAEffect = new MatrixEffectQuaternion(quatA, center);
+      MatrixEffectQuaternion  quatEffect = new MatrixEffectQuaternion( initializeQuat(), center);
 
       // 3x2 bitmap = 6 squares:
       //
@@ -135,19 +130,18 @@ class RubikCube
 
               mCubes[x][y][z]           = new MeshCubes(vertices,vertices,vertices, tmpFront, tmpBack, tmpLeft, tmpRight, tmpTop, tmpBottom);
               cubeVectors[x][y][z]      = new Static3D( TEXTURE_SIZE*(x-nc), TEXTURE_SIZE*(y-nc), TEXTURE_SIZE*(z-nc) );
-              mQuatScramble[x][y][z]    = new Static4D(0,0,0,1);
               mRotationAngle[x][y][z]   = new Dynamic1D();
               mRotationAxis[x][y][z]    = new Static3D(1,0,0);
-              mCurrentPosition[x][y][z] = new Static3D(x,y,z);
+
+              mRotationAngle[x][y][z].add(new Static1D(0.0f));
+              mRotationAngle[x][y][z].add(new Static1D(0.0f));
 
               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(quatCEffect);
-              mEffects[x][y][z].apply(quatAEffect);
+              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( new MatrixEffectQuaternion(mQuatScramble[x][y][z], center));
               mEffects[x][y][z].apply( new MatrixEffectMove(cubeVectors[x][y][z]) );
               }
             }
@@ -192,123 +186,63 @@ class RubikCube
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    void addNewRotation(int vector, float offset )
+    void addRotation(EffectListener listener)
       {
+      mRnd.setSeed(System.currentTimeMillis());
+
+      int vector = mRnd.nextInt(3);
+      int rotRow = mRnd.nextInt(mSize);
+
+      boolean first = true;
       Static3D axis = VectX;
 
       switch(vector)
         {
-        case RubikSurfaceView.VECTX: axis = VectX; break;
-        case RubikSurfaceView.VECTY: axis = VectY; break;
-        case RubikSurfaceView.VECTZ: axis = VectZ; break;
+        case VECTX: axis = VectX; break;
+        case VECTY: axis = VectY; break;
+        case VECTZ: axis = VectZ; break;
         }
 
-      mRotAxis = vector;
-      mRotRow  = (int)(mSize*offset);
-
-      mRotationAngleStatic.set1(0.0f);
-
       for(int x=0; x<mSize; x++)
         for(int y=0; y<mSize; y++)
           for(int z=0; z<mSize; z++)
             if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
               {
-              if( belongsToRotation(x,y,z,vector,mRotRow) )
+              if( belongsToRotation(x,y,z,vector,rotRow) )
                 {
                 mRotationAxis[x][y][z].set(axis);
-                mRotationAngle[x][y][z].add(mRotationAngleStatic);
-                }
-              }
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    void continueRotation(float angleInDegrees)
-      {
-      mRotationAngleStatic.set1(angleInDegrees);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private int computeNearestAngle(float angle)
-      {
-      final int NEAREST = 90;
-
-      int tmp = (int)((angle+NEAREST/2)/NEAREST);
-      if( angle< -(NEAREST/2) ) tmp-=1;
-
-      return NEAREST*tmp;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    void finishRotationNow(EffectListener listener)
-      {
-      boolean first = true;
-      float startingAngle = mRotationAngleStatic.get1();
-      int nearestAngleInDegrees = computeNearestAngle(startingAngle);
-
-      mRotationAngleFinal.set1(nearestAngleInDegrees);
-      mRotationAngleMiddle.set1( nearestAngleInDegrees + (nearestAngleInDegrees-startingAngle)*0.2f );
-
-      for(int x=0; x<mSize; x++)
-        for(int y=0; y<mSize; y++)
-          for(int z=0; z<mSize; z++)
-            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
-              {
-              if( belongsToRotation(x,y,z,mRotAxis,mRotRow) )
-                {
-                mRotationAngle[x][y][z].makeRunNowFor(POST_ROTATION_MILLISEC);
-                mRotationAngle[x][y][z].add(mRotationAngleMiddle);
-                mRotationAngle[x][y][z].add(mRotationAngleFinal);
+                mRotationAngle[x][y][z].makeRunNowFor(ROTATION_MILLISEC);
+                mRotationAngle[x][y][z].setPoint(1,90.0f);
 
                 if( first )
                   {
                   first = false;
-                  mEffectsListeningForNow = mEffects[x][y][z];
-                  mEffectsListeningForNow.registerForMessages(listener);
+                  mEffects[x][y][z].registerForMessages(listener);
                   }
                 }
               }
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+// Initial rotation of the cube. Something semi-random that looks good.
 
-    void removeRotationNow(EffectListener listener)
-      {
-      mEffectsListeningForNow.deregisterForMessages(listener);
-
-      int nearestAngleInDegrees = computeNearestAngle(mRotationAngleStatic.get1());
-      double nearestAngleInRadians = nearestAngleInDegrees*Math.PI/180;
-      float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
-      float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
-
-      mRotationAngleStatic.set1(0);
+   private Static4D initializeQuat()
+     {
+     return new Static4D(-0.25189602f,0.3546389f,0.009657208f,0.90038127f);
+     }
 
-      float qx=0,qy=0,qz=0;
+///////////////////////////////////////////////////////////////////////////////////////////////////
 
-      switch(mRotAxis)
+    private boolean belongsToRotation(int x, int y, int z, int vector, int row)
+      {
+      switch(vector)
         {
-        case RubikSurfaceView.VECTX: qx=1; break;
-        case RubikSurfaceView.VECTY: qy=1; break;
-        case RubikSurfaceView.VECTZ: qz=1; break;
+        case VECTX: return x==row;
+        case VECTY: return y==row;
+        case VECTZ: return z==row;
         }
 
-      Static4D quat = new Static4D(qx*sinA, qy*sinA, qz*sinA, cosA);
-
-      for(int x=0; x<mSize; x++)
-        for(int y=0; y<mSize; y++)
-          for(int z=0; z<mSize; z++)
-            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
-              {
-              if( belongsToRotation(x,y,z,mRotAxis,mRotRow) )
-                {
-                mRotationAngle[x][y][z].makeRunNowFor(0);
-                mRotationAngle[x][y][z].removeAll();
-                mQuatScramble[x][y][z].set(RubikSurfaceView.quatMultiply(quat,mQuatScramble[x][y][z]));
-                modifyCurrentPosition(x,y,z,quat);
-                }
-              }
+      return false;
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -325,46 +259,6 @@ class RubikCube
         }
       }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private boolean belongsToRotation(int x, int y, int z, int vector, int row)
-      {
-      switch(vector)
-        {
-        case RubikSurfaceView.VECTX: return mCurrentPosition[x][y][z].get1()==row;
-        case RubikSurfaceView.VECTY: return mCurrentPosition[x][y][z].get2()==row;
-        case RubikSurfaceView.VECTZ: return mCurrentPosition[x][y][z].get3()==row;
-        }
-
-      return false;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private void modifyCurrentPosition(int x, int y, int z, Static4D quat)
-      {
-      Static3D current = mCurrentPosition[x][y][z];
-      float diff = 0.5f*(mSize-1);
-      float cubitCenterX = current.get1() - diff;
-      float cubitCenterY = current.get2() - diff;
-      float cubitCenterZ = current.get3() - diff;
-
-      Static4D cubitCenter =  new Static4D(cubitCenterX, cubitCenterY, cubitCenterZ, 0);
-      Static4D rotatedCenter = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat);
-
-      float rotatedX = rotatedCenter.get1() + diff;
-      float rotatedY = rotatedCenter.get2() + diff;
-      float rotatedZ = rotatedCenter.get3() + diff;
-
-      int roundedX = (int)(rotatedX+0.1f);
-      int roundedY = (int)(rotatedY+0.1f);
-      int roundedZ = (int)(rotatedZ+0.1f);
-
-      mCurrentPosition[x][y][z].set1(roundedX);
-      mCurrentPosition[x][y][z].set2(roundedY);
-      mCurrentPosition[x][y][z].set3(roundedZ);
-      }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
     void createTexture()
diff --git a/src/main/java/org/distorted/examples/rubik/RubikRenderer.java b/src/main/java/org/distorted/examples/rubik/RubikRenderer.java
index 7b68952..6f719b2 100644
--- a/src/main/java/org/distorted/examples/rubik/RubikRenderer.java
+++ b/src/main/java/org/distorted/examples/rubik/RubikRenderer.java
@@ -38,16 +38,15 @@ class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
 {
     private static final float CUBE_SCREEN_RATIO = 0.5f;
     private static final float CAMERA_DISTANCE   = 0.6f;  // 0.6 of the length of max(scrHeight,scrWidth)
+    private static final int MIN_CUBE_SIZE = 1;
+    private static final int MAX_CUBE_SIZE = 6;
 
     private RubikSurfaceView mView;
     private DistortedScreen mScreen;
     private Static3D mMove, mScale;
-    private Static4D mQuatCurrent, mQuatAccumulated;
-    private Static4D mTempCurrent, mTempAccumulated;
-    private float mCubeSizeInScreenSpace;
     private int mNextCubeSize;
-    private boolean mFinishRotation, mRemoveRotation, mFinishDragCurrent, mFinishDragAccumulated;
-    private boolean mCanRotate;
+    private boolean mChangeCubeSizeNow;
+    private int mNumCube;
     private RubikCube mCube;
 
     private int mScreenWidth, mScreenHeight;
@@ -60,68 +59,28 @@ class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
 
       mScreen = new DistortedScreen();
 
-      mTempCurrent     = new Static4D(0,0,0,1);
-      mTempAccumulated = initializeQuat();
-      mQuatCurrent     = new Static4D(0,0,0,1);
-      mQuatAccumulated = initializeQuat();
-
       mScreenWidth = mScreenHeight = 0;
 
       mMove  = new Static3D(0,0,0);
       mScale = new Static3D(1,1,1);
 
-      mFinishRotation        = false;
-      mRemoveRotation        = false;
-      mFinishDragCurrent     = false;
-      mFinishDragAccumulated = false;
-
-      mNextCubeSize= 0;
+      mNextCubeSize= MIN_CUBE_SIZE;
+      mChangeCubeSizeNow = false;
 
-      mCanRotate = true;
+      mNumCube = 0;
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// various things are done here delayed, 'after the next render' as not to be done mid-render and
-// cause artifacts.
+// change the cube size here, 'after the next render' as not to cause artifacts.
 
     public void onDrawFrame(GL10 glUnused) 
       {
       mScreen.render( System.currentTimeMillis() );
 
-      if( mFinishDragCurrent )
-        {
-        mFinishDragCurrent = false;
-        mQuatCurrent.set(mTempCurrent);
-        }
-
-      if( mFinishDragAccumulated )
-        {
-        mFinishDragAccumulated = false;
-        mQuatAccumulated.set(mTempAccumulated);
-        }
-
-      if( mFinishRotation )
+      if( mChangeCubeSizeNow )
         {
-        mCanRotate = false;
-        mFinishRotation=false;
-        mCube.finishRotationNow(this);
-        }
-
-      if( mRemoveRotation )
-        {
-        mRemoveRotation=false;
-        mCube.removeRotationNow(this);
-        mCanRotate = true;
-        }
-
-      if( mNextCubeSize!=0 )
-        {
-        createCubeNow(mNextCubeSize);
-        mScreen.detachAll();
-        mCube.attachToScreen(mScreen);
-        mNextCubeSize = 0;
-        mCanRotate = true;   // it can happen that we have just changed the size of the cube while
-                             // finishing rotation and never removed it!
+        mChangeCubeSizeNow = false;
+        createNextCube();
         }
       }
 
@@ -132,7 +91,10 @@ class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
      {
      switch(em)
         {
-        case EFFECT_FINISHED: mRemoveRotation = true; break;
+        case EFFECT_FINISHED: mNextCubeSize++;
+                              if( mNextCubeSize> MAX_CUBE_SIZE ) mNextCubeSize = MIN_CUBE_SIZE;
+                              mChangeCubeSizeNow = true;
+                              break;
         }
      }
 
@@ -144,8 +106,6 @@ class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
       float fovInDegrees   = computeFOV(cameraDistance,height);
 
       mScreen.setProjection( fovInDegrees, 0.1f);
-      mView.setScreenSize(width,height);
-      mView.setCameraDist(cameraDistance);
       mScreen.resize(width, height);
 
       recomputeScaleFactor(width,height);
@@ -158,9 +118,7 @@ class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
     
     public void onSurfaceCreated(GL10 glUnused, EGLConfig config) 
       {
-      mCube.createTexture();
-      mScreen.detachAll();
-      mCube.attachToScreen(mScreen);
+      createNextCube();
 
       VertexEffectSink.enable();
 
@@ -176,107 +134,38 @@ class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   private float computeFOV(float cameraDistance, int screenHeight)
-     {
-     double halfFOVInRadians = Math.atan( screenHeight/(2*cameraDistance) );
-     return (float)(2*halfFOVInRadians*(180/Math.PI));
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// no this will not race with onDrawFrame
-
-   void finishRotation()
+   private void createNextCube()
      {
-     mFinishRotation = true;
-     }
+     if( mCube!=null ) mCube.releaseResources();
+     mCube = new RubikCube(mNextCubeSize, mMove, mScale);
+     mCube.createTexture();
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+     if( mScreenWidth!=0 ) recomputeScaleFactor(mScreenWidth,mScreenHeight);
 
-   void createCube(int newSize)
-     {
-     mNextCubeSize = newSize;
+     mScreen.detachAll();
+     mCube.attachToScreen(mScreen);
+     mCube.addRotation(this);
+     RubikActivity act = mView.getRubikActivity();
+     act.setText(++mNumCube);
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   void createCubeNow(int newSize)
+   private float computeFOV(float cameraDistance, int screenHeight)
      {
-     int oldSize = mCube==null ? 0 : mCube.getSize();
-
-     if( oldSize!=newSize )
-       {
-       if( mCube!=null ) mCube.releaseResources();
-       mCube = new RubikCube(newSize, mMove, mScale, mQuatCurrent, mQuatAccumulated);
-       mCube.createTexture();
-
-       if( mScreenWidth!=0 )
-         {
-         recomputeScaleFactor(mScreenWidth,mScreenHeight);
-         }
-       }
+     double halfFOVInRadians = Math.atan( screenHeight/(2*cameraDistance) );
+     return (float)(2*halfFOVInRadians*(180/Math.PI));
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
    private void recomputeScaleFactor(int screenWidth, int screenHeight)
      {
-     mCubeSizeInScreenSpace = CUBE_SCREEN_RATIO*(screenWidth>screenHeight ? screenHeight:screenWidth);
+     float cubeSizeInScreenSpace = CUBE_SCREEN_RATIO*(screenWidth>screenHeight ? screenHeight:screenWidth);
      float texSize = mCube.getTextureSize();
-     float scaleFactor = mCubeSizeInScreenSpace/(texSize*mCube.getSize());
+     float scaleFactor = cubeSizeInScreenSpace/(texSize*mCube.getSize());
 
      mMove.set( (screenWidth-scaleFactor*texSize)/2 , (screenHeight-scaleFactor*texSize)/2 , -scaleFactor*texSize/2 );
      mScale.set(scaleFactor,scaleFactor,scaleFactor);
      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   void scrambleCube()
-     {
-
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   float returnCubeSizeInScreenSpace()
-     {
-     return mCubeSizeInScreenSpace;
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   boolean canRotate()
-     {
-     return mCanRotate;
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   RubikCube getCube()
-     {
-     return mCube;
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Initial rotation of the cube. Something semi-random that looks good.
-
-   Static4D initializeQuat()
-     {
-     return new Static4D(-0.25189602f,0.3546389f,0.009657208f,0.90038127f);
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   void setQuatCurrent(Static4D current)
-     {
-     mTempCurrent.set(current);
-     mFinishDragCurrent = true;
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   void setQuatAccumulated(Static4D accumulated)
-     {
-     mTempAccumulated.set(accumulated);
-     mFinishDragAccumulated = true;
-     }
 }
diff --git a/src/main/java/org/distorted/examples/rubik/RubikSurfaceView.java b/src/main/java/org/distorted/examples/rubik/RubikSurfaceView.java
index 1d00b8e..bd0db97 100644
--- a/src/main/java/org/distorted/examples/rubik/RubikSurfaceView.java
+++ b/src/main/java/org/distorted/examples/rubik/RubikSurfaceView.java
@@ -24,42 +24,14 @@ import android.content.Context;
 import android.content.pm.ConfigurationInfo;
 import android.opengl.GLSurfaceView;
 import android.util.AttributeSet;
-import android.view.MotionEvent;
 
-import org.distorted.library.type.Static4D;
+import java.lang.ref.WeakReference;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 class RubikSurfaceView extends GLSurfaceView
 {
-    // Moving the finger from the middle of the vertical screen to the right edge will rotate a
-    // given face by SWIPING_SENSITIVITY/2 degrees.
-    private final static int SWIPING_SENSITIVITY = 240;
-
-    private final static int NONE   =-1;
-    private final static int FRONT  = 0;  // has to be 6 consecutive ints
-    private final static int BACK   = 1;  // FRONT ... BOTTOM
-    private final static int LEFT   = 2;  //
-    private final static int RIGHT  = 3;  //
-    private final static int TOP    = 4;  //
-    private final static int BOTTOM = 5;  //
-
-    static final int VECTX = 0;  //
-    static final int VECTY = 1;  // don't change this
-    static final int VECTZ = 2;  //
-
-    private static final int[] VECT = {VECTX,VECTY,VECTZ};
-
-    private boolean mDragging, mBeginningRotation, mContinuingRotation;
-    private int mX, mY;
-    private Static4D mQuatCurrent, mQuatAccumulated;
-    private int mRotationVect;
-    private RubikRenderer mRenderer;
-
-    private float[] mPoint, mCamera, mTouchPointCastOntoFace, mDiff, mTouchPoint; // all in screen space
-    private int mLastTouchedFace;
-    private int mScreenWidth, mScreenHeight, mScreenMin;
-    private float mCameraDistance;
+    private WeakReference<RubikActivity> mWeakAct;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -69,410 +41,20 @@ class RubikSurfaceView extends GLSurfaceView
 
       if(!isInEditMode())
         {
-        mRotationVect = VECT[0];
-
-        mPoint = new float[3];
-        mCamera= new float[3];
-        mDiff  = new float[3];
-        mTouchPoint = new float[3];
-        mTouchPointCastOntoFace = new float[3];
-
-        mScreenWidth = mScreenHeight = mScreenMin = 0;
-
-        mRenderer = new RubikRenderer(this);
-        mRenderer.createCubeNow(RubikActivity.DEFAULT_SIZE);
-
-        mQuatCurrent     = new Static4D(0,0,0,1);
-        mQuatAccumulated = mRenderer.initializeQuat();
+        mWeakAct = new WeakReference<>( (RubikActivity)context);
 
         final ActivityManager activityManager     = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
         final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
         setEGLContextClientVersion( (configurationInfo.reqGlEsVersion>>16) >= 3 ? 3:2 );
-        setRenderer(mRenderer);
-        }
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event)
-      {
-      int action = event.getAction();
-      int x = (int)event.getX();
-      int y = (int)event.getY();
-
-      switch(action)
-         {
-         case MotionEvent.ACTION_DOWN: mX = x;
-                                       mY = y;
-                                       mLastTouchedFace = faceTouched(x,y);
-
-                                       if( mLastTouchedFace != NONE )
-                                         {
-                                         mDragging           = false;
-                                         mBeginningRotation  = mRenderer.canRotate();
-                                         mContinuingRotation = false;
-                                         }
-                                       else
-                                         {
-                                         mDragging           = true;
-                                         mBeginningRotation  = false;
-                                         mContinuingRotation = false;
-                                         }
-                                       break;
-         case MotionEvent.ACTION_MOVE: if( mDragging )
-                                         {
-                                         mQuatCurrent.set(quatFromDrag(mX-x,mY-y));
-                                         mRenderer.setQuatCurrent(mQuatCurrent);
-                                         }
-                                       if( mBeginningRotation )
-                                         {
-                                         int minimumDistToStartRotating = (mScreenMin*mScreenMin)/100;
-
-                                         if( (mX-x)*(mX-x)+(mY-y)*(mY-y) > minimumDistToStartRotating )
-                                           {
-                                           addNewRotation(x,y);
-                                           mBeginningRotation = false;
-                                           mContinuingRotation= true;
-                                           }
-                                         }
-                                       else if( mContinuingRotation )
-                                         {
-                                         continueRotation(x,y);
-                                         }
-                                       break;
-         case MotionEvent.ACTION_UP  : if( mDragging )
-                                         {
-                                         mQuatAccumulated.set(quatMultiply(mQuatCurrent, mQuatAccumulated));
-                                         mQuatCurrent.set(0f, 0f, 0f, 1f);
-                                         mRenderer.setQuatCurrent(mQuatCurrent);
-                                         mRenderer.setQuatAccumulated(mQuatAccumulated);
-                                         }
-
-                                       if( mContinuingRotation )
-                                         {
-                                         finishRotation();
-                                         }
-
-                                       break;
-         }
-
-      return true;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    void setNewCubeSize(int newCubeSize)
-      {
-      mRenderer.createCube(newCubeSize);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    void scrambleCube()
-      {
-      mRenderer.scrambleCube();
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    void setScreenSize(int width, int height)
-      {
-      mScreenWidth = width;
-      mScreenHeight= height;
-
-      mScreenMin = width<height ? width:height;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    void setCameraDist(float distance)
-      {
-      mCameraDistance = distance;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private int faceTouched(int xTouch, int yTouch)
-      {
-      float cubeHalfSize= mRenderer.returnCubeSizeInScreenSpace()*0.5f;
-
-      convertTouchPointToScreenSpace(xTouch,yTouch);
-      convertCameraPointToScreenSpace();
-
-      for(int face=FRONT; face<=BOTTOM; face++)
-        {
-        if( faceIsVisible(face,cubeHalfSize) )
-          {
-          castTouchPointOntoFace(face,cubeHalfSize, mTouchPointCastOntoFace);
-
-          float qX= (mTouchPointCastOntoFace[0]+cubeHalfSize) / (2*cubeHalfSize);
-          float qY= (mTouchPointCastOntoFace[1]+cubeHalfSize) / (2*cubeHalfSize);
-          float qZ= (mTouchPointCastOntoFace[2]+cubeHalfSize) / (2*cubeHalfSize);
-
-          if( qX<=1 && qX>=0 && qY<=1 && qY>=0 && qZ<=1 && qZ>=0 ) return face;
-          }
-        }
-
-      return NONE;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private void addNewRotation(int x, int y)
-      {
-      float cubeHalfSize= mRenderer.returnCubeSizeInScreenSpace()*0.5f;
-
-      convertTouchPointToScreenSpace(x,y);
-      castTouchPointOntoFace(mLastTouchedFace,cubeHalfSize,mDiff);
-
-      mDiff[0] -= mTouchPointCastOntoFace[0];
-      mDiff[1] -= mTouchPointCastOntoFace[1];
-      mDiff[2] -= mTouchPointCastOntoFace[2];
-
-      int xAxis = retFaceXaxis(mLastTouchedFace);
-      int yAxis = retFaceYaxis(mLastTouchedFace);
-      mRotationVect = (isVertical( mDiff[xAxis], mDiff[yAxis]) ? VECT[xAxis]:VECT[yAxis]);
-      float offset= (mTouchPointCastOntoFace[mRotationVect]+cubeHalfSize)/(2*cubeHalfSize);
-
-      mTouchPoint[0] = mPoint[0];
-      mTouchPoint[1] = mPoint[1];
-      mTouchPoint[2] = mPoint[2];
-
-      mRenderer.getCube().addNewRotation(mRotationVect,offset);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private boolean isVertical(float x, float y)
-      {
-      return (y>x) ? (y>=-x) : (y< -x);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private void continueRotation(int x, int y)
-      {
-      convertTouchPointToScreenSpace(x,y);
-
-      mDiff[0] = mPoint[0]-mTouchPoint[0];
-      mDiff[1] = mPoint[1]-mTouchPoint[1];
-      mDiff[2] = mPoint[2]-mTouchPoint[2];
-
-      int xAxis= retFaceXaxis(mLastTouchedFace);
-      int yAxis= retFaceYaxis(mLastTouchedFace);
-      int sign = retFaceRotationSign(mLastTouchedFace);
-      float angle = (mRotationVect==xAxis ? mDiff[yAxis] : -mDiff[xAxis]);
-
-      mRenderer.getCube().continueRotation(SWIPING_SENSITIVITY*sign*angle/mScreenMin);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private void finishRotation()
-      {
-      mRenderer.finishRotation();
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// return quat1*quat2
-
-    static Static4D quatMultiply( Static4D quat1, Static4D quat2 )
-      {
-      float qx = quat1.get1();
-      float qy = quat1.get2();
-      float qz = quat1.get3();
-      float qw = quat1.get4();
-
-      float rx = quat2.get1();
-      float ry = quat2.get2();
-      float rz = quat2.get3();
-      float rw = quat2.get4();
-
-      float tx = rw*qx - rz*qy + ry*qz + rx*qw;
-      float ty = rw*qy + rz*qx + ry*qw - rx*qz;
-      float tz = rw*qz + rz*qw - ry*qx + rx*qy;
-      float tw = rw*qw - rz*qz - ry*qy - rx*qx;
-
-      return new Static4D(tx,ty,tz,tw);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// rotate 'vector' by quat^(-1)  ( i.e. return (quat^-1)*vector*quat )
-
-    static Static4D rotateVectorByInvertedQuat(Static4D vector, Static4D quat)
-      {
-      float qx = quat.get1();
-      float qy = quat.get2();
-      float qz = quat.get3();
-      float qw = quat.get4();
-
-      Static4D quatInverted= new Static4D(-qx,-qy,-qz,qw);
-      Static4D tmp = quatMultiply(quatInverted,vector);
-
-      return quatMultiply(tmp,quat);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// rotate 'vector' by quat  ( i.e. return quat*vector*(quat^-1) )
-
-    static Static4D rotateVectorByQuat(Static4D vector, Static4D quat)
-      {
-      float qx = quat.get1();
-      float qy = quat.get2();
-      float qz = quat.get3();
-      float qw = quat.get4();
-
-      Static4D quatInverted= new Static4D(-qx,-qy,-qz,qw);
-      Static4D tmp = quatMultiply(quat,vector);
-
-      return quatMultiply(tmp,quatInverted);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private Static4D quatFromDrag(float dragX, float dragY)
-      {
-      float axisX = dragY;  // inverted X and Y - rotation axis is
-      float axisY = dragX;  // perpendicular to (dragX,dragY)   Why not (-dragY, dragX) ? because Y axis is also inverted!
-      float axisZ = 0;
-      float axisL = (float)Math.sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ);
-
-      if( axisL>0 )
-        {
-        axisX /= axisL;
-        axisY /= axisL;
-        axisZ /= axisL;
-
-        float cosA = (float)Math.cos(axisL*Math.PI/mScreenMin);
-        float sinA = (float)Math.sqrt(1-cosA*cosA);
-
-        return new Static4D(axisX*sinA, axisY*sinA, axisZ*sinA, cosA);
-        }
-
-      return new Static4D(0f, 0f, 0f, 1f);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private boolean faceIsVisible(int face, float cubeHalfSize)
-      {
-      int sign = retFaceSign(face);
-      int zAxis= retFaceZaxis(face);
-
-      return sign*mCamera[zAxis] > cubeHalfSize;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private void convertTouchPointToScreenSpace(int x, int y)
-      {
-      float halfScrWidth  = mScreenWidth *0.5f;
-      float halfScrHeight = mScreenHeight*0.5f;
-      Static4D touchPoint = new Static4D(x-halfScrWidth, halfScrHeight-y, 0, 0);
-      Static4D rotatedTouchPoint= rotateVectorByInvertedQuat(touchPoint, mQuatAccumulated);
-
-      mPoint[0] = rotatedTouchPoint.get1();
-      mPoint[1] = rotatedTouchPoint.get2();
-      mPoint[2] = rotatedTouchPoint.get3();
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private void convertCameraPointToScreenSpace()
-      {
-      Static4D cameraPoint = new Static4D(0, 0, mCameraDistance, 0);
-      Static4D rotatedCamera= rotateVectorByInvertedQuat(cameraPoint, mQuatAccumulated);
-
-      mCamera[0] = rotatedCamera.get1();
-      mCamera[1] = rotatedCamera.get2();
-      mCamera[2] = rotatedCamera.get3();
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// given precomputed mCamera and mPoint, respectively camera and touch point positions in ScreenSpace,
-// cast this touch point onto the surface defined by the 'face' and write the cast coords to 'output'.
-// Center of the 'face' = (0,0), third coord always +- cubeHalfSize.
-
-    private void castTouchPointOntoFace(int face, float cubeHalfSize, float[] output)
-      {
-      int sign = retFaceSign(face);
-      int zAxis= retFaceZaxis(face);
-      float diff = mPoint[zAxis]-mCamera[zAxis];
-
-      float ratio =  diff!=0.0f ? (sign*cubeHalfSize-mCamera[zAxis])/diff : 0.0f;
-
-      output[0] = (mPoint[0]-mCamera[0])*ratio + mCamera[0];
-      output[1] = (mPoint[1]-mCamera[1])*ratio + mCamera[1];
-      output[2] = (mPoint[2]-mCamera[2])*ratio + mCamera[2];
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private int retFaceSign(int face)
-      {
-      return (face==FRONT || face==RIGHT || face==TOP) ? 1:-1;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private int retFaceRotationSign(int face)
-      {
-      return (face==BACK || face==RIGHT || face==TOP) ? 1:-1;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// retFace{X,Y,Z}axis: 3 functions which return which real AXIS gets mapped to which when we look
-// directly at a given face. For example, when we look at the RIGHT face of the cube (with TOP still
-// in the top) then the 'real' X axis becomes the 'Z' axis, thus retFaceZaxis(RIGHT) = VECTX.
-
-    private int retFaceXaxis(int face)
-      {
-      switch(face)
-        {
-        case FRONT :
-        case BACK  : return VECTX;
-        case LEFT  :
-        case RIGHT : return VECTZ;
-        case TOP   :
-        case BOTTOM: return VECTX;
+        setRenderer(new RubikRenderer(this));
         }
-
-      return -1;
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    private int retFaceYaxis(int face)
+    RubikActivity getRubikActivity()
       {
-      switch(face)
-        {
-        case FRONT :
-        case BACK  : return VECTY;
-        case LEFT  :
-        case RIGHT : return VECTY;
-        case TOP   :
-        case BOTTOM: return VECTZ;
-        }
-
-      return -1;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private int retFaceZaxis(int face)
-      {
-      switch(face)
-        {
-        case FRONT :
-        case BACK  : return VECTZ;
-        case LEFT  :
-        case RIGHT : return VECTX;
-        case TOP   :
-        case BOTTOM: return VECTY;
-        }
-
-      return -1;
+      return mWeakAct.get();
       }
 }
 
diff --git a/src/main/res/layout/rubiklayout.xml b/src/main/res/layout/rubiklayout.xml
index 3b950e6..94bb9f1 100644
--- a/src/main/res/layout/rubiklayout.xml
+++ b/src/main/res/layout/rubiklayout.xml
@@ -4,65 +4,17 @@
     android:layout_height="fill_parent"
     android:orientation="vertical" >
 
-    <LinearLayout
-        android:id="@+id/linearLayout"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center|fill_horizontal" >
-
-         <Button
-            android:id="@+id/rubikCredits"
-            android:layout_width="wrap_content"
-            android:layout_height="64dp"
-            android:layout_weight="0.5"
-            android:onClick="Credits"
-            android:paddingLeft="5dp"
-            android:paddingRight="5dp"
-            android:text="@string/credits" />
-
-        <ImageButton
-            android:id="@+id/rubikSize2"
-            android:layout_width="64dp"
-            android:layout_height="wrap_content"
-            android:onClick="setSize"
-            android:paddingLeft="5dp"
-            android:paddingRight="5dp"
-            android:src="@raw/button_rubik2"/>
-
-        <ImageButton
-            android:id="@+id/rubikSize3"
-            android:layout_width="64dp"
-            android:layout_height="wrap_content"
-            android:onClick="setSize"
-            android:paddingLeft="5dp"
-            android:paddingRight="5dp"
-            android:src="@raw/button_rubik3"/>
-
-        <ImageButton
-            android:id="@+id/rubikSize4"
-            android:layout_width="64dp"
-            android:layout_height="wrap_content"
-            android:onClick="setSize"
-            android:paddingLeft="5dp"
-            android:paddingRight="5dp"
-            android:src="@raw/button_rubik4"/>
-
-        <Button
-            android:id="@+id/rubikScramble"
-            android:layout_width="wrap_content"
-            android:layout_height="64dp"
-            android:layout_weight="0.5"
-            android:onClick="Scramble"
-            android:paddingLeft="5dp"
-            android:paddingRight="5dp"
-            android:text="@string/scramble" />
-
-    </LinearLayout>
-
     <org.distorted.examples.rubik.RubikSurfaceView
         android:id="@+id/rubikSurfaceView"
         android:layout_width="fill_parent"
         android:layout_height="0dp"
         android:layout_weight="1" />
 
+    <TextView
+        android:id="@+id/rubikText"
+        android:layout_width="fill_parent"
+        android:layout_height="48dp"
+        android:gravity="center_vertical|center"
+        android:textAppearance="?android:attr/textAppearanceMedium" />
+
 </LinearLayout>
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 730ae1b..2999951 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -113,6 +113,7 @@
     <string name="glow_alpha_placeholder">Alpha: %1$.2f</string>
     <string name="inflate_placeholder">Inflate: %1$.2f</string>
     <string name="effect_id_placeholder">ID: %1$d</string>
+    <string name="rubik_placeholder">Cube: %1$d Available Memory: %2$d MB</string>
 
     <string name="example_monalisa">Mona Lisa</string>
     <string name="example_monalisa_subtitle">The basics of Distortions.</string>
@@ -184,8 +185,8 @@
     <string name="example_moving_glow_subtitle">See moving objects glowing with light.</string>
     <string name="example_earth">Earth</string>
     <string name="example_earth_subtitle">Test the sphere Mesh by showing the Earth in cosmos.</string>
-    <string name="example_rubik">Rubik Cube</string>
-    <string name="example_rubik_subtitle">Moveable Rubik Cube.</string>
+    <string name="example_rubik">Memory Test</string>
+    <string name="example_rubik_subtitle">Keep an eye on memory consumption while allocating and deallocating Nodes.</string>
 
     <string name="example_movingeffects_toast">Click on \'RESET\' and define your path by touching the screen. Then click on one of the effects and see it move along your path.</string>
     <string name="example_rotate_toast">Rotate the scene by swiping the screen</string>
