commit a8576d915695d6fddeb03fe756e4ad238c3b798b
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Sun Oct 25 23:09:11 2020 +0000

    Lots of changes:
    
    1) new 'info' button (not working yet)
    2) make all dialogs modal; rework the way the Pattern Dialog appears.

diff --git a/src/main/java/org/distorted/dialogs/RubikDialogAbout.java b/src/main/java/org/distorted/dialogs/RubikDialogAbout.java
index fbe22535..8f383ec9 100644
--- a/src/main/java/org/distorted/dialogs/RubikDialogAbout.java
+++ b/src/main/java/org/distorted/dialogs/RubikDialogAbout.java
@@ -80,7 +80,6 @@ public class RubikDialogAbout extends AppCompatDialogFragment
     TextView text = view.findViewById(R.id.about_version);
     String appName = getString(R.string.app_name);
 
-
     text.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
     text.setText(getString(R.string.ap_placeholder,appName, getAppVers(act)));
 
@@ -115,8 +114,6 @@ public class RubikDialogAbout extends AppCompatDialogFragment
 
     if( window!=null )
       {
-      window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-      window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
       window.getDecorView().setSystemUiVisibility(RubikActivity.FLAGS);
       }
 
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogEffects.java b/src/main/java/org/distorted/dialogs/RubikDialogEffects.java
index 7c49ea3a..9d9389cc 100644
--- a/src/main/java/org/distorted/dialogs/RubikDialogEffects.java
+++ b/src/main/java/org/distorted/dialogs/RubikDialogEffects.java
@@ -261,15 +261,11 @@ public class RubikDialogEffects extends AppCompatDialogFragment implements SeekB
       }
 
     Dialog dialog = builder.create();
-
     dialog.setCanceledOnTouchOutside(false);
