commit 4f3e2d901c5a9d431b1b58f9998bfa6dcffe7584
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Mon Jul 11 23:50:38 2022 +0200

    Progress with central menu popup.

diff --git a/src/main/java/org/distorted/dialogs/RubikDialogFreePlaySettings.java b/src/main/java/org/distorted/dialogs/RubikDialogFreePlaySettings.java
deleted file mode 100644
index 1c47b992..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogFreePlaySettings.java
+++ /dev/null
@@ -1,203 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2019 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is free software: you can redistribute it and/or modify                            //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Magic Cube is distributed in the hope that it will be useful,                                 //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.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.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.Spinner;
-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.RubikActivity;
-import org.distorted.screens.RubikScreenFreePlay;
-import org.distorted.main.R;
-import org.distorted.screens.ScreenList;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogFreePlaySettings extends AppCompatDialogFragment implements AdapterView.OnItemSelectedListener
-  {
-  private int mAnimPos, mScraPos;
-  private float mTextSize;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private String[] createScrambleModes()
-    {
-    int len = RubikScreenFreePlay.DEPTHS.length;
-    String[] ret = new String[len];
-
-    for(int i=0; i<len; i++)
-      {
-      ret[i] = String.valueOf(RubikScreenFreePlay.DEPTHS[i]);
-      }
-
-    return ret;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void configureView(FragmentActivity act, View view, float textSize, int scraPos, int animPos)
-    {
-    TextView text1 = view.findViewById(R.id.bandaged_text_depth);
-    text1.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-    TextView text2 = view.findViewById(R.id.bandaged_text_animation);
-    text2.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-
-    mScraPos = scraPos;
-    mAnimPos = animPos;
-
-    Spinner scramble = view.findViewById(R.id.bandaged_spinner_scramble);
-    scramble.setOnItemSelectedListener(this);
-    String[] scrambleModes = createScrambleModes();
-
-    ArrayAdapter<String> scrambleAdapter = new ArrayAdapter<>(act, R.layout.spinner_item, scrambleModes );
-    scrambleAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-    scramble.setAdapter(scrambleAdapter);
-    scramble.setSelection(mScraPos,true);
-    View v1 = scramble.getSelectedView();
-    ((TextView)v1).setTextSize(TypedValue.COMPLEX_UNIT_PX,mTextSize);
-
-    Spinner animation = view.findViewById(R.id.bandaged_spinner_animation);
-    animation.setOnItemSelectedListener(this);
-    String[] animationModes = { "ON" , "OFF" };
-
-    ArrayAdapter<String> animationAdapter = new ArrayAdapter<>(act, R.layout.spinner_item, animationModes );
-    animationAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-    animation.setAdapter(animationAdapter);
-    animation.setSelection(mAnimPos,true);
-    View v2 = animation.getSelectedView();
-    ((TextView)v2).setTextSize(TypedValue.COMPLEX_UNIT_PX,mTextSize);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
-    {
-    int spinnerID = parent.getId();
-
-    ((TextView) view).setTextSize(TypedValue.COMPLEX_UNIT_PX,mTextSize);
-
-    if( spinnerID == R.id.bandaged_spinner_scramble )
-      {
-      mScraPos = pos;
-      }
-    else if( spinnerID == R.id.bandaged_spinner_animation )
-      {
-      mAnimPos = pos;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public void onNothingSelected(AdapterView<?> parent) { }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @NonNull
-  @Override
-  public Dialog onCreateDialog(Bundle savedInstanceState)
-    {
-    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;
-    mTextSize = displaymetrics.widthPixels * RubikActivity.MENU_BIG_TEXT_SIZE;
-
-    TextView tv = (TextView) inflater.inflate(R.layout.dialog_title, null);
-    tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, titleSize);
-    tv.setText(R.string.settings);
-    builder.setCustomTitle(tv);
-
-    Bundle args = getArguments();
-    int scraPos, animPos;
-
-    try
-      {
-      scraPos = args.getInt("scraPos");
-      animPos = args.getInt("animPos");
-      }
-    catch(Exception e)
-      {
-      scraPos = 0;
-      animPos = 0;
-      }
-
-    builder.setCancelable(true);
-    builder.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener()
-      {
-      @Override
-      public void onClick(DialogInterface dialog, int which)
-        {
-        RubikScreenFreePlay free = (RubikScreenFreePlay) ScreenList.FREE.getScreenClass();
-
-        if( free!=null )
-          {
-          free.setScrambleDepth(mScraPos);
-          free.setAnimationMode(mAnimPos);
-          }
-        }
-      });
-
-    final View view = inflater.inflate(R.layout.dialog_settings, null);
-    configureView(act,view,mTextSize,scraPos,animPos);
-    builder.setView(view);
-
-    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/main/RubikActivity.java b/src/main/java/org/distorted/main/RubikActivity.java
index 6bd1c775..3704ccc3 100644
--- a/src/main/java/org/distorted/main/RubikActivity.java
+++ b/src/main/java/org/distorted/main/RubikActivity.java
@@ -56,7 +56,6 @@ import org.distorted.external.RubikScores;
 import org.distorted.external.RubikNetwork;
 import org.distorted.objects.RubikObject;
 import org.distorted.objects.RubikObjectList;
-import org.distorted.screens.RubikScreenFreePlay;
 import org.distorted.screens.RubikScreenSolving;
 import org.distorted.screens.ScreenList;
 import org.distorted.screens.RubikScreenPlay;
@@ -103,7 +102,6 @@ public class RubikActivity extends AppCompatActivity
 
     private static final String KEY_PLAY = "movesController_play";
     private static final String KEY_SOLV = "movesController_solv";
-    public  static final String KEY_FREE = "movesController_free";
 
     private boolean mJustStarted;
     private FirebaseAnalytics mFirebaseAnalytics;
@@ -334,11 +332,6 @@ public class RubikActivity extends AppCompatActivity
         RubikScreenSolving solv = (RubikScreenSolving)ScreenList.SOLV.getScreenClass();
         solv.saveMovePreferences(KEY_SOLV,editor);
         }
-      if( curr==ScreenList.FREE )
-        {
-        RubikScreenFreePlay free = (RubikScreenFreePlay)ScreenList.FREE.getScreenClass();
-        free.saveMovePreferences(KEY_FREE,editor);
-        }
 
       editor.apply();
       }
@@ -407,11 +400,6 @@ public class RubikActivity extends AppCompatActivity
         RubikScreenSolving solv = (RubikScreenSolving)ScreenList.SOLV.getScreenClass();
         solv.restoreMovePreferences(this,KEY_SOLV,preferences);
         }
-      if( curr==ScreenList.FREE )
-        {
-        RubikScreenFreePlay free = (RubikScreenFreePlay)ScreenList.FREE.getScreenClass();
-        free.restoreMovePreferences(this,KEY_FREE,preferences);
-        }
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/main/RubikObjectLibInterface.java b/src/main/java/org/distorted/main/RubikObjectLibInterface.java
index 59424f94..3ca4eb1d 100644
--- a/src/main/java/org/distorted/main/RubikObjectLibInterface.java
+++ b/src/main/java/org/distorted/main/RubikObjectLibInterface.java
@@ -45,7 +45,6 @@ import org.distorted.dialogs.RubikDialogSolved;
 import org.distorted.external.RubikScores;
 import org.distorted.objects.RubikObject;
 import org.distorted.objects.RubikObjectList;
-import org.distorted.screens.RubikScreenFreePlay;
 import org.distorted.screens.RubikScreenPlay;
 import org.distorted.screens.RubikScreenReady;
 import org.distorted.screens.RubikScreenSolver;
@@ -265,11 +264,6 @@ public class RubikObjectLibInterface implements ObjectLibInterface
       RubikScreenPlay play = (RubikScreenPlay) ScreenList.PLAY.getScreenClass();
       play.addMove(mAct.get(), axis, row, angle);
       }
