commit 85b09df4b27e66f742408c36f8d2dfd0903a56e3
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Thu Apr 9 15:04:07 2020 +0100

    Reorganize UI of the Play state.

diff --git a/src/main/java/org/distorted/component/HorizontalNumberPicker.java b/src/main/java/org/distorted/component/HorizontalNumberPicker.java
deleted file mode 100644
index 1020e5eb..00000000
--- a/src/main/java/org/distorted/component/HorizontalNumberPicker.java
+++ /dev/null
@@ -1,129 +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.component;
-
-import android.content.Context;
-import android.support.annotation.Nullable;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.Button;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import org.distorted.main.R;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class HorizontalNumberPicker extends LinearLayout
-{
-  private TextView mNumber;
-  private int mMin, mMax;
-
-  private class AddHandler implements OnClickListener
-    {
-    final int diff;
-
-    AddHandler(int diff)
-      {
-      this.diff = diff;
-      }
-
-    @Override
-    public void onClick(View v)
-      {
-      int newValue = getValue() + diff;
-
-      if (newValue < mMin)
-        {
-        newValue = mMin;
-        }
-      else if (newValue > mMax)
-        {
-        newValue = mMax;
-        }
-      setValue(newValue);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public HorizontalNumberPicker(Context context, @Nullable AttributeSet attrs)
-    {
-    super(context, attrs);
-
-    mMin = 0;
-    mMax = 5;
-
-    inflate(context, R.layout.numberpicker, this);
-
-    mNumber = findViewById(R.id.textNumber);
-
-    final Button btn_less = findViewById(R.id.buttonLess);
-    btn_less.setOnClickListener(new AddHandler(-1));
-
-    final Button btn_more = findViewById(R.id.buttonMore);
-    btn_more.setOnClickListener(new AddHandler( 1));
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getValue()
-    {
-    if(mNumber != null)
-      {
-      try
-        {
-        final String value = mNumber.getText().toString();
-        return Integer.parseInt(value);
-        }
-      catch(NumberFormatException ex)
-        {
-        android.util.Log.e("HorizontalNumberPicker", ex.toString());
-        }
-      }
-    return 0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void setValue(final int value)
-    {
-    if (mNumber != null)
-      {
-      mNumber.setText(String.valueOf(value));
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void setMin(int min)
-    {
-    mMin = min;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void setMax(int max)
-    {
-    mMax = max;
-    }
-}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogScoresPagerAdapter.java b/src/main/java/org/distorted/dialogs/RubikDialogScoresPagerAdapter.java
index 33deabc3..8fd42f56 100644
--- a/src/main/java/org/distorted/dialogs/RubikDialogScoresPagerAdapter.java
+++ b/src/main/java/org/distorted/dialogs/RubikDialogScoresPagerAdapter.java
@@ -32,7 +32,7 @@ import org.distorted.scores.RubikScores;
 import org.distorted.scores.RubikScoresDownloader;
 import org.distorted.objects.RubikObjectList;
 
-import static org.distorted.states.RubikStatePlay.MAX_SCRAMBLE;
+import static org.distorted.states.RubikStatePlay.MAX_LEVEL;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -136,7 +136,7 @@ class RubikDialogScoresPagerAdapter extends PagerAdapter implements RubikScoresD
 
   private void addPage(int tab, final RubikDialogScoresView view, final String[][] country, final String[][] name, final float[][] time)
     {
-    for(int i=0; i<MAX_SCRAMBLE; i++)
+    for(int i=0; i<MAX_LEVEL; i++)
       {
       final LinearLayout section = view.createSection(mAct, tab, i, country[i], name[i], time[i]);
 
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogScoresView.java b/src/main/java/org/distorted/dialogs/RubikDialogScoresView.java
index 3336e038..a1e7840a 100644
--- a/src/main/java/org/distorted/dialogs/RubikDialogScoresView.java
+++ b/src/main/java/org/distorted/dialogs/RubikDialogScoresView.java
@@ -69,11 +69,11 @@ public class RubikDialogScoresView extends FrameLayout
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  LinearLayout createSection(FragmentActivity act, int tab, int scramble, final String[] country, final String[] name, final float[] time)
+  LinearLayout createSection(FragmentActivity act, int tab, int level, final String[] country, final String[] name, final float[] time)
     {
-    LinearLayout level = (LinearLayout)inflate(act, R.layout.dialog_scores_scramble_title, null);
-    TextView text = level.findViewById(R.id.scoresScrambleTitle);
-    text.setText(act.getString(R.string.sc_placeholder,(scramble+1)));
+    LinearLayout levelLayout = (LinearLayout)inflate(act, R.layout.dialog_scores_scramble_title, null);
+    TextView text = levelLayout.findViewById(R.id.scoresScrambleTitle);
+    text.setText(act.getString(R.string.lv_placeholder,level+1));
 
     Resources res = act.getResources();
     String packageName = act.getPackageName();
@@ -83,9 +83,9 @@ public class RubikDialogScoresView extends FrameLayout
     RubikScores scores = RubikScores.getInstance();
 
     boolean inserted = false;
-    long myRecordInMillis = scores.getRecord(object, size, scramble);
+    long myRecordInMillis = scores.getRecord(object, size, level);
     float myRecordInSeconds = (myRecordInMillis/100)/10.0f;
-    boolean mySubmitted = scores.isSubmitted(object, size, scramble);
+    boolean mySubmitted = scores.isSubmitted(object, size, level);
     String myName = scores.getName();
     if( myName.length()==0 ) myName = act.getString(R.string.you);
     int myCountryID = res.getIdentifier( scores.getCountry(), "drawable", packageName);
@@ -105,7 +105,7 @@ public class RubikDialogScoresView extends FrameLayout
           {
           inserted = true;
           View row = createRow(act, myCountryID, myName, myRecord, red);
-          level.addView(row);
+          levelLayout.addView(row);
           }
 
         equals = name[j].equals(myName);
@@ -116,7 +116,7 @@ public class RubikDialogScoresView extends FrameLayout
           theirCountryID = res.getIdentifier( country[j], "drawable", packageName);
           theirTime = Float.toString(time[j]);
           View row = createRow(act, theirCountryID, name[j], theirTime, equals ? red:white);
-          level.addView(row);
+          levelLayout.addView(row);
           }
         }
       }
@@ -124,10 +124,10 @@ public class RubikDialogScoresView extends FrameLayout
     if( !inserted )
       {
       View row = createRow(act, myCountryID, myName, myRecord, red);
-      level.addView(row);
+      levelLayout.addView(row);
       }
 
-    return level;
+    return levelLayout;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/main/RubikActivity.java b/src/main/java/org/distorted/main/RubikActivity.java
index b7ca2b49..47763581 100644
--- a/src/main/java/org/distorted/main/RubikActivity.java
+++ b/src/main/java/org/distorted/main/RubikActivity.java
@@ -262,22 +262,4 @@ public class RubikActivity extends AppCompatActivity
       RubikDialogAbout diag = new RubikDialogAbout();
       diag.show(getSupportFragmentManager(), null);
       }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public void Scramble(View v)
-      {
-      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      RubikStatePlay play = (RubikStatePlay)RubikState.PLAY.getStateClass();
-      int scramble = play.getPicker();
-      view.getPostRender().scrambleObject(scramble);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public void Solve(View v)
-      {
-      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      view.getPostRender().solveObject();
-      }
 }
diff --git a/src/main/java/org/distorted/main/RubikPostRender.java b/src/main/java/org/distorted/main/RubikPostRender.java
index f96370f5..a7b7709a 100644
--- a/src/main/java/org/distorted/main/RubikPostRender.java
+++ b/src/main/java/org/distorted/main/RubikPostRender.java
@@ -361,23 +361,13 @@ public class RubikPostRender implements EffectListener
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  void scrambleObject(int num)
+  void setTextureMap(int cubit, int face, int newColor)
     {
-    if( mCanPlay )
-      {
-      mScrambleObject = true;
-      mScrambleObjectNum = num;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    mSetTextureMap = true;
 
-  void solveObject()
-    {
-    if( mCanPlay )
-      {
-      mSolveObject = true;
-      }
+    mCubit    = cubit;
+    mFace     = face;
+    mNewColor = newColor;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -451,13 +441,23 @@ public class RubikPostRender implements EffectListener
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  void setTextureMap(int cubit, int face, int newColor)
+  public void scrambleObject(int num)
     {
-    mSetTextureMap = true;
+    if( mCanPlay )
+      {
+      mScrambleObject = true;
+      mScrambleObjectNum = num;
+      }
+    }
 
-    mCubit    = cubit;
-    mFace     = face;
-    mNewColor = newColor;
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void solveObject()
+    {
+    if( mCanPlay )
+      {
+      mSolveObject = true;
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/scores/RubikScores.java b/src/main/java/org/distorted/scores/RubikScores.java
index 41375ecf..4a4663e2 100644
--- a/src/main/java/org/distorted/scores/RubikScores.java
+++ b/src/main/java/org/distorted/scores/RubikScores.java
@@ -29,7 +29,7 @@ import java.util.UUID;
 
 import static org.distorted.objects.RubikObjectList.MAX_SIZE;
 import static org.distorted.objects.RubikObjectList.NUM_OBJECTS;
-import static org.distorted.states.RubikStatePlay.MAX_SCRAMBLE;
+import static org.distorted.states.RubikStatePlay.MAX_LEVEL;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // hold my own scores, and some other statistics.
@@ -52,12 +52,12 @@ public class RubikScores
 
   private RubikScores()
     {
-    mRecords   = new long[NUM_OBJECTS][MAX_SIZE][MAX_SCRAMBLE];
-    mSubmitted = new int [NUM_OBJECTS][MAX_SIZE][MAX_SCRAMBLE];
+    mRecords   = new long[NUM_OBJECTS][MAX_SIZE][MAX_LEVEL];
+    mSubmitted = new int [NUM_OBJECTS][MAX_SIZE][MAX_LEVEL];
 
     for(int i=0; i<NUM_OBJECTS; i++)
       for(int j=0; j<MAX_SIZE; j++)
-        for(int k=0; k<MAX_SCRAMBLE; k++)
+        for(int k=0; k<MAX_LEVEL; k++)
           {
           mRecords[i][j][k]   = NO_RECORD;
           mSubmitted[i][j][k] = 0;
@@ -111,7 +111,7 @@ public class RubikScores
 
       for(int size=0; size<length; size++)
         {
-        for(int scramble=0; scramble<MAX_SCRAMBLE; scramble++)
+        for(int scramble=0; scramble<MAX_LEVEL; scramble++)
           {
           if( mSubmitted[object][size][scramble]==0 && mRecords[object][size][scramble]<NO_RECORD )
             {
@@ -160,7 +160,7 @@ public class RubikScores
     int[] sizes;
     int length;
 
-    for(int scramble=0; scramble<MAX_SCRAMBLE; scramble++)
+    for(int scramble=0; scramble<MAX_LEVEL; scramble++)
       {
       builder.setLength(0);
 
@@ -202,7 +202,7 @@ public class RubikScores
     int object, sizeIndex, subm;
     long time;
 
-    for(int scramble=0; scramble<MAX_SCRAMBLE; scramble++)
+    for(int scramble=0; scramble<MAX_LEVEL; scramble++)
       {
       start = end = 0;
       recordStr = preferences.getString("scores_record"+scramble, "");
@@ -268,7 +268,7 @@ public class RubikScores
     {
     int maxsize = RubikObjectList.getObject(object).getSizes().length;
 
-    if( object>=0 && object<NUM_OBJECTS && size>=0 && size<maxsize && scramble>=1 && scramble<=MAX_SCRAMBLE )
+    if( object>=0 && object<NUM_OBJECTS && size>=0 && size<maxsize && scramble>=1 && scramble<=MAX_LEVEL )
       {
       if( mRecords[object][size][scramble-1]> timeTaken )
         {
@@ -310,7 +310,7 @@ public class RubikScores
 
     for(int i=0; i<NUM_OBJECTS; i++)
       for(int j=0; j<MAX_SIZE   ; j++)
-        for(int k=0; k<MAX_SCRAMBLE; k++)
+        for(int k=0; k<MAX_LEVEL; k++)
           {
           mSubmitted[i][j][k]=1;
           }
@@ -345,7 +345,7 @@ public class RubikScores
     {
     int maxsize = RubikObjectList.getObject(object).getSizes().length;
 
-    if( object>=0 && object<NUM_OBJECTS && size>=0 && size<maxsize && scramble>=0 && scramble<MAX_SCRAMBLE )
+    if( object>=0 && object<NUM_OBJECTS && size>=0 && size<maxsize && scramble>=0 && scramble<MAX_LEVEL )
       {
       return mRecords[object][size][scramble];
       }
@@ -359,7 +359,7 @@ public class RubikScores
     {
     int maxsize = RubikObjectList.getObject(object).getSizes().length;
 
-    if( object>=0 && object<NUM_OBJECTS && size>=0 && size<maxsize && scramble>=0 && scramble<MAX_SCRAMBLE )
+    if( object>=0 && object<NUM_OBJECTS && size>=0 && size<maxsize && scramble>=0 && scramble<MAX_LEVEL )
       {
       return mSubmitted[object][size][scramble]==1;
       }
@@ -422,7 +422,7 @@ public class RubikScores
       length = list.getSizes().length;
 
       for(int size=0; size<length; size++)
-        for(int scramble=0; scramble<MAX_SCRAMBLE; scramble++)
+        for(int scramble=0; scramble<MAX_LEVEL; scramble++)
           {
           if( mSubmitted[object][size][scramble]==0 && mRecords[object][size][scramble]<NO_RECORD )
             {
diff --git a/src/main/java/org/distorted/scores/RubikScoresDownloader.java b/src/main/java/org/distorted/scores/RubikScoresDownloader.java
index b974c7c4..0982c5a0 100644
--- a/src/main/java/org/distorted/scores/RubikScoresDownloader.java
+++ b/src/main/java/org/distorted/scores/RubikScoresDownloader.java
@@ -29,7 +29,7 @@ import java.net.HttpURLConnection;
 import java.net.URL;
 import java.net.UnknownHostException;
 
-import static org.distorted.states.RubikStatePlay.MAX_SCRAMBLE;
+import static org.distorted.states.RubikStatePlay.MAX_LEVEL;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -90,11 +90,11 @@ public class RubikScoresDownloader implements Runnable
 
   private static int mTotal = RubikObjectList.getTotal();
   private static String mScores = "";
-  private static String[][][] mCountry = new String[mTotal][MAX_SCRAMBLE][MAX_PLACES];
-  private static String[][][] mName    = new String[mTotal][MAX_SCRAMBLE][MAX_PLACES];
-  private static  float[][][] mTime    = new  float[mTotal][MAX_SCRAMBLE][MAX_PLACES];
+  private static String[][][] mCountry = new String[mTotal][MAX_LEVEL][MAX_PLACES];
+  private static String[][][] mName    = new String[mTotal][MAX_LEVEL][MAX_PLACES];
+  private static  float[][][] mTime    = new  float[mTotal][MAX_LEVEL][MAX_PLACES];
 
-  private static int[][] mPlaces = new int[mTotal][MAX_SCRAMBLE];
+  private static int[][] mPlaces = new int[mTotal][MAX_LEVEL];
   private static RubikScoresDownloader mThis;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -143,7 +143,7 @@ public class RubikScoresDownloader implements Runnable
       }
 
     for(int i=0; i<mTotal; i++)
-      for(int j=0; j<MAX_SCRAMBLE; j++)
+      for(int j=0; j<MAX_LEVEL; j++)
         {
         mPlaces[i][j] = 0;
         }
@@ -190,7 +190,7 @@ public class RubikScoresDownloader implements Runnable
         int time       = Integer.parseInt( row.substring(s3+1,s4) );
         String country = row.substring(s4+1, s5);
 
-        if(scramble>=0 && scramble<MAX_SCRAMBLE)
+        if(scramble>=0 && scramble<MAX_LEVEL)
           {
           int p = mPlaces[object][scramble];
           mPlaces[object][scramble]++;
@@ -311,7 +311,7 @@ public class RubikScoresDownloader implements Runnable
 
     String url="https://distorted.org/magic/cgi-bin/download.cgi";
     url += "?n="+name+"&v="+veri+"&r="+numRuns+"&p="+numPlay+"&e="+mVersion+"d";
-    url += "&o="+RubikObjectList.getObjectList()+"&min=0&max="+MAX_SCRAMBLE+"&l="+MAX_PLACES;
+    url += "&o="+RubikObjectList.getObjectList()+"&min=0&max="+MAX_LEVEL+"&l="+MAX_PLACES;
 
     return url;
     }
@@ -335,7 +335,7 @@ public class RubikScoresDownloader implements Runnable
     String url="https://distorted.org/magic/cgi-bin/submit.cgi";
     url += "?n="+name+"&v="+veri+"&r="+numRuns+"&p="+numPlay+"&i="+deviceID+"&e="+mVersion+"d";
     url += "&o="+objlist+"&l="+lvllist+"&t="+timlist+"&c="+country+"&h="+hash;
-    url += "&oo="+RubikObjectList.getObjectList()+"&min=0&max="+MAX_SCRAMBLE+"&lo="+MAX_PLACES;
+    url += "&oo="+RubikObjectList.getObjectList()+"&min=0&max="+MAX_LEVEL+"&lo="+MAX_PLACES;
 
     return url;
     }
diff --git a/src/main/java/org/distorted/states/RubikStatePlay.java b/src/main/java/org/distorted/states/RubikStatePlay.java
index 46c912e9..b1b214e2 100644
--- a/src/main/java/org/distorted/states/RubikStatePlay.java
+++ b/src/main/java/org/distorted/states/RubikStatePlay.java
@@ -26,65 +26,69 @@ import android.util.DisplayMetrics;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
 import android.widget.Button;
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
 import android.widget.PopupWindow;
+import android.widget.Spinner;
 
-import org.distorted.component.HorizontalNumberPicker;
 import org.distorted.main.R;
 import org.distorted.main.RubikActivity;
 import org.distorted.objects.RubikObjectList;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-public class RubikStatePlay extends RubikStateAbstract
+public class RubikStatePlay extends RubikStateAbstract implements AdapterView.OnItemSelectedListener
   {
-  private static final int MIN_SCRAMBLE =  1;
-  private static final int DEF_SCRAMBLE =  1;
-  public  static final int MAX_SCRAMBLE = 18;
-  public  static final int DEF_OBJECT   = RubikObjectList.CUBE.ordinal();
-  public  static final int DEF_SIZE     =  3;
+  private static final int DEF_LEVEL =  1;
+  public  static final int MAX_LEVEL = 18;
+  public  static final int DEF_OBJECT= RubikObjectList.CUBE.ordinal();
+  public  static final int DEF_SIZE  =  3;
 
   private ImageButton mObjButton;
-  private Button mBackButton;
+  private Button mBackButton, mSolveButton, mPlayButton;
   private PopupWindow mPopup;
-  private HorizontalNumberPicker mPicker;
-  private int mPickerValue;
   private int mObject = DEF_OBJECT;
   private int mSize   = DEF_SIZE;
-  private int mLayoutWidth, mLayoutHeight;
+  private int mLayoutWidth;
   private LinearLayout mLayout;
+  private Spinner mLevelSpinner;
+  private int mLevelValue;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   void leaveState(RubikActivity act)
     {
-    mPickerValue = mPicker.getValue();
+
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   void enterState(final RubikActivity act)
     {
-    LayoutInflater inflater = act.getLayoutInflater();
+    DisplayMetrics metrics = act.getResources().getDisplayMetrics();
+    final float scale = metrics.density;
 
     // TOP ////////////////////////////
-    final View viewTop = inflater.inflate(R.layout.play_title, null);
-
     LinearLayout layoutTop = act.findViewById(R.id.upperBar);
     layoutTop.removeAllViews();
-    layoutTop.addView(viewTop);
+
+    if( mObjButton   ==null ) setupObjectButton(act,scale);
+    layoutTop.addView(mObjButton);
+    if( mLevelSpinner==null ) setupLevelSpinner(act,scale);
+    layoutTop.addView(mLevelSpinner);
+    if( mPlayButton  ==null ) setupPlayButton(act,scale);
+    layoutTop.addView(mPlayButton);
 
     // BOT ////////////////////////////
-    DisplayMetrics metrics = act.getResources().getDisplayMetrics();
-    final float scale = metrics.density;
 
-    if( mObjButton==null ) setupObjectButton(act,scale);
+    if( mSolveButton==null ) setupSolveButton(act,scale);
 
     LinearLayout layoutLeft = act.findViewById(R.id.mainBarLeft);
     layoutLeft.removeAllViews();
-    layoutLeft.addView(mObjButton);
+    layoutLeft.addView(mSolveButton);
 
     if( mBackButton==null ) setupBackButton(act,scale);
 
@@ -92,11 +96,6 @@ public class RubikStatePlay extends RubikStateAbstract
     layoutRight.removeAllViews();
     layoutRight.addView(mBackButton);
 
-    mPicker = act.findViewById(R.id.rubikNumberPicker);
-    mPicker.setMin(MIN_SCRAMBLE);
-    mPicker.setMax(MAX_SCRAMBLE);
-    mPicker.setValue(mPickerValue);
-
     if( mPopup==null ) setupPopupWindow(act, scale);
     }
 
@@ -122,17 +121,90 @@ public class RubikStatePlay extends RubikStateAbstract
           boolean vertical = act.isVertical();
           mLayout.setOrientation(vertical ? LinearLayout.VERTICAL:LinearLayout.HORIZONTAL);
 
-          int height = view.getHeight();
           int width  = view.getWidth();
           int laywid = mLayoutWidth * (vertical? 1:total);
-          int layhei = mLayoutHeight* (vertical? total:1);
 
-          mPopup.showAsDropDown(view, (width-laywid)/2, -height-layhei, Gravity.LEFT);
+          mPopup.showAsDropDown(view, (width-laywid)/2, 0, Gravity.LEFT);
           }
         }
       });
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setupLevelSpinner(final RubikActivity act, final float scale)
+    {
+    int spinnerPadding = (int)(scale* 10 + 0.5f);
+    int spinnerMargin  = (int)(scale*  3 + 0.5f);
+    int spinnerLength  = (int)(scale*150 + 0.5f);
+    LinearLayout.LayoutParams spinnerLayoutParams = new LinearLayout.LayoutParams(spinnerLength,LinearLayout.LayoutParams.MATCH_PARENT);
+    spinnerLayoutParams.topMargin    =   spinnerMargin;
+    spinnerLayoutParams.bottomMargin =   spinnerMargin;
+    spinnerLayoutParams.leftMargin   =   spinnerMargin;
+    spinnerLayoutParams.rightMargin  = 2*spinnerMargin;
+
+    mLevelSpinner = new Spinner(act);
+    mLevelSpinner.setLayoutParams(spinnerLayoutParams);
+    mLevelSpinner.setPadding(spinnerPadding,0,spinnerPadding,0);
+    mLevelSpinner.setBackgroundResource(R.drawable.spinner);
+    mLevelSpinner.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
+
+    mLevelSpinner.setOnItemSelectedListener(this);
+    String[] levels = new String[MAX_LEVEL];
+
+    for(int i=0; i<MAX_LEVEL; i++)
+      {
+      levels[i] = act.getString(R.string.lv_placeholder,i+1);
+      }
+
+    ArrayAdapter<String> adapterType = new ArrayAdapter<>(act,android.R.layout.simple_spinner_item, levels);
+    adapterType.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+    mLevelSpinner.setAdapter(adapterType);
+    mLevelSpinner.setSelection(mLevelValue-1);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setupPlayButton(final RubikActivity act, final float scale)
+    {
+    int padding = (int)(3*scale + 0.5f);
+    LinearLayout.LayoutParams backParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.MATCH_PARENT);
+    mPlayButton = new Button(act);
+    mPlayButton.setLayoutParams(backParams);
+    mPlayButton.setPadding(padding,0,padding,0);
+    mPlayButton.setText(R.string.play);
+
+    mPlayButton.setOnClickListener( new View.OnClickListener()
+      {
+      @Override
+      public void onClick(View v)
+        {
+        act.getPostRender().scrambleObject(mLevelValue);
+        }
+      });
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setupSolveButton(final RubikActivity act, final float scale)
+    {
+    int padding = (int)(3*scale + 0.5f);
+    LinearLayout.LayoutParams backParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT);
+    mSolveButton = new Button(act);
+    mSolveButton.setLayoutParams(backParams);
+    mSolveButton.setPadding(padding,0,padding,0);
+    mSolveButton.setText(R.string.solve);
+
+    mSolveButton.setOnClickListener( new View.OnClickListener()
+      {
+      @Override
+      public void onClick(View v)
+        {
+        act.getPostRender().solveObject();
+        }
+      });
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private void setupBackButton(final RubikActivity act, final float scale)
@@ -169,10 +241,7 @@ public class RubikStatePlay extends RubikStateAbstract
 
     BitmapDrawable bd = (BitmapDrawable) act.getResources().getDrawable(R.drawable.cube2);
     int cubeWidth  = bd.getIntrinsicWidth();
-    int cubeHeight = bd.getIntrinsicHeight();
-
     mLayoutWidth = (int)(cubeWidth + 2*margin + 0.5f);
-    mLayoutHeight= (int)(cubeHeight+ 2*margin + 0.5f);
 
     for(int object=0; object<RubikObjectList.NUM_OBJECTS; object++)
       {
@@ -217,16 +286,15 @@ public class RubikStatePlay extends RubikStateAbstract
 
   public void savePreferences(SharedPreferences.Editor editor)
     {
-    if( mPicker!=null )
-      {
-      editor.putInt("statePlay_scramble", mPicker.getValue() );
-      }
-
+    editor.putInt("statePlay_level" , mLevelValue);
     editor.putInt("statePlay_object", mObject);
     editor.putInt("statePlay_size"  , mSize);
 
-    mObjButton = null;
-    mBackButton= null;
+    mObjButton   = null;
+    mBackButton  = null;
+    mSolveButton = null;
+    mPlayButton  = null;
+    mLevelSpinner= null;
 
     if( mPopup!=null )
       {
@@ -239,9 +307,9 @@ public class RubikStatePlay extends RubikStateAbstract
 
   public void restorePreferences(SharedPreferences preferences)
     {
-    mPickerValue= preferences.getInt("statePlay_scramble", DEF_SCRAMBLE);
-    mObject     = preferences.getInt("statePlay_object"  , DEF_OBJECT  );
-    mSize       = preferences.getInt("statePlay_size"    , DEF_SIZE    );
+    mLevelValue = preferences.getInt("statePlay_level" , DEF_LEVEL );
+    mObject     = preferences.getInt("statePlay_object", DEF_OBJECT);
+    mSize       = preferences.getInt("statePlay_size"  , DEF_SIZE  );
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -268,9 +336,9 @@ public class RubikStatePlay extends RubikStateAbstract
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public int getPicker()
+  int getLevel()
     {
-    return mPicker!=null ? mPicker.getValue() : 1;
+    return mLevelValue;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -286,4 +354,15 @@ public class RubikStatePlay extends RubikStateAbstract
     {
     return mSize;
     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
+    {
+    mLevelValue = pos+1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onNothingSelected(AdapterView<?> parent) { }
   }
diff --git a/src/main/java/org/distorted/states/RubikStateSolving.java b/src/main/java/org/distorted/states/RubikStateSolving.java
index 7474c1cf..dad555ed 100644
--- a/src/main/java/org/distorted/states/RubikStateSolving.java
+++ b/src/main/java/org/distorted/states/RubikStateSolving.java
@@ -172,10 +172,10 @@ public class RubikStateSolving extends RubikStateAbstract
       RubikStatePlay play = (RubikStatePlay)RubikState.PLAY.getStateClass();
       int object  = play.getObject();
       int size    = play.getSize();
-      int scramble= play.getPicker();
+      int level   = play.getLevel();
       int realSize= RubikObjectList.getSizeIndex(object,size);
 
-      boolean isNew = mScores.setRecord(object, realSize, scramble, timeTaken);
+      boolean isNew = mScores.setRecord(object, realSize, level, timeTaken);
 
       return isNew ? timeTaken : -timeTaken;
       }
diff --git a/src/main/res/drawable/cube_menu.png b/src/main/res/drawable/cube_menu.png
index 0ba0061e..f8a97b0c 100644
Binary files a/src/main/res/drawable/cube_menu.png and b/src/main/res/drawable/cube_menu.png differ
diff --git a/src/main/res/layout/main.xml b/src/main/res/layout/main.xml
index 14e2402d..fb0e0388 100644
--- a/src/main/res/layout/main.xml
+++ b/src/main/res/layout/main.xml
@@ -7,7 +7,7 @@
     <LinearLayout
         android:id="@+id/upperBar"
         android:layout_width="fill_parent"
-        android:layout_height="50dp"
+        android:layout_height="52dp"
         android:gravity="center"
         android:orientation="horizontal">
     </LinearLayout>
@@ -20,7 +20,7 @@
 
     <LinearLayout
         android:layout_width="match_parent"
-        android:layout_height="60dp"
+        android:layout_height="55dp"
         android:orientation="horizontal">
 
         <LinearLayout
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 203bfb7c..a9ba1bd6 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -52,7 +52,7 @@
     <string name="solver_cube3_error9">Solver interrupted!</string>
 
     <string name="ms_placeholder">%1$d ms</string>
-    <string name="sc_placeholder">Scramble %1$d</string>
+    <string name="lv_placeholder">Level %1$d</string>
     <string name="tm_placeholder">%1$02d:%2$02d</string>
     <string name="ap_placeholder">%1$s %2$s</string>
     <string name="ti_placeholder">%1$.1f seconds</string>