-
     Window window = dialog.getWindow();
 
     if( window!=null )
       {
-      window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-      window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
       window.getDecorView().setSystemUiVisibility(RubikActivity.FLAGS);
       }
 
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogError.java b/src/main/java/org/distorted/dialogs/RubikDialogError.java
index a3a07102..b134b375 100644
--- a/src/main/java/org/distorted/dialogs/RubikDialogError.java
+++ b/src/main/java/org/distorted/dialogs/RubikDialogError.java
@@ -83,7 +83,6 @@ public class RubikDialogError extends AppCompatDialogFragment
 
     if( window!=null )
       {
-      window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
       window.getDecorView().setSystemUiVisibility(RubikActivity.FLAGS);
       }
 
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogInfo.java b/src/main/java/org/distorted/dialogs/RubikDialogInfo.java
new file mode 100644
index 00000000..751ea2b9
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/RubikDialogInfo.java
@@ -0,0 +1,100 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 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.dialogs;
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatDialogFragment;
+import androidx.fragment.app.FragmentActivity;
+
+import org.distorted.main.R;
+import org.distorted.main.RubikActivity;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class RubikDialogInfo extends AppCompatDialogFragment
+  {
+  @NonNull
+  @Override
+  public Dialog onCreateDialog(Bundle savedInstanceState)
+    {
+    final FragmentActivity act = getActivity();
+    LayoutInflater inflater = act.getLayoutInflater();
+    AlertDialog.Builder builder = new AlertDialog.Builder(act);
+
+    DisplayMetrics displaymetrics = new DisplayMetrics();
+    act.getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
+    final float titleSize= displaymetrics.widthPixels * RubikActivity.MENU_BIG_TEXT_SIZE;
+    final float okSize   = displaymetrics.widthPixels * RubikActivity.DIALOG_BUTTON_SIZE;
+
+    TextView tv = (TextView) inflater.inflate(R.layout.dialog_title, null);
+    tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, titleSize);
+    tv.setText(R.string.opengl_error);
+    builder.setCustomTitle(tv);
+
+    final View view = inflater.inflate(R.layout.dialog_error, null);
+    TextView text = view.findViewById(R.id.error_string);
+    text.setText(R.string.opengl_error_text);
+
+    builder.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener()
+      {
+      @Override
+      public void onClick(DialogInterface dialog, int which)
+        {
+
+        }
+      });
+
+    builder.setView(view);
+
+    final Dialog dialog = builder.create();
+    dialog.setCanceledOnTouchOutside(false);
+
+    Window window = dialog.getWindow();
+
+    if( window!=null )
+      {
+      window.getDecorView().setSystemUiVisibility(RubikActivity.FLAGS);
+      }
+
+    dialog.setOnShowListener(new DialogInterface.OnShowListener()
+      {
+      @Override
+      public void onShow(DialogInterface dialog)
+        {
+        Button btnPositive = ((AlertDialog)dialog).getButton(Dialog.BUTTON_POSITIVE);
+        btnPositive.setTextSize(TypedValue.COMPLEX_UNIT_PX, okSize);
+        }
+      });
+
+    return dialog;
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogNewRecord.java b/src/main/java/org/distorted/dialogs/RubikDialogNewRecord.java
index d754452b..ccd56691 100644
--- a/src/main/java/org/distorted/dialogs/RubikDialogNewRecord.java
+++ b/src/main/java/org/distorted/dialogs/RubikDialogNewRecord.java
@@ -47,28 +47,6 @@ import org.distorted.states.RubikStatePlay;
 
 public class RubikDialogNewRecord extends AppCompatDialogFragment
   {
-  @Override
-  public void onStart()
-    {
-    super.onStart();
-
-    Dialog dialog = getDialog();
-
-    if( dialog!=null )
-      {
-      dialog.setCanceledOnTouchOutside(false);
-
-      Window window = dialog.getWindow();
-
-      if( window!=null )
-        {
-        window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
   @NonNull
   @Override
   public Dialog onCreateDialog(Bundle savedInstanceState)
@@ -157,15 +135,11 @@ public class RubikDialogNewRecord extends AppCompatDialogFragment
     submit.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
 
     Dialog dialog = builder.create();
-
     dialog.setCanceledOnTouchOutside(false);
-
     Window window = dialog.getWindow();
 
     if( window!=null )
       {
-      window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-      window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
       window.getDecorView().setSystemUiVisibility(RubikActivity.FLAGS);
       }
 
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogPattern.java b/src/main/java/org/distorted/dialogs/RubikDialogPattern.java
index e4670e11..643b3985 100644
--- a/src/main/java/org/distorted/dialogs/RubikDialogPattern.java
+++ b/src/main/java/org/distorted/dialogs/RubikDialogPattern.java
@@ -56,7 +56,7 @@ public class RubikDialogPattern extends AppCompatDialogFragment
   @Override
   public Dialog onCreateDialog(Bundle savedInstanceState)
     {
-    FragmentActivity act = getActivity();
+    final FragmentActivity act = getActivity();
     AlertDialog.Builder builder = new AlertDialog.Builder(act);
 
     DisplayMetrics displaymetrics = new DisplayMetrics();
@@ -70,6 +70,15 @@ public class RubikDialogPattern extends AppCompatDialogFragment
     tv.setText(R.string.choose_pattern);
     builder.setCustomTitle(tv);
 
+    builder.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener()
+      {
+      @Override
+      public void onClick(DialogInterface dialog, int which)
+        {
+
+        }
+      });
+
     Bundle args = getArguments();
     int curTab;
 
@@ -106,18 +115,11 @@ public class RubikDialogPattern extends AppCompatDialogFragment
       }
 
     Dialog dialog = builder.create();
-
     dialog.setCanceledOnTouchOutside(false);
-
     Window window = dialog.getWindow();
 
     if( window!=null )
       {
-      window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
-                      WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
-      window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-
-      window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
       window.getDecorView().setSystemUiVisibility(RubikActivity.FLAGS);
       }
 
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogPatternView.java b/src/main/java/org/distorted/dialogs/RubikDialogPatternView.java
index 4fc2fd48..eb933e9b 100644
--- a/src/main/java/org/distorted/dialogs/RubikDialogPatternView.java
+++ b/src/main/java/org/distorted/dialogs/RubikDialogPatternView.java
@@ -92,15 +92,14 @@ public class RubikDialogPatternView extends FrameLayout
       public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id)
         {
         RubikPattern pattern = RubikPattern.getInstance();
-        int[][] moves = pattern.reInitialize(mTab, groupPosition, childPosition);
-
+        int[][] moves   = pattern.reInitialize(mTab, groupPosition, childPosition);
         ObjectList list = RubikPatternList.getObject(mTab);
-        int size             = RubikPatternList.getSize(mTab);
+        int size        = RubikPatternList.getSize(mTab);
 
         ract.setupObject(list, size, moves);
 
+        StateList.switchState(ract,StateList.PATT);
         RubikStatePattern state = (RubikStatePattern) StateList.PATT.getStateClass();
-
         state.setPattern(ract, mTab, groupPosition, childPosition);
 
         mDialog.rememberState();
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogPrivacy.java b/src/main/java/org/distorted/dialogs/RubikDialogPrivacy.java
index 4112cb6a..a2979478 100644
--- a/src/main/java/org/distorted/dialogs/RubikDialogPrivacy.java
+++ b/src/main/java/org/distorted/dialogs/RubikDialogPrivacy.java
@@ -94,12 +94,10 @@ public class RubikDialogPrivacy extends AppCompatDialogFragment
 
     final Dialog dialog = builder.create();
     dialog.setCanceledOnTouchOutside(false);
-
     Window window = dialog.getWindow();
 
     if( window!=null )
       {
-      window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
       window.getDecorView().setSystemUiVisibility(RubikActivity.FLAGS);
       }
 
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogScores.java b/src/main/java/org/distorted/dialogs/RubikDialogScores.java
index 2df622eb..19969863 100644
--- a/src/main/java/org/distorted/dialogs/RubikDialogScores.java
+++ b/src/main/java/org/distorted/dialogs/RubikDialogScores.java
@@ -123,15 +123,11 @@ public class RubikDialogScores extends AppCompatDialogFragment
       }
 
     Dialog dialog = builder.create();
-
     dialog.setCanceledOnTouchOutside(false);
-
     Window window = dialog.getWindow();
 
     if( window!=null )
       {
-      window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-      window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
       window.getDecorView().setSystemUiVisibility(RubikActivity.FLAGS);
       }
 
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogSetName.java b/src/main/java/org/distorted/dialogs/RubikDialogSetName.java
index af91edee..b54e37d1 100644
--- a/src/main/java/org/distorted/dialogs/RubikDialogSetName.java
+++ b/src/main/java/org/distorted/dialogs/RubikDialogSetName.java
@@ -183,12 +183,10 @@ public class RubikDialogSetName extends AppCompatDialogFragment
       });
 
     dialog.setCanceledOnTouchOutside(false);
-
     Window window = dialog.getWindow();
 
     if( window!=null )
       {
-      window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
       window.getDecorView().setSystemUiVisibility(RubikActivity.FLAGS2);
       }
 
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogSolved.java b/src/main/java/org/distorted/dialogs/RubikDialogSolved.java
index a4f73df4..d1d1f80f 100644
--- a/src/main/java/org/distorted/dialogs/RubikDialogSolved.java
+++ b/src/main/java/org/distorted/dialogs/RubikDialogSolved.java
@@ -93,15 +93,11 @@ public class RubikDialogSolved extends AppCompatDialogFragment
     builder.setView(view);
 
     Dialog dialog = builder.create();