-    if( ScreenList.getCurrentScreen()== ScreenList.FREE )
-      {
-      RubikScreenFreePlay free = (RubikScreenFreePlay) ScreenList.FREE.getScreenClass();
-      free.addMove(mAct.get(), axis, row, angle);
-      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -314,11 +308,6 @@ public class RubikObjectLibInterface implements ObjectLibInterface
       RubikScreenSolving solv = (RubikScreenSolving) ScreenList.SOLV.getScreenClass();
       solv.reddenLock(mAct.get());
       }
-    else if( curr==ScreenList.FREE )
-      {
-      RubikScreenFreePlay free = (RubikScreenFreePlay) ScreenList.FREE.getScreenClass();
-      free.reddenLock(mAct.get());
-      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/screens/RubikScreenFreePlay.java b/src/main/java/org/distorted/screens/RubikScreenFreePlay.java
deleted file mode 100644
index 25466949..00000000
--- a/src/main/java/org/distorted/screens/RubikScreenFreePlay.java
+++ /dev/null
@@ -1,236 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2022 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.screens;
-
-import java.util.Random;
-
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.LinearLayout;
-
-import org.distorted.dialogs.RubikDialogFreePlaySettings;
-import org.distorted.helpers.TransparentImageButton;
-import org.distorted.main.R;
-import org.distorted.main.RubikActivity;
-import org.distorted.objectlib.main.ObjectControl;
-import org.distorted.objectlib.main.TwistyObject;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikScreenFreePlay extends RubikScreenBase
-{
-  public static final int[] DEPTHS = new int[] {20,50,100};
-  public static final int ANIMATION_ON  = 0;
-  public static final int ANIMATION_OFF = 1;
-
-  private TransparentImageButton mBackButton, mScrambleButton, mSolveButton, mSettingsButton;
-  private int[][] mMoves;
-  private Random mRnd;
-  private int mAnimationMode;
-  private int mScrambleDepth;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void leaveScreen(final RubikActivity act)
-    {
-
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void enterScreen(final RubikActivity act)
-    {
-    int width = act.getScreenWidthInPixels();
-
-    LinearLayout.LayoutParams paramsL = new LinearLayout.LayoutParams(width/4, LinearLayout.LayoutParams.MATCH_PARENT);
-    LinearLayout.LayoutParams paramsM = new LinearLayout.LayoutParams(width/2, LinearLayout.LayoutParams.MATCH_PARENT);
-    LinearLayout.LayoutParams paramsR = new LinearLayout.LayoutParams(width/4, LinearLayout.LayoutParams.MATCH_PARENT);
-
-    // UPPER PANE /////////////
-
-    setupSolveButton(act);
-    setupSettingsButton(act);
-    setupScrambleButton(act);
-
-    LinearLayout layoutUpper = act.findViewById(R.id.upperBar);
-
-    LinearLayout layoutLeftU = new LinearLayout(act);
-    layoutLeftU.setLayoutParams(paramsL);
-    LinearLayout layoutMidU  = new LinearLayout(act);
-    layoutMidU.setLayoutParams(paramsM);
-    LinearLayout layoutRightU= new LinearLayout(act);
-    layoutRightU.setLayoutParams(paramsR);
-
-    layoutLeftU.addView(mSolveButton);
-    layoutMidU.addView(mSettingsButton);
-    layoutRightU.addView(mScrambleButton);
-
-    layoutUpper.removeAllViews();
-    layoutUpper.addView(layoutLeftU);
-    layoutUpper.addView(layoutMidU);
-    layoutUpper.addView(layoutRightU);
-
-    // LOWER PANE /////////////
-
-    setupBackButton(act);
-    createBottomPane(act,mBackButton,null);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setupBackButton(final RubikActivity act)
-    {
-    int icon = RubikActivity.getDrawable(R.drawable.ui_small_smallback,R.drawable.ui_medium_smallback, R.drawable.ui_big_smallback, R.drawable.ui_huge_smallback);
-    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT);
-    mBackButton = new TransparentImageButton(act, icon, TransparentImageButton.GRAVITY_MIDDLE, params);
-
-    mBackButton.setOnClickListener( new View.OnClickListener()
-      {
-      @Override
-      public void onClick(View v)
-        {
-        if(act.getControl().isUINotBlocked()) ScreenList.goBack(act);
-        }
-      });
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setupSettingsButton(final RubikActivity act)
-    {
-    int icon = RubikActivity.getDrawable(R.drawable.ui_small_settings,R.drawable.ui_medium_settings, R.drawable.ui_big_settings, R.drawable.ui_huge_settings);
-    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT);
-    mSettingsButton = new TransparentImageButton(act, icon, TransparentImageButton.GRAVITY_MIDDLE, params);
-
-    mSettingsButton.setOnClickListener( new View.OnClickListener()
-      {
-      @Override
-      public void onClick(View v)
-        {
-        if(act.getControl().isUINotBlocked())
-          {
-          Bundle bundle = new Bundle();
-          bundle.putInt("scraPos", mScrambleDepth );
-          bundle.putInt("animPos", mAnimationMode );
-
-          RubikDialogFreePlaySettings setDiag = new RubikDialogFreePlaySettings();
-          setDiag.setArguments(bundle);
-          setDiag.show(act.getSupportFragmentManager(), null);
-          }
-        }
-      });
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setupSolveButton(final RubikActivity 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);
-    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT,1.0f);
-    mSolveButton = new TransparentImageButton(act, icon, TransparentImageButton.GRAVITY_MIDDLE,params);
-
-    mSolveButton.setOnClickListener( new View.OnClickListener()
-      {
-      @Override
-      public void onClick(View v)
-        {
-        ObjectControl control = act.getControl();
-        if( mAnimationMode==ANIMATION_OFF ) control.solveOnly();
-        if( mAnimationMode==ANIMATION_ON  ) control.solveObject();
-        mMovesController.clearMoves(act);
-        }
-      });
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setupScrambleButton(final RubikActivity act)
-    {
-    int icon = RubikActivity.getDrawable(R.drawable.ui_small_cube_scramble,R.drawable.ui_medium_cube_scramble, R.drawable.ui_big_cube_scramble, R.drawable.ui_huge_cube_scramble);
-    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT,1.0f);
-    mScrambleButton = new TransparentImageButton(act, icon, TransparentImageButton.GRAVITY_MIDDLE, params);
-
-    mScrambleButton.setOnClickListener( new View.OnClickListener()
-      {
-      @Override
-      public void onClick(View v)
-        {
-        ObjectControl control = act.getControl();
-        TwistyObject object = control.getObject();
-
-        int depth = DEPTHS[mScrambleDepth];
-
-        if( mAnimationMode==ANIMATION_OFF )
-          {
-          if( mMoves==null || mMoves.length<depth ) mMoves = new int[depth][3];
-          if( mRnd==null ) mRnd = new Random();
-
-          for(int move=0; move<depth; move++)
-            {
-            object.randomizeNewScramble(mMoves, mRnd, move, depth);
-            }
-          for(int move=0; move<depth; move++)
-            {
-            int row = mMoves[move][1];
-            mMoves[move][1] = (1<<row);
-            }
-
-          control.initializeObject(mMoves);
-          }
-
-        if( mAnimationMode==ANIMATION_ON )
-          {
-          control.scrambleObject(depth);
-          }
-        }
-      });
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void savePreferences(SharedPreferences.Editor editor)
-    {
-    editor.putInt("freePlayScreen_scramble" , mScrambleDepth);
-    editor.putInt("freePlayScreen_animation", mAnimationMode);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void restorePreferences(SharedPreferences preferences)
-    {
-    mScrambleDepth = preferences.getInt("freePlayScreen_scramble" ,1);
-    mAnimationMode = preferences.getInt("freePlayScreen_animation",ANIMATION_OFF);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void setAnimationMode(int mode)
-    {
-    mAnimationMode = mode;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void setScrambleDepth(int depth)
-    {
-    mScrambleDepth = depth;
-    }
-}
diff --git a/src/main/java/org/distorted/screens/RubikScreenPlay.java b/src/main/java/org/distorted/screens/RubikScreenPlay.java
index 3a5fa79f..32a20fb9 100644
--- a/src/main/java/org/distorted/screens/RubikScreenPlay.java
+++ b/src/main/java/org/distorted/screens/RubikScreenPlay.java
@@ -49,6 +49,7 @@ import org.distorted.dialogs.RubikDialogPattern;
 import org.distorted.dialogs.RubikDialogScores;
 import org.distorted.dialogs.RubikDialogTutorial;
 import org.distorted.helpers.TransparentImageButton;
+import org.distorted.objectlib.main.ObjectControl;
 import org.distorted.objects.MainEntry;
 import org.distorted.objects.MainEntryList;
 import org.distorted.objects.RubikObject;
@@ -63,12 +64,11 @@ public class RubikScreenPlay extends RubikScreenBase implements RubikNetwork.Upd
   public static final int NUM_COLUMNS  = 5;
   public static final int LEVELS_SHOWN = 8;
   private static final int NUM_BUTTONS = 5;
-
   private static final int[] mLocation = new int[2];
 
   private TransparentImageButton mObjButton, mMenuButton, mSolveButton, mScrambleButton;
-  private PopupWindow mObjectPopup, mMenuPopup, mPlayPopup;
-
+  private PopupWindow mObjectPopup, mMenuPopup;
+  private WeakReference<RubikActivity> mWeakAct;
   private TextView mBubbleUpdates;
   private int mObjectSize, mMenuLayoutWidth, mMenuLayoutHeight, mMenuButtonHeight;
   private int mLevelValue;
@@ -76,8 +76,6 @@ public class RubikScreenPlay extends RubikScreenBase implements RubikNetwork.Upd
   private int mUpperBarHeight;
   private boolean mShouldReactToEndOfScrambling;
   private float mScreenWidth;
-  private WeakReference<RubikActivity> mWeakAct;
-  private String mObjectsPlayed;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -380,11 +378,10 @@ public class RubikScreenPlay extends RubikScreenBase implements RubikNetwork.Upd
     mMenuPopup = new PopupWindow(act);
     mMenuPopup.setContentView(layout);
     mMenuPopup.setFocusable(true);
-    int margin  = (int)(width*RubikActivity.SMALL_MARGIN);
     int padding = (int)(width*RubikActivity.MEDIUM_MARGIN);
 
     mMenuLayoutWidth = (int)(width*0.6f);
-    mMenuLayoutHeight= (int)(2*padding + NUM_BUTTONS*(mMenuButtonHeight+margin));
+    mMenuLayoutHeight= (int)( padding + NUM_BUTTONS*(mMenuButtonHeight+padding) + 4*mMenuButtonHeight+5*padding);
 
     layout.setPadding(padding,0,padding,0);
 
@@ -451,6 +448,73 @@ public class RubikScreenPlay extends RubikScreenBase implements RubikNetwork.Upd
           aDiag.show(act.getSupportFragmentManager(), null);
           }
         });
+
+    setupLevelButtons(act,layout,padding);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setupLevelButtons(RubikActivity act, View layout, int padding)
+    {
+    int sizeW = (mMenuLayoutWidth-4*padding)/3;
+    int sizeH = (int)(sizeW*0.85f);
+    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(sizeW,sizeH);
+    params.setMargins(padding/2,0,padding/2,0);
+
+    Button level1 = layout.findViewById(R.id.level1);
+    level1.setLayoutParams(params);
+    setupLevelButton(act,level1,1);
+    Button level2 = layout.findViewById(R.id.level2);
+    level2.setLayoutParams(params);
+    setupLevelButton(act,level2,2);
+    Button level3 = layout.findViewById(R.id.level3);
+    level3.setLayoutParams(params);
+    setupLevelButton(act,level3,3);
+    Button level4 = layout.findViewById(R.id.level4);
+    level4.setLayoutParams(params);
+    setupLevelButton(act,level4,4);
+    Button level5 = layout.findViewById(R.id.level5);
+    level5.setLayoutParams(params);
+    setupLevelButton(act,level5,5);
+    Button level6 = layout.findViewById(R.id.level6);
+    level6.setLayoutParams(params);
+    setupLevelButton(act,level6,6);
+    Button level7 = layout.findViewById(R.id.level7);
+    level7.setLayoutParams(params);
+    setupLevelButton(act,level7,7);
+    Button level8 = layout.findViewById(R.id.level8);
+    level8.setLayoutParams(params);
+    setupLevelButton(act,level8,8);
+    Button levelM = layout.findViewById(R.id.levelM);
+    levelM.setLayoutParams(params);
+    setupLevelButton(act,levelM,9);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setupLevelButton(RubikActivity act, Button button, int level)
+    {
+    button.setOnClickListener( new View.OnClickListener()
+        {
+        @Override
+        public void onClick(View v)
+          {
+          ObjectControl control = act.getControl();
+
+          if(control.isUINotBlocked())
+            {
+            if( mMenuPopup!=null ) mMenuPopup.dismiss();
+
+            int currObject = RubikObjectList.getCurrObject();
+            RubikObject object = RubikObjectList.getObject(currObject);
+            final int scrambles = level<=LEVELS_SHOWN ? level : (object==null ? 0 : object.getNumScramble());
+
+            mLevelValue = level;
+            mShouldReactToEndOfScrambling = true;
+            control.scrambleObject(scrambles);
+            }
+          }
+        });
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -462,7 +526,6 @@ public class RubikScreenPlay extends RubikScreenBase implements RubikNetwork.Upd
   public void savePreferences(SharedPreferences.Editor editor)
     {
     editor.putInt("play_LevelValue", mLevelValue );
-    editor.putString("play_objectsPlayed", mObjectsPlayed );
 
     if( mObjectPopup!=null )
       {
@@ -475,28 +538,6 @@ public class RubikScreenPlay extends RubikScreenBase implements RubikNetwork.Upd
       mMenuPopup.dismiss();
       mMenuPopup = null;
       }
-
-    if( mPlayPopup!=null )
-      {
-      mPlayPopup.dismiss();
-      mPlayPopup = null;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void addToPlayedObjects()
-    {
-    String name = RubikObjectList.getCurrentName();
-    if( !mObjectsPlayed.contains(name) ) mObjectsPlayed += (" "+name);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private boolean hasBeenPlayed()
-    {
-    String name = RubikObjectList.getCurrentName();
-    return mObjectsPlayed.contains(name);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -504,7 +545,6 @@ public class RubikScreenPlay extends RubikScreenBase implements RubikNetwork.Upd
   public void restorePreferences(SharedPreferences preferences)
     {
     mLevelValue    = preferences.getInt("play_LevelValue", 0);
-    mObjectsPlayed = preferences.getString("play_objectsPlayed", "");
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/screens/ScreenList.java b/src/main/java/org/distorted/screens/ScreenList.java
index 4c873c77..61b2a7c5 100644
--- a/src/main/java/org/distorted/screens/ScreenList.java
+++ b/src/main/java/org/distorted/screens/ScreenList.java
@@ -38,7 +38,6 @@ public enum ScreenList
   SOLU ( SVER , MODE_DRAG   , new RubikScreenSolution() ),
   READ ( PLAY , MODE_ROTATE , new RubikScreenReady()    ),
   DONE ( PLAY , MODE_DRAG   , new RubikScreenDone()     ),
-  FREE ( PLAY , MODE_ROTATE , new RubikScreenFreePlay() ),
   ;
 
   public static final int LENGTH = values().length;
diff --git a/src/main/res/layout/popup_menu.xml b/src/main/res/layout/popup_menu.xml
index 143cffb9..2f8ff1df 100644
--- a/src/main/res/layout/popup_menu.xml
+++ b/src/main/res/layout/popup_menu.xml
@@ -65,4 +65,110 @@
       android:singleLine="true"
       android:backgroundTint="@color/dark_grey"
       android:gravity="center"/>
+
+   <TextView
+       android:layout_width="match_parent"
+       android:text="@string/levels"
+       android:layout_height="0dp"
+       android:layout_weight="0.7"
+       android:gravity="center"/>
+
+   <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1.0"
+        android:gravity="center"
+        android:orientation="horizontal">
+
+        <Button
+           android:id="@+id/level1"
+           android:text="1"
+           android:layout_width="30dp"
+           android:layout_height="30dp"
+           android:paddingRight="10dp"
+           android:paddingLeft="10dp"
+           android:backgroundTint="@color/dark_grey"/>
+        <Button
+           android:id="@+id/level2"
+           android:text="2"
+           android:layout_width="30dp"
+           android:layout_height="30dp"
+           android:paddingRight="10dp"
+           android:paddingLeft="10dp"
+           android:backgroundTint="@color/dark_grey"/>
+        <Button
+           android:id="@+id/level3"
+           android:text="3"
+           android:layout_width="30dp"
+           android:layout_height="30dp"
+           android:paddingRight="10dp"
+           android:paddingLeft="10dp"
+           android:backgroundTint="@color/dark_grey"/>
+   </LinearLayout>
+
+   <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1.0"
+        android:gravity="center"
+        android:orientation="horizontal">
+
+        <Button
+           android:id="@+id/level4"
+           android:text="4"
+           android:layout_width="30dp"
+           android:layout_height="30dp"
+           android:paddingRight="10dp"
+           android:paddingLeft="10dp"
+           android:backgroundTint="@color/dark_grey"/>
+        <Button
+           android:id="@+id/level5"
+           android:text="5"
+           android:layout_width="30dp"
+           android:layout_height="30dp"
+           android:paddingRight="10dp"
+           android:paddingLeft="10dp"
+           android:backgroundTint="@color/dark_grey"/>
+        <Button
+           android:id="@+id/level6"
+           android:text="6"
+           android:layout_width="30dp"
+           android:layout_height="30dp"
+           android:paddingRight="10dp"
+           android:paddingLeft="10dp"
+           android:backgroundTint="@color/dark_grey"/>
+   </LinearLayout>
+
+   <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1.0"
+        android:gravity="center"
+        android:orientation="horizontal">
+
+        <Button
+           android:id="@+id/level7"
+           android:text="7"
+           android:layout_width="30dp"
+           android:layout_height="30dp"
+           android:paddingRight="10dp"
+           android:paddingLeft="10dp"
+           android:backgroundTint="@color/dark_grey"/>
+        <Button
+           android:id="@+id/level8"
+           android:text="8"
+           android:layout_width="30dp"
+           android:layout_height="30dp"
+           android:paddingRight="10dp"
+           android:paddingLeft="10dp"
+           android:backgroundTint="@color/dark_grey"/>
+        <Button
+           android:id="@+id/levelM"
+           android:text="MAX"
+           android:layout_width="30dp"
+           android:layout_height="30dp"
+           android:paddingRight="10dp"
+           android:paddingLeft="10dp"
+           android:backgroundTint="@color/dark_grey"/>
+   </LinearLayout>
 </LinearLayout>
\ No newline at end of file
diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml
index d23b30a1..8eefe08a 100755
--- a/src/main/res/values-de/strings.xml
+++ b/src/main/res/values-de/strings.xml
@@ -37,6 +37,7 @@
     <string name="view">Sehen</string>
     <string name="free_play">Freies Spiel</string>
     <string name="level_full">Volles Scramble</string>
+    <string name="levels">Ebenen</string>
     <string name="save_object">Speichern</string>
     <string name="save_object_really">Möchten Sie diesen Cube speichern?</string>
     <string name="delete_object">Löschen</string>
diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml
index 123472a5..47ad51ba 100755
--- a/src/main/res/values-es/strings.xml
+++ b/src/main/res/values-es/strings.xml
@@ -37,6 +37,7 @@
     <string name="view">Ver</string>
     <string name="free_play">Juego libre</string>
     <string name="level_full">Revuelto Completo</string>
+    <string name="levels">Niveles</string>
     <string name="save_object">Guardar</string>
     <string name="save_object_really">¿Quieres guardar este Cubo?</string>
     <string name="delete_object">Borrar</string>
diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml
index 26a4fd0b..ca4bc825 100755
--- a/src/main/res/values-fr/strings.xml
+++ b/src/main/res/values-fr/strings.xml
@@ -37,6 +37,7 @@
     <string name="view">Regarder</string>
     <string name="free_play">Jeu libre</string>
     <string name="level_full">Brouillage Complet</string>
+    <string name="levels">Niveaux</string>
     <string name="save_object">Enregistrer</string>
     <string name="save_object_really">Voulez-vous enregistrer ce Cube?</string>
     <string name="delete_object">Supprimer</string>
diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml
index 9bfcb32c..89b29286 100755
--- a/src/main/res/values-ja/strings.xml
+++ b/src/main/res/values-ja/strings.xml
@@ -37,6 +37,7 @@
     <string name="view">見る</string>
     <string name="free_play">無料プレイ</string>
     <string name="level_full">フルスクランブル</string>
+    <string name="levels">レベル</string>
     <string name="save_object">保存</string>
     <string name="save_object_really">このキューブを保存しますか?</string>
     <string name="delete_object">消す</string>
diff --git a/src/main/res/values-ko/strings.xml b/src/main/res/values-ko/strings.xml
index 89a85066..679b49cc 100755
--- a/src/main/res/values-ko/strings.xml
+++ b/src/main/res/values-ko/strings.xml
@@ -37,6 +37,7 @@
     <string name="view">보다</string>
     <string name="free_play">게임</string>
     <string name="level_full">풀 스크램블</string>
+    <string name="levels">레벨</string>
     <string name="save_object">저장</string>
     <string name="save_object_really">이 큐브를 저장하시겠습니까?</string>
     <string name="delete_object">삭제</string>
diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml
index 32fcc9cc..393cf595 100644
--- a/src/main/res/values-pl/strings.xml
+++ b/src/main/res/values-pl/strings.xml
@@ -37,6 +37,7 @@
     <string name="view">Zobacz</string>
     <string name="free_play">Zabawa z Kostką</string>
     <string name="level_full">Pełne Pomieszanie</string>
+    <string name="levels">Poziomy</string>
     <string name="save_object">Zapisz</string>
     <string name="save_object_really">Chcesz zapisać tą kostkę?</string>
     <string name="delete_object">Usuń</string>
diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml
index 1ce1e4d1..faae8a5e 100755
--- a/src/main/res/values-ru/strings.xml
+++ b/src/main/res/values-ru/strings.xml
@@ -37,6 +37,7 @@
     <string name="view">Смотри</string>
     <string name="free_play">Игра с кубом</string>
     <string name="level_full">Полная Схватка</string>
+    <string name="levels">Уровни</string>
     <string name="save_object">Сохранять</string>
     <string name="save_object_really">Вы хотите сохранить этот куб?</string>
     <string name="delete_object">Удалить</string>
diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml
index 933c9fc0..3941e21d 100644
--- a/src/main/res/values-zh-rCN/strings.xml
+++ b/src/main/res/values-zh-rCN/strings.xml
@@ -37,6 +37,7 @@
     <string name="view">看</string>
     <string name="free_play">玩立方体</string>
     <string name="level_full">级满</string>
+    <string name="levels">级别</string>
     <string name="save_object">保存</string>
     <string name="save_object_really">你想保存这个多维数据集吗?</string>
     <string name="delete_object">删除</string>
diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml
index 2ef9dcbc..f4d484ac 100644
--- a/src/main/res/values-zh-rTW/strings.xml
+++ b/src/main/res/values-zh-rTW/strings.xml
@@ -37,6 +37,7 @@
     <string name="view">看</string>
     <string name="free_play">玩立方體</string>
     <string name="level_full">級滿</string>
+    <string name="levels">級別</string>
     <string name="save_object">保存</string>
     <string name="save_object_really">你想保存這個多維數據集嗎?</string>
     <string name="delete_object">刪除</string>
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index ef485a62..d18ce50a 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -39,6 +39,7 @@
     <string name="view">View</string>
     <string name="free_play">Free Play</string>
     <string name="level_full">Full Scramble</string>
+    <string name="levels">Levels</string>
     <string name="save_object">Save</string>
     <string name="save_object_really">Do you want to save this Cube?</string>
     <string name="delete_object">Delete</string>