-
     dialog.setCanceledOnTouchOutside(false);
-
     Window window = dialog.getWindow();
 
     if( window!=null )
       {
-      window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-      window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
       window.getDecorView().setSystemUiVisibility(RubikActivity.FLAGS);
       }
 
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogSolverError.java b/src/main/java/org/distorted/dialogs/RubikDialogSolverError.java
index 98fa9d66..fcea3acd 100644
--- a/src/main/java/org/distorted/dialogs/RubikDialogSolverError.java
+++ b/src/main/java/org/distorted/dialogs/RubikDialogSolverError.java
@@ -91,15 +91,11 @@ public class RubikDialogSolverError extends AppCompatDialogFragment
     builder.setView(view);
 
     Dialog dialog = builder.create();
-
     dialog.setCanceledOnTouchOutside(false);
-
     Window window = dialog.getWindow();
 
     if( window!=null )
       {
-      window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-      window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
       window.getDecorView().setSystemUiVisibility(RubikActivity.FLAGS);
       }
 
diff --git a/src/main/java/org/distorted/states/RubikStateAbstract.java b/src/main/java/org/distorted/states/RubikStateAbstract.java
index a0362d90..de10be6e 100644
--- a/src/main/java/org/distorted/states/RubikStateAbstract.java
+++ b/src/main/java/org/distorted/states/RubikStateAbstract.java
@@ -25,6 +25,8 @@ import android.util.TypedValue;
 import android.widget.LinearLayout;
 
 import org.distorted.main.RubikActivity;
+import org.distorted.objects.ObjectList;
+import org.distorted.patterns.RubikPatternList;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -84,6 +86,25 @@ public abstract class RubikStateAbstract
       }
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getPatternOrdinal()
+    {
+    RubikStatePlay play = (RubikStatePlay) StateList.PLAY.getStateClass();
+    int obj  = play.getObject();
+    int size = play.getSize();
+    int ret = RubikPatternList.getOrdinal(obj,size);
+
+    if( ret<0 )
+      {
+      ret = ObjectList.getSizeIndex(RubikStatePlay.DEF_OBJECT,RubikStatePlay.DEF_SIZE);
+      }
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
   abstract void enterState(RubikActivity act);
   abstract void leaveState(RubikActivity act);
   public abstract void savePreferences(SharedPreferences.Editor editor);
diff --git a/src/main/java/org/distorted/states/RubikStateBase.java b/src/main/java/org/distorted/states/RubikStateBase.java
new file mode 100644
index 00000000..aaffdd94
--- /dev/null
+++ b/src/main/java/org/distorted/states/RubikStateBase.java
@@ -0,0 +1,211 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 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.states;
+
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+import org.distorted.dialogs.RubikDialogInfo;
+import org.distorted.main.R;
+import org.distorted.main.RubikActivity;
+import org.distorted.main.RubikPreRender;
+import org.distorted.objects.TwistyObject;
+
+import java.util.ArrayList;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+abstract class RubikStateBase extends RubikStateAbstract implements RubikPreRender.ActionFinishedListener
+  {
+  private static final int DURATION_MILLIS = 750;
+
+  private ImageButton mPrevButton, mLockButton, mInfoButton;
+
+  private boolean mCanPrevMove;
+
+  private static class Move
+    {
+    private int mAxis, mRow, mAngle;
+
+    Move(int axis, int row, int angle)
+      {
+      mAxis = axis;
+      mRow  = row;
+      mAngle= angle;
+      }
+    }
+
+  ArrayList<Move> mMoves;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void backMove(RubikPreRender pre)
+    {
+    if( mCanPrevMove )
+      {
+      int numMoves = mMoves.size();
+
+      if( numMoves>0 )
+        {
+        RubikStateBase.Move move = mMoves.remove(numMoves-1);
+        TwistyObject object = pre.getObject();
+
+        int axis  = move.mAxis;
+        int row   = (1<<move.mRow);
+        int angle = move.mAngle;
+        int numRot= Math.abs(angle*object.getBasicAngle()/360);
+
+        if( angle!=0 )
+          {
+          mCanPrevMove = false;
+          pre.addRotation(this, axis, row, -angle, numRot*DURATION_MILLIS);
+          }
+        else
+          {
+          android.util.Log.e("solution", "error: trying to back move of angle 0");
+          }
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void toggleLock(RubikActivity act)
+    {
+    act.toggleLock();
+    mLockButton.setImageResource(getLockIcon(act));
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int getLockIcon(RubikActivity act)
+    {
+    if( act.retLocked() )
+      {
+      return RubikActivity.getDrawable(R.drawable.ui_small_locked,R.drawable.ui_medium_locked, R.drawable.ui_big_locked, R.drawable.ui_huge_locked);
+      }
+    else
+      {
+      return RubikActivity.getDrawable(R.drawable.ui_small_unlocked,R.drawable.ui_medium_unlocked, R.drawable.ui_big_unlocked, R.drawable.ui_huge_unlocked);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void createBottomPane(final RubikActivity act, float width, ImageButton button)
+    {
+    mCanPrevMove = true;
+
+    if( mMoves==null ) mMoves = new ArrayList<>();
+    else               mMoves.clear();
+
+    LinearLayout layoutBot = act.findViewById(R.id.lowerBar);
+    layoutBot.removeAllViews();
+
+    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT,1);
+
+    LinearLayout layoutLeft = new LinearLayout(act);
+    layoutLeft.setLayoutParams(params);
+    LinearLayout layoutMid = new LinearLayout(act);
+    layoutMid.setLayoutParams(params);
+    LinearLayout layoutRight = new LinearLayout(act);
+    layoutRight.setLayoutParams(params);
+
+    setupPrevButton(act,width);
+    layoutLeft.addView(mPrevButton);
+    setupLockButton(act,width);
+    layoutMid.addView(mLockButton);
+    setupInfoButton(act,width);
+    layoutMid.addView(mInfoButton);
+    layoutRight.addView(button);
+
+    layoutBot.addView(layoutLeft);
+    layoutBot.addView(layoutMid);
+    layoutBot.addView(layoutRight);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void setupLockButton(final RubikActivity act, final float width)
+    {
+    final int icon = getLockIcon(act);
+    mLockButton = new TransparentImageButton(act, icon, width,LinearLayout.LayoutParams.MATCH_PARENT);
+
+    mLockButton.setOnClickListener( new View.OnClickListener()
+      {
+      @Override
+      public void onClick(View v)
+        {
+        toggleLock(act);
+        }
+      });
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void setupInfoButton(final RubikActivity act, final float width)
+    {
+    int icon = RubikActivity.getDrawable(R.drawable.ui_small_info,R.drawable.ui_medium_info, R.drawable.ui_big_info, R.drawable.ui_huge_info);
+    mInfoButton = new TransparentImageButton(act, icon, width,LinearLayout.LayoutParams.MATCH_PARENT);
+
+    mInfoButton.setOnClickListener( new View.OnClickListener()
+      {
+      @Override
+      public void onClick(View v)
+        {
+        RubikDialogInfo infoDiag = new RubikDialogInfo();
+        infoDiag.show(act.getSupportFragmentManager(), null);
+        }
+      });
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void setupPrevButton(final RubikActivity act, final float width)
+    {
+    int icon = RubikActivity.getDrawable(R.drawable.ui_small_cube_back,R.drawable.ui_medium_cube_back, R.drawable.ui_big_cube_back, R.drawable.ui_huge_cube_back);
+    mPrevButton = new TransparentImageButton(act, icon, width,LinearLayout.LayoutParams.MATCH_PARENT);
+
+    mPrevButton.setOnClickListener( new View.OnClickListener()
+      {
+      @Override
+      public void onClick(View v)
+        {
+        RubikPreRender pre = act.getPreRender();
+        backMove(pre);
+        }
+      });
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void addMove(int axis, int row, int angle)
+    {
+    mMoves.add(new Move(axis,row,angle));
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onActionFinished(final long effectID)
+    {
+    mCanPrevMove = true;
+    }
+  }
diff --git a/src/main/java/org/distorted/states/RubikStatePattern.java b/src/main/java/org/distorted/states/RubikStatePattern.java
index d115adcc..a6fce1d5 100644
--- a/src/main/java/org/distorted/states/RubikStatePattern.java
+++ b/src/main/java/org/distorted/states/RubikStatePattern.java
@@ -81,23 +81,7 @@ public class RubikStatePattern extends RubikStateAbstract
     float width = act.getScreenWidthInPixels();
     mButtonSize = width*RubikActivity.BUTTON_TEXT_SIZE;
     float titleSize  = width*RubikActivity.TITLE_TEXT_SIZE;
-
-    RubikStatePlay play = (RubikStatePlay) StateList.PLAY.getStateClass();
-    int obj  = play.getObject();
-    int size = play.getSize();
-
-    mPatternOrdinal = RubikPatternList.getOrdinal(obj,size);
-
-    if( mPatternOrdinal<0 )
-      {
-      mPatternOrdinal = ObjectList.getSizeIndex(RubikStatePlay.DEF_OBJECT,RubikStatePlay.DEF_SIZE);
-      }
-
-    FragmentManager mana = act.getSupportFragmentManager();
-    RubikDialogPattern diag = (RubikDialogPattern) mana.findFragmentByTag(RubikDialogPattern.getDialogTag());
-
-    if( diag==null ) showDialog(mana);
-
+    mPatternOrdinal = getPatternOrdinal();
     LayoutInflater inflater = act.getLayoutInflater();
 
     // TOP ////////////////////////////
@@ -177,19 +161,8 @@ public class RubikStatePattern extends RubikStateAbstract
       public void onClick(View v)
         {
         FragmentManager mana = act.getSupportFragmentManager();
-        RubikDialogPattern diag = (RubikDialogPattern) mana.findFragmentByTag(RubikDialogPattern.getDialogTag());
-
-        if( diag==null )
-          {
-          showDialog(mana);
-          setTrioState(false);
-          }
-        else
-          {
-          diag.rememberState();
-          diag.dismiss();
-          StateList.goBack(act);
-          }
+        StateList.goBack(act);
+        showDialog(mana);
         }
       });
     }
diff --git a/src/main/java/org/distorted/states/RubikStatePlay.java b/src/main/java/org/distorted/states/RubikStatePlay.java
index 9a8f9d6b..f08fab25 100644
--- a/src/main/java/org/distorted/states/RubikStatePlay.java
+++ b/src/main/java/org/distorted/states/RubikStatePlay.java
@@ -32,22 +32,20 @@ import android.widget.ImageButton;
 import android.widget.LinearLayout;
 import android.widget.PopupWindow;
 
+import androidx.fragment.app.FragmentManager;
+
 import org.distorted.dialogs.RubikDialogAbout;
+import org.distorted.dialogs.RubikDialogPattern;
 import org.distorted.dialogs.RubikDialogScores;
 import org.distorted.main.R;
 import org.distorted.main.RubikActivity;
-import org.distorted.main.RubikPreRender;
-import org.distorted.objects.TwistyObject;
 import org.distorted.objects.ObjectList;
 import org.distorted.scores.RubikScores;
 
-import java.util.ArrayList;
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-public class RubikStatePlay extends RubikStateAbstract implements RubikPreRender.ActionFinishedListener
+public class RubikStatePlay extends RubikStateBase
   {
-  private static final int DURATION_MILLIS = 750;
   private static final int LEVELS_SHOWN = 10;
   public  static final int DEF_OBJECT= ObjectList.CUBE.ordinal();
   public  static final int DEF_SIZE  =  3;
@@ -55,7 +53,7 @@ public class RubikStatePlay extends RubikStateAbstract implements RubikPreRender
   private static int[] BUTTON_LABELS = { R.string.scores, R.string.patterns, R.string.solver, R.string.about };
   private static final int NUM_BUTTONS = BUTTON_LABELS.length;
 
-  private ImageButton mObjButton, mMenuButton, mPrevButton, mSolveButton, mLockButton;
+  private ImageButton mObjButton, mMenuButton, mSolveButton;
   private Button mPlayButton;
   private PopupWindow mObjectPopup, mMenuPopup, mPlayPopup;
   private int mObject = DEF_OBJECT;
@@ -66,21 +64,6 @@ public class RubikStatePlay extends RubikStateAbstract implements RubikPreRender
   private int mColCount, mRowCount;
   private LinearLayout mPlayLayout;
 
-  private ArrayList<Move> mMoves;
-  private boolean mCanPrevMove;
-
-  private static class Move
-    {
-    private int mAxis, mRow, mAngle;
-
-    Move(int axis, int row, int angle)
-      {
-      mAxis = axis;
-      mRow  = row;
-      mAngle= angle;
-      }
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   void leaveState(RubikActivity act)
@@ -93,15 +76,11 @@ public class RubikStatePlay extends RubikStateAbstract implements RubikPreRender
   void enterState(final RubikActivity act)
     {
     float width = act.getScreenWidthInPixels();
+
     mMenuTextSize = width*RubikActivity.MENU_MED_TEXT_SIZE;
     mButtonSize   = width*RubikActivity.BUTTON_TEXT_SIZE;
     mMenuItemSize = width*RubikActivity.MENU_ITEM_SIZE;
 
-    mCanPrevMove = true;
-
-    if( mMoves==null ) mMoves = new ArrayList<>();
-    else               mMoves.clear();
-
     mRowCount = ObjectList.getRowCount();
     mColCount = ObjectList.getColumnCount();
 
@@ -121,29 +100,8 @@ public class RubikStatePlay extends RubikStateAbstract implements RubikPreRender
     setupPlayButton(act,width);
     layoutTop.addView(mPlayButton);
 
-    // BOT ////////////////////////////
-    LinearLayout layoutBot = act.findViewById(R.id.lowerBar);
-    layoutBot.removeAllViews();
-
-    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT,1);
-
-    LinearLayout layoutLeft = new LinearLayout(act);
-    layoutLeft.setLayoutParams(params);
-    LinearLayout layoutMid = new LinearLayout(act);
-    layoutMid.setLayoutParams(params);
-    LinearLayout layoutRight = new LinearLayout(act);
-    layoutRight.setLayoutParams(params);
-
-    setupPrevButton(act,width);
-    layoutLeft.addView(mPrevButton);
-    setupLockButton(act,width);
-    layoutMid.addView(mLockButton);
     setupSolveButton(act,width);
-    layoutRight.addView(mSolveButton);
-
-    layoutBot.addView(layoutLeft);
-    layoutBot.addView(layoutMid);
-    layoutBot.addView(layoutRight);
+    createBottomPane(act,width,mSolveButton);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -263,59 +221,6 @@ public class RubikStatePlay extends RubikStateAbstract implements RubikPreRender
       });
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setupSolveButton(final RubikActivity act, final float width)
-    {
-    int icon = RubikActivity.getDrawable(R.drawable.ui_small_cube_solve,R.drawable.ui_medium_cube_solve, R.drawable.ui_big_cube_solve, R.drawable.ui_huge_cube_solve);
-    mSolveButton = new TransparentImageButton(act, icon, width,LinearLayout.LayoutParams.MATCH_PARENT);
-
-    mSolveButton.setOnClickListener( new View.OnClickListener()
-      {
-      @Override
-      public void onClick(View v)
-        {
-        act.getPreRender().solveObject();
-        mMoves.clear();
-        }
-      });
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setupLockButton(final RubikActivity act, final float width)
-    {
-    final int icon = getLockIcon(act);
-    mLockButton = new TransparentImageButton(act, icon, width,LinearLayout.LayoutParams.MATCH_PARENT);
-
-    mLockButton.setOnClickListener( new View.OnClickListener()
-      {
-      @Override
-      public void onClick(View v)
-        {
-        toggleLock(act);
-        }
-      });
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setupPrevButton(final RubikActivity act, final float width)
-    {
-    int icon = RubikActivity.getDrawable(R.drawable.ui_small_cube_back,R.drawable.ui_medium_cube_back, R.drawable.ui_big_cube_back, R.drawable.ui_huge_cube_back);
-    mPrevButton = new TransparentImageButton(act, icon, width,LinearLayout.LayoutParams.MATCH_PARENT);
-
-    mPrevButton.setOnClickListener( new View.OnClickListener()
-      {
-      @Override
-      public void onClick(View v)
-        {
-        RubikPreRender pre = act.getPreRender();
-        backMove(pre);
-        }
-      });
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private void setupObjectWindow(final RubikActivity act, final float width)
@@ -463,37 +368,6 @@ public class RubikStatePlay extends RubikStateAbstract implements RubikPreRender
     adjustLevels(act);
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void backMove(RubikPreRender pre)
-    {
-    if( mCanPrevMove )
-      {
-      int numMoves = mMoves.size();
-
-      if( numMoves>0 )
-        {
-        Move move = mMoves.remove(numMoves-1);
-        TwistyObject object = pre.getObject();
-
-        int axis  = move.mAxis;
-        int row   = (1<<move.mRow);
-        int angle = move.mAngle;
-        int numRot= Math.abs(angle*object.getBasicAngle()/360);
-
-        if( angle!=0 )
-          {
-          mCanPrevMove = false;
-          pre.addRotation(this, axis, row, -angle, numRot*DURATION_MILLIS);
-          }
-        else
-          {
-          android.util.Log.e("solution", "error: trying to back move of angle 0");
-          }
-        }
-      }
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private void MenuAction(RubikActivity act, int button)
@@ -504,45 +378,45 @@ public class RubikStatePlay extends RubikStateAbstract implements RubikPreRender
               int object = play.getObject();
               int size   = play.getSize();
               int sizeIndex = ObjectList.getSizeIndex(object,size);
-
-              Bundle bundle = new Bundle();
-              bundle.putInt("tab", ObjectList.pack(object,sizeIndex) );
-              bundle.putBoolean("submitting", false);
-
+              Bundle sBundle = new Bundle();
+              sBundle.putInt("tab", ObjectList.pack(object,sizeIndex) );
+              sBundle.putBoolean("submitting", false);
               RubikDialogScores scores = new RubikDialogScores();
-              scores.setArguments(bundle);
+              scores.setArguments(sBundle);
               scores.show(act.getSupportFragmentManager(), null);
               break;
-      case 1: StateList.switchState(act, StateList.PATT);
+      case 1: FragmentManager mana = act.getSupportFragmentManager();
+              RubikDialogPattern pDiag = new RubikDialogPattern();
+              Bundle pBundle = new Bundle();
+              int ordinal = getPatternOrdinal();
+              pBundle.putInt("tab", ordinal );
+              pDiag.setArguments(pBundle);
+              pDiag.show( mana, RubikDialogPattern.getDialogTag() );
               break;
       case 2: StateList.switchState(act, StateList.SVER);
               break;
-      case 3: RubikDialogAbout diag = new RubikDialogAbout();
-              diag.show(act.getSupportFragmentManager(), null);
+      case 3: RubikDialogAbout aDiag = new RubikDialogAbout();
+              aDiag.show(act.getSupportFragmentManager(), null);
               break;
       }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void toggleLock(RubikActivity act)
+  void setupSolveButton(final RubikActivity act, final float width)
     {
-    act.toggleLock();
-    mLockButton.setImageResource(getLockIcon(act));
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    int icon = RubikActivity.getDrawable(R.drawable.ui_small_cube_solve,R.drawable.ui_medium_cube_solve, R.drawable.ui_big_cube_solve, R.drawable.ui_huge_cube_solve);
+    mSolveButton = new TransparentImageButton(act, icon, width,LinearLayout.LayoutParams.MATCH_PARENT);
 
-  private int getLockIcon(RubikActivity act)
-    {
-    if( act.retLocked() )
-      {
-      return RubikActivity.getDrawable(R.drawable.ui_small_locked,R.drawable.ui_medium_locked, R.drawable.ui_big_locked, R.drawable.ui_huge_locked);
-      }
-    else
+    mSolveButton.setOnClickListener( new View.OnClickListener()
       {
-      return RubikActivity.getDrawable(R.drawable.ui_small_unlocked,R.drawable.ui_medium_unlocked, R.drawable.ui_big_unlocked, R.drawable.ui_huge_unlocked);
-      }
+      @Override
+      public void onClick(View v)
+        {
+        act.getPreRender().solveObject();
+        mMoves.clear();
+        }
+      });
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -671,13 +545,6 @@ public class RubikStatePlay extends RubikStateAbstract implements RubikPreRender
     return mLevelValue;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void addMove(int axis, int row, int angle)
-    {
-    mMoves.add(new Move(axis,row,angle));
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getObject()
@@ -691,11 +558,4 @@ public class RubikStatePlay extends RubikStateAbstract implements RubikPreRender
     {
     return mSize;
     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onActionFinished(final long effectID)
-    {
-    mCanPrevMove = true;
-    }
   }
diff --git a/src/main/java/org/distorted/states/RubikStateReady.java b/src/main/java/org/distorted/states/RubikStateReady.java
index 63d2fef7..5bb1954b 100644
--- a/src/main/java/org/distorted/states/RubikStateReady.java
+++ b/src/main/java/org/distorted/states/RubikStateReady.java
@@ -32,9 +32,9 @@ import org.distorted.main.RubikActivity;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-public class RubikStateReady extends RubikStateAbstract
+public class RubikStateReady extends RubikStateBase
   {
-  private ImageButton mPrevButton, mLockButton, mBackButton;
+  private ImageButton mBackButton;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -48,7 +48,7 @@ public class RubikStateReady extends RubikStateAbstract
   void enterState(final RubikActivity act)
     {
     float width = act.getScreenWidthInPixels();
-    float titleSize  = width*RubikActivity.TITLE_TEXT_SIZE;
+    float titleSize = width*RubikActivity.TITLE_TEXT_SIZE;
 
     LayoutInflater inflater = act.getLayoutInflater();
 
@@ -60,36 +60,15 @@ public class RubikStateReady extends RubikStateAbstract
     label.setText(R.string.ready);
     layoutTop.addView(label);
 
-    // BOT ////////////////////////////
-    LinearLayout layoutBot = act.findViewById(R.id.lowerBar);
-    layoutBot.removeAllViews();
-
-    LinearLayout.LayoutParams paramsL = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT,1);
-
-    LinearLayout layoutLeft = new LinearLayout(act);
-    layoutLeft.setLayoutParams(paramsL);
-    LinearLayout layoutMid = new LinearLayout(act);
-    layoutMid.setLayoutParams(paramsL);
-    LinearLayout layoutRight = new LinearLayout(act);
-    layoutRight.setLayoutParams(paramsL);
-
-    setupPrevButtom(act,width);
-    layoutLeft.addView(mPrevButton);
-    setupLockButton(act,width);
-    layoutMid.addView(mLockButton);
     setupBackButton(act,width);
-    layoutRight.addView(mBackButton);
-
-    layoutBot.addView(layoutLeft);
-    layoutBot.addView(layoutMid);
-    layoutBot.addView(layoutRight);
+    createBottomPane(act,width,mBackButton);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void setupBackButton(final RubikActivity act, float width)
+  private void setupBackButton(final RubikActivity act, final float width)
     {
-    final int icon = RubikActivity.getDrawable(R.drawable.ui_small_back,R.drawable.ui_medium_back, R.drawable.ui_big_back, R.drawable.ui_huge_back);
+    int icon = RubikActivity.getDrawable(R.drawable.ui_small_back,R.drawable.ui_medium_back, R.drawable.ui_big_back, R.drawable.ui_huge_back);
     mBackButton = new TransparentImageButton(act, icon, width, LinearLayout.LayoutParams.MATCH_PARENT);
 
     mBackButton.setOnClickListener( new View.OnClickListener()
@@ -102,62 +81,6 @@ public class RubikStateReady extends RubikStateAbstract
       });
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setupLockButton(final RubikActivity act, final float width)
-    {
-    final int icon = getLockIcon(act);
-    mLockButton = new TransparentImageButton(act, icon, width,LinearLayout.LayoutParams.MATCH_PARENT);
-
-    mLockButton.setOnClickListener( new View.OnClickListener()
-      {
-      @Override
-      public void onClick(View v)
-        {
-        toggleLock(act);
-        }
-      });
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setupPrevButtom(final RubikActivity act, float width)
-    {
-    int icon = RubikActivity.getDrawable(R.drawable.ui_small_cube_back,R.drawable.ui_medium_cube_back, R.drawable.ui_big_cube_back, R.drawable.ui_huge_cube_back);
-    mPrevButton = new TransparentImageButton(act, icon, width, LinearLayout.LayoutParams.MATCH_PARENT);
-
-    mPrevButton.setOnClickListener( new View.OnClickListener()
-      {
-      @Override
-      public void onClick(View v)
-        {
-        // empty
-        }
-      });
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void toggleLock(RubikActivity act)
-    {
-    act.toggleLock();
-    mLockButton.setImageResource(getLockIcon(act));
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int getLockIcon(RubikActivity act)
-    {
-    if( act.retLocked() )
-      {
-      return RubikActivity.getDrawable(R.drawable.ui_small_locked,R.drawable.ui_medium_locked, R.drawable.ui_big_locked, R.drawable.ui_huge_locked);
-      }
-    else
-      {
-      return RubikActivity.getDrawable(R.drawable.ui_small_unlocked,R.drawable.ui_medium_unlocked, R.drawable.ui_big_unlocked, R.drawable.ui_huge_unlocked);
-      }
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public void savePreferences(SharedPreferences.Editor editor)
diff --git a/src/main/java/org/distorted/states/RubikStateSolving.java b/src/main/java/org/distorted/states/RubikStateSolving.java
index af3baa6c..a7373630 100644
--- a/src/main/java/org/distorted/states/RubikStateSolving.java
+++ b/src/main/java/org/distorted/states/RubikStateSolving.java
@@ -29,42 +29,23 @@ import android.widget.TextView;
 
 import org.distorted.main.R;
 import org.distorted.main.RubikActivity;
-import org.distorted.main.RubikPreRender;
-import org.distorted.objects.TwistyObject;
 import org.distorted.objects.ObjectList;
 import org.distorted.scores.RubikScores;
 
-import java.util.ArrayList;
 import java.util.Timer;
 import java.util.TimerTask;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-public class RubikStateSolving extends RubikStateAbstract implements RubikPreRender.ActionFinishedListener
+public class RubikStateSolving extends RubikStateBase
   {
-  private static final int DURATION_MILLIS = 750;
-
   private TextView mTime;
   private Timer mTimer;
   private long mStartTime;
   private boolean mRunning;
   private RubikScores mScores;
-  private ImageButton mPrevButton, mLockButton, mBackButton;
-  private boolean mCanPrevMove;
-  private ArrayList<Move> mMoves;
   private long mElapsed;
-
-  private static class Move
-    {
-    private int mAxis, mRow, mAngle;
-
-    Move(int axis, int row, int angle)
-      {
-      mAxis = axis;
-      mRow  = row;
-      mAngle= angle;
-      }
-    }
+  private ImageButton mBackButton;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -87,13 +68,8 @@ public class RubikStateSolving extends RubikStateAbstract implements RubikPreRen
     float width = act.getScreenWidthInPixels();
     float titleSize  = width*RubikActivity.TITLE_TEXT_SIZE;
 
-    mCanPrevMove = true;
-
     startCounting(act);
 
-    if( mMoves==null ) mMoves = new ArrayList<>();
-    else               mMoves.clear();
-
     LayoutInflater inflater = act.getLayoutInflater();
 
     // TOP ////////////////////////////
@@ -105,37 +81,15 @@ public class RubikStateSolving extends RubikStateAbstract implements RubikPreRen
     mTime.setText(act.getString(R.string.tm_placeholder,elapsed/60,elapsed%60));
     layoutTop.addView(mTime);
 
-    // BOT ////////////////////////////
-    LinearLayout layoutBot = act.findViewById(R.id.lowerBar);
-    layoutBot.removeAllViews();
-
-    LinearLayout.LayoutParams paramsL = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT,1);
-
-    LinearLayout layoutLeft = new LinearLayout(act);
-    layoutLeft.setLayoutParams(paramsL);
-    LinearLayout layoutMid = new LinearLayout(act);
-    layoutMid.setLayoutParams(paramsL);
-    LinearLayout layoutRight = new LinearLayout(act);
-    layoutRight.setLayoutParams(paramsL);
-
-    setupPrevButtom(act,width);
-    layoutLeft.addView(mPrevButton);
-    setupLockButton(act,width);
-    layoutMid.addView(mLockButton);
-    setupBackButtom(act,width);
-    layoutRight.addView(mBackButton);
-
-    layoutBot.addView(layoutLeft);
-    layoutBot.addView(layoutMid);
-    layoutBot.addView(layoutRight);
+    setupBackButton(act,width);
+    createBottomPane(act,width,mBackButton);
     }
 
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void setupBackButtom(final RubikActivity act, float width)
+  private void setupBackButton(final RubikActivity act, final float width)
     {
-    final int icon = RubikActivity.getDrawable(R.drawable.ui_small_back,R.drawable.ui_medium_back, R.drawable.ui_big_back, R.drawable.ui_huge_back);
+    int icon = RubikActivity.getDrawable(R.drawable.ui_small_back,R.drawable.ui_medium_back, R.drawable.ui_big_back, R.drawable.ui_huge_back);
     mBackButton = new TransparentImageButton(act, icon, width, LinearLayout.LayoutParams.MATCH_PARENT);
 
     mBackButton.setOnClickListener( new View.OnClickListener()
@@ -148,101 +102,6 @@ public class RubikStateSolving extends RubikStateAbstract implements RubikPreRen
       });
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setupLockButton(final RubikActivity act, final float width)
-    {
-    final int icon = getLockIcon(act);
-    mLockButton = new TransparentImageButton(act, icon, width, LinearLayout.LayoutParams.MATCH_PARENT);
-
-    mLockButton.setOnClickListener( new View.OnClickListener()
-      {
-      @Override
-      public void onClick(View v)
-        {
-        toggleLock(act);
-        }
-      });
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setupPrevButtom(final RubikActivity act, float width)
-    {
-    int icon = RubikActivity.getDrawable(R.drawable.ui_small_cube_back,R.drawable.ui_medium_cube_back, R.drawable.ui_big_cube_back, R.drawable.ui_huge_cube_back);
-    mPrevButton = new TransparentImageButton(act, icon, width, LinearLayout.LayoutParams.MATCH_PARENT);
-
-    mPrevButton.setOnClickListener( new View.OnClickListener()
-      {
-      @Override
-      public void onClick(View v)
-        {
-        RubikPreRender pre = act.getPreRender();
-        backMove(pre);
-        }
-      });
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void backMove(RubikPreRender pre)
-    {
-    if( mCanPrevMove )
-      {
-      int numMoves = mMoves.size();
-
-      if( numMoves>0 )
-        {
-        Move move = mMoves.remove(numMoves-1);
-        TwistyObject object = pre.getObject();
-
-        int axis  = move.mAxis;
-        int row   = (1<<move.mRow);
-        int angle = move.mAngle;
-        int numRot= Math.abs(angle*object.getBasicAngle()/360);
-
-        if( angle!=0 )
-          {
-          mCanPrevMove = false;
-          pre.addRotation(this, axis, row, -angle, numRot*DURATION_MILLIS);
-          }
-        else
-          {
-          android.util.Log.e("solution", "error: trying to back move of angle 0");
-          }
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void toggleLock(RubikActivity act)
-    {
-    act.toggleLock();
-    mLockButton.setImageResource(getLockIcon(act));
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int getLockIcon(RubikActivity act)
-    {
-    if( act.retLocked() )
-      {
-      return RubikActivity.getDrawable(R.drawable.ui_small_locked,R.drawable.ui_medium_locked, R.drawable.ui_big_locked, R.drawable.ui_huge_locked);
-      }
-    else
-      {
-      return RubikActivity.getDrawable(R.drawable.ui_small_unlocked,R.drawable.ui_medium_unlocked, R.drawable.ui_big_unlocked, R.drawable.ui_huge_unlocked);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void addMove(int axis, int row, int angle)
-    {
-    mMoves.add(new Move(axis,row,angle));
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public void savePreferences(SharedPreferences.Editor editor)
@@ -332,11 +191,4 @@ public class RubikStateSolving extends RubikStateAbstract implements RubikPreRen
     {
     mElapsed = 0;
     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onActionFinished(final long effectID)
-    {
-    mCanPrevMove = true;
-    }
   }
diff --git a/src/main/res/layout/main.xml b/src/main/res/layout/main.xml
index ee97aa6a..1292a41f 100644
--- a/src/main/res/layout/main.xml
+++ b/src/main/res/layout/main.xml
@@ -24,7 +24,7 @@
     <LinearLayout
         android:id="@+id/upperBar"
         android:layout_below="@id/hiddenBar"
-        android:layout_width="fill_parent"
+        android:layout_width="match_parent"
         android:layout_height="0dp"
         android:gravity="center"
         android:orientation="horizontal"
