commit 211b48f2c72d6f820609c49eb41a37d6f183d73a
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Tue Feb 18 22:58:37 2020 +0000

    Separate the States (and also Dialogues) into their own package.

diff --git a/src/main/java/org/distorted/dialog/RubikDialogAbout.java b/src/main/java/org/distorted/dialog/RubikDialogAbout.java
new file mode 100644
index 00000000..7b228550
--- /dev/null
+++ b/src/main/java/org/distorted/dialog/RubikDialogAbout.java
@@ -0,0 +1,83 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.dialog;
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.FragmentActivity;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatDialogFragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import org.distorted.magic.R;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class RubikDialogAbout extends AppCompatDialogFragment
+  {
+  @Override
+  public void onStart()
+    {
+    super.onStart();
+
+    Window window = getDialog().getWindow();
+    window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
+                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+    window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @NonNull
+  @Override
+  public Dialog onCreateDialog(Bundle savedInstanceState)
+    {
+    FragmentActivity act = getActivity();
+    LayoutInflater inflater = act.getLayoutInflater();
+    AlertDialog.Builder builder = new AlertDialog.Builder(act);
+
+    TextView tv = (TextView) inflater.inflate(R.layout.dialog_title, null);
+    tv.setText(R.string.about);
+    builder.setCustomTitle(tv);
+
+    builder.setCancelable(true);
+    builder.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener()
+      {
+      @Override
+      public void onClick(DialogInterface dialog, int which)
+        {
+
+        }
+      });
+
+    final View view = inflater.inflate(R.layout.dialog_about, null);
+    TextView text = view.findViewById(R.id.about_version);
+    text.setText(R.string.app_version);
+    builder.setView(view);
+
+    return builder.create();
+    }
+  }
diff --git a/src/main/java/org/distorted/dialog/RubikDialogMain.java b/src/main/java/org/distorted/dialog/RubikDialogMain.java
new file mode 100644
index 00000000..bb1f35ed
--- /dev/null
+++ b/src/main/java/org/distorted/dialog/RubikDialogMain.java
@@ -0,0 +1,71 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.dialog;
+
+import android.app.Dialog;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.FragmentActivity;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatDialogFragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+
+import org.distorted.magic.R;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class RubikDialogMain extends AppCompatDialogFragment
+  {
+  @Override
+  public void onStart()
+    {
+    super.onStart();
+
+    Window window = getDialog().getWindow();
+    window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
+                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+    window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @NonNull
+  @Override
+  public Dialog onCreateDialog(Bundle savedInstanceState)
+    {
+    FragmentActivity act = getActivity();
+    AlertDialog.Builder builder = new AlertDialog.Builder(act);
+    LayoutInflater inflater = act.getLayoutInflater();
+    final View view = inflater.inflate(R.layout.dialog_main, null);
+    builder.setView(view);
+
+    return builder.create();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static String getDialogTag()
+    {
+    return "DialogMain";
+    }
+  }
\ No newline at end of file
diff --git a/src/main/java/org/distorted/dialog/RubikDialogScores.java b/src/main/java/org/distorted/dialog/RubikDialogScores.java
new file mode 100644
index 00000000..fbdf174b
--- /dev/null
+++ b/src/main/java/org/distorted/dialog/RubikDialogScores.java
@@ -0,0 +1,117 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.dialog;
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatDialogFragment;
+import android.support.design.widget.TabLayout;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.distorted.magic.R;
+import org.distorted.magic.RubikSize;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class RubikDialogScores extends AppCompatDialogFragment
+  {
+  RubikDialogScoresPagerAdapter mPagerAdapter;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public void onStart()
+    {
+    super.onStart();
+
+    Window window = getDialog().getWindow();
+    window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
+                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+    window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @NonNull
+  @Override
+  public Dialog onCreateDialog(Bundle savedInstanceState)
+    {
+    FragmentActivity act = getActivity();
+    AlertDialog.Builder builder = new AlertDialog.Builder(act);
+
+    LayoutInflater layoutInflater = act.getLayoutInflater();
+    TextView tv = (TextView) layoutInflater.inflate(R.layout.dialog_title, null);
+    tv.setText(R.string.scores);
+    builder.setCustomTitle(tv);
+
+    builder.setCancelable(true);
+    builder.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener()
+      {
+      @Override
+      public void onClick(DialogInterface dialog, int which)
+        {
+
+        }
+      });
+
+    Bundle args = getArguments();
+    int curTab;
+
+    try
+      {
+      curTab = args.getInt("tab");
+      }
+    catch(Exception e)
+      {
+      curTab = 0;
+      }
+
+    LayoutInflater inflater = act.getLayoutInflater();
+    final View view = inflater.inflate(R.layout.dialog_scores, null);
+    builder.setView(view);
+
+    ViewPager viewPager = view.findViewById(R.id.viewpager);
+    TabLayout tabLayout = view.findViewById(R.id.sliding_tabs);
+    mPagerAdapter = new RubikDialogScoresPagerAdapter(act,viewPager);
+    tabLayout.setupWithViewPager(viewPager);
+
+    viewPager.setCurrentItem(curTab);
+
+    for (int i = 0; i< RubikSize.LENGTH; i++)
+      {
+      ImageView imageView = new ImageView(act);
+      imageView.setImageResource(RubikSize.getSize(i).getIconID());
+      TabLayout.Tab tab = tabLayout.getTabAt(i);
+      if(tab!=null) tab.setCustomView(imageView);
+      }
+
+    return builder.create();
+    }
+  }
diff --git a/src/main/java/org/distorted/dialog/RubikDialogScoresPagerAdapter.java b/src/main/java/org/distorted/dialog/RubikDialogScoresPagerAdapter.java
new file mode 100644
index 00000000..10083572
--- /dev/null
+++ b/src/main/java/org/distorted/dialog/RubikDialogScoresPagerAdapter.java
@@ -0,0 +1,196 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.dialog;
+
+import android.support.annotation.NonNull;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.distorted.magic.RubikScoresDownloader;
+import org.distorted.magic.RubikSize;
+import org.distorted.uistate.RubikStatePlay;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class RubikDialogScoresPagerAdapter extends PagerAdapter implements RubikScoresDownloader.Receiver
+  {
+  private FragmentActivity mAct;
+  private RubikDialogScoresView[] mViews;
+  private ViewPager mViewPager;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void receive(final int[][][] country, final String[][][] name, final String[][][] time)
+    {
+    prepareView();
+
+    int c = mViewPager.getCurrentItem();
+
+    addPage(mViews[c],country[c],name[c],time[c]);
+
+    for(int i = 0; i< RubikSize.LENGTH; i++)
+      {
+      if( i==c ) continue;
+
+      addPage(mViews[i],country[i],name[i],time[i]);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void prepareView()
+    {
+    mAct.runOnUiThread(new Runnable()
+      {
+      @Override
+      public void run()
+        {
+        for(int i=0; i<RubikSize.LENGTH; i++)
+          {
+          mViews[i].prepareView(mAct);
+          }
+        }
+      });
+    try
+      {
+      Thread.sleep(200);
+      }
+    catch( InterruptedException ie)
+      {
+
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void addPage(final RubikDialogScoresView view, final int[][] country, final String[][] name, final String[][] time)
+    {
+    for(int section = 0; section< RubikStatePlay.MAX_SCRAMBLE; section++)
+      {
+      final int sec = section;
+      final int[] c = country[section];
+      final String[] n = name[section];
+      final String[] t = time[section];
+
+      mAct.runOnUiThread(new Runnable()
+        {
+        @Override
+        public void run()
+          {
+          view.addSection(mAct, sec, c, n, t);
+          }
+        });
+
+      try
+        {
+        Thread.sleep(60);
+        }
+      catch( InterruptedException ie)
+        {
+
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void exception(final String exce)
+    {
+    mAct.runOnUiThread(new Runnable()
+      {
+      @Override
+      public void run()
+        {
+        for(int i=0; i<RubikSize.LENGTH; i++)
+          {
+          mViews[i].exception(exce);
+          }
+        }
+      });
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  RubikDialogScoresPagerAdapter(FragmentActivity act, ViewPager viewPager)
+    {
+    mAct = act;
+    mViews = new RubikDialogScoresView[RubikSize.LENGTH];
+    mViewPager = viewPager;
+
+    viewPager.setAdapter(this);
+    viewPager.setOffscreenPageLimit( RubikSize.LENGTH-1 );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  @NonNull
+  public Object instantiateItem(@NonNull ViewGroup collection, int position)
+    {
+    mViews[position] = new RubikDialogScoresView(mAct);
+    collection.addView(mViews[position]);
+
+    boolean allCreated = true;
+
+    for(int i=0; i<RubikSize.LENGTH; i++)
+      {
+      if( mViews[i]==null )
+        {
+        allCreated = false;
+        break;
+        }
+      }
+
+    if( allCreated )
+      {
+      RubikScoresDownloader downloader = new RubikScoresDownloader();
+      downloader.download(this, mAct.getResources(), mAct.getPackageName());
+      }
+
+    return mViews[position];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public void destroyItem(ViewGroup collection, int position, @NonNull Object view)
+    {
+    collection.removeView((View) view);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public int getCount()
+    {
+    return RubikSize.LENGTH;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public boolean isViewFromObject(@NonNull View view, @NonNull Object object)
+    {
+    return view == object;
+    }
+  }
diff --git a/src/main/java/org/distorted/dialog/RubikDialogScoresView.java b/src/main/java/org/distorted/dialog/RubikDialogScoresView.java
new file mode 100644
index 00000000..f740e312
--- /dev/null
+++ b/src/main/java/org/distorted/dialog/RubikDialogScoresView.java
@@ -0,0 +1,111 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.dialog;
+
+import android.content.Context;
+import android.support.v4.app.FragmentActivity;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.distorted.magic.R;
+import org.distorted.magic.RubikScoresDownloader;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class RubikDialogScoresView extends FrameLayout
+  {
+  LinearLayout mLayout;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public RubikDialogScoresView(Context context, AttributeSet attrs, int defStyle)
+    {
+    super(context, attrs, defStyle);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public RubikDialogScoresView(Context context, AttributeSet attrs)
+    {
+    super(context, attrs);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public RubikDialogScoresView(Context context)
+    {
+    super(context);
+
+    View view = inflate(context, R.layout.dialog_scores_downloading, null);
+    addView(view);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void exception(final String exce)
+    {
+    TextView text = findViewById(R.id.downloading_text);
+    text.setText(exce);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void prepareView(FragmentActivity act)
+    {
+    removeAllViews();
+
+    View tab = inflate(act, R.layout.dialog_scores_tab, null);
+    mLayout = tab.findViewById(R.id.tabLayout);
+    addView(tab);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void addSection(FragmentActivity act, int scramble, final int[] country, final String[] name, final String[] 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)));
+
+    for(int j = 0; j< RubikScoresDownloader.MAX_PLACES; j++)
+      {
+      if( name[j] != null )
+        {
+        View row = inflate(act, R.layout.dialog_scores_scramble_row, null);
+
+        ImageView imgCoun = row.findViewById(R.id.scoresScrambleRowCountry);
+        TextView textName = row.findViewById(R.id.scoresScrambleRowName);
+        TextView textTime = row.findViewById(R.id.scoresScrambleRowTime);
+
+        imgCoun.setImageResource(country[j]);
+        textName.setText(name[j]);
+        textTime.setText(time[j]);
+
+        level.addView(row);
+        }
+      }
+
+    mLayout.addView(level);
+    }
+  }
diff --git a/src/main/java/org/distorted/dialog/RubikDialogSettings.java b/src/main/java/org/distorted/dialog/RubikDialogSettings.java
new file mode 100644
index 00000000..a3a09a32
--- /dev/null
+++ b/src/main/java/org/distorted/dialog/RubikDialogSettings.java
@@ -0,0 +1,282 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.dialog;
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatDialogFragment;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import org.distorted.effect.BaseEffect;
+import org.distorted.magic.R;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class RubikDialogSettings extends AppCompatDialogFragment implements SeekBar.OnSeekBarChangeListener, AdapterView.OnItemSelectedListener
+  {
+  private TextView[] mDurationText;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void addSettingsSection(FragmentActivity act, LinearLayout layout, int index)
+    {
+    BaseEffect.Type beType = BaseEffect.Type.getType(index);
+    DisplayMetrics metrics = act.getResources().getDisplayMetrics();
+    float scale = metrics.density;
+
+    int textH=32;
+    int layoH=36;
+    int margH=10;
+
+    ///// OUTER LAYOUT ///////////////////////////////////////////////////////////////////
+
+    int margin = (int)(scale*margH + 0.5f);
+    int color  = ContextCompat.getColor(act, R.color.grey);
+    LinearLayout outerLayout = new LinearLayout(act);
+    LinearLayout.LayoutParams outerLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT, 0.5f);
+    outerLayoutParams.topMargin    = margin;
+    outerLayoutParams.bottomMargin = 0;
+    outerLayoutParams.leftMargin   = margin;
+    outerLayoutParams.rightMargin  = margin;
+
+    outerLayout.setLayoutParams(outerLayoutParams);
+    outerLayout.setGravity(Gravity.CENTER|Gravity.FILL_HORIZONTAL);
+    outerLayout.setBackgroundColor(color);
+    outerLayout.setOrientation(LinearLayout.VERTICAL);
+    layout.addView(outerLayout);
+
+    ///// TEXT ///////////////////////////////////////////////////////////////////////////
+
+    int layoutHeight = (int)(scale*textH + 0.5f);
+    int padding      = (int)(scale*10    + 0.5f);
+
+    LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,layoutHeight);
+
+    TextView textView = new TextView(act);
+    textView.setText(beType.getText());
+    textView.setLayoutParams(textParams);
+    textView.setGravity(Gravity.CENTER);
+    textView.setPadding(padding,0,padding,0);
+    textView.setTextAppearance(android.R.style.TextAppearance_Small);
+    outerLayout.addView(textView);
+
+    ///// INNER LAYOUT1 //////////////////////////////////////////////////////////////////
+
+    int innerLayout1Height = (int)(scale*layoH + 0.5f);
+    LinearLayout innerLayout1 = new LinearLayout(act);
+    LinearLayout.LayoutParams innerLayout1Params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,innerLayout1Height);
+
+    innerLayout1.setLayoutParams(innerLayout1Params);
+    innerLayout1.setGravity(Gravity.CENTER|Gravity.FILL_HORIZONTAL);
+    innerLayout1.setOrientation(LinearLayout.HORIZONTAL);
+    outerLayout.addView(innerLayout1);
+
+    ///// STUFF INSIDE INNER LAYOUT1 /////////////////////////////////////////////////////
+
+    int text1Padding = (int)(scale*5 + 0.5f);
+    LinearLayout.LayoutParams text1LayoutParams = new LinearLayout.LayoutParams(0,layoutHeight,0.2f);
+
+    TextView text1View = new TextView(act);
+    text1View.setText(R.string.duration);
+    text1View.setLayoutParams(text1LayoutParams);
+    text1View.setGravity(Gravity.START|Gravity.CENTER);
+    text1View.setPadding(text1Padding,0,text1Padding,0);
+    text1View.setTextAppearance(android.R.style.TextAppearance_Small);
+    innerLayout1.addView(text1View);
+    //////////////////////////////////////////////////////////////////
+    int text2Padding = (int)(scale*5 + 0.5f);
+    LinearLayout.LayoutParams text2LayoutParams = new LinearLayout.LayoutParams(0,layoutHeight,0.2f);
+
+    mDurationText[index] = new TextView(act);
+    mDurationText[index].setLayoutParams(text2LayoutParams);
+    mDurationText[index].setGravity(Gravity.END|Gravity.CENTER);
+    mDurationText[index].setPadding(text2Padding,0,text2Padding,0);
+    mDurationText[index].setTextAppearance(android.R.style.TextAppearance_Small);
+    innerLayout1.addView(mDurationText[index]);
+    //////////////////////////////////////////////////////////////////
+    int seekPadding = (int)(scale*10 + 0.5f);
+    LinearLayout.LayoutParams seekLayoutParams = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,0.6f);
+
+    SeekBar seekBar = new SeekBar(act);
+    seekBar.setLayoutParams(seekLayoutParams);
+    seekBar.setPadding(seekPadding,0,seekPadding,0);
+    seekBar.setId(index);
+    innerLayout1.addView(seekBar);
+
+    seekBar.setOnSeekBarChangeListener(this);
+    seekBar.setProgress(beType.getCurrentPos());
+
+    ///// INNER LAYOUT2 //////////////////////////////////////////////////////////////////
+
+    int innerLayout2Height = (int)(scale*layoH + 0.5f);
+    LinearLayout innerLayout2 = new LinearLayout(act);
+    LinearLayout.LayoutParams innerLayout2Params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,innerLayout2Height);
+
+    innerLayout2.setLayoutParams(innerLayout2Params);
+    innerLayout2.setGravity(Gravity.CENTER|Gravity.FILL_HORIZONTAL);
+    innerLayout2.setOrientation(LinearLayout.HORIZONTAL);
+    outerLayout.addView(innerLayout2);
+
+    ///// STUFF INSIDE INNER LAYOUT2 /////////////////////////////////////////////////////
+
+    int text3Padding = (int)(scale*5 + 0.5f);
+    LinearLayout.LayoutParams text3LayoutParams = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,0.4f);
+
+    TextView text3View = new TextView(act);
+    text3View.setText(R.string.type);
+    text3View.setLayoutParams(text3LayoutParams);
+    text3View.setGravity(Gravity.START|Gravity.CENTER);
+    text3View.setPadding(text3Padding,0,text3Padding,0);
+    text3View.setTextAppearance(android.R.style.TextAppearance_Small);
+    innerLayout2.addView(text3View);
+    //////////////////////////////////////////////////////////////////
+    int spinnerPadding = (int)(scale*10 + 0.5f);
+    LinearLayout.LayoutParams spinnerLayoutParams = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,0.6f);
+
+    Spinner spinner = new Spinner(act);
+    spinner.setLayoutParams(spinnerLayoutParams);
+    spinner.setPadding(spinnerPadding,0,spinnerPadding,0);
+    spinner.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
+    spinner.setId(index+BaseEffect.Type.LENGTH);
+    innerLayout2.addView(spinner);
+
+    spinner.setOnItemSelectedListener(this);
+    String[] appear = BaseEffect.Type.getType(index).getNames();
+
+    ArrayAdapter<String> adapterType = new ArrayAdapter<>(act,android.R.layout.simple_spinner_item, appear);
+    adapterType.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+    spinner.setAdapter(adapterType);
+    spinner.setSelection(beType.getCurrentType());
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public RubikDialogSettings()
+    {
+    mDurationText = new TextView[BaseEffect.Type.LENGTH];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public void onStart()
+    {
+    super.onStart();
+
+    Window window = getDialog().getWindow();
+    window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
+                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+    window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @NonNull
+  @Override
+  public Dialog onCreateDialog(Bundle savedInstanceState)
+    {
+    FragmentActivity act = getActivity();
+    LayoutInflater inflater = act.getLayoutInflater();
+    AlertDialog.Builder builder = new AlertDialog.Builder(act);
+    TextView tv = (TextView) inflater.inflate(R.layout.dialog_title, null);
+    tv.setText(R.string.settings);
+    builder.setCustomTitle(tv);
+
+    builder.setCancelable(true);
+    builder.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener()
+      {
+      @Override
+      public void onClick(DialogInterface dialog, int which)
+        {
+
+        }
+      });
+
+    final View view = inflater.inflate(R.layout.dialog_settings, null);
+    builder.setView(view);
+
+    LinearLayout linearLayout = view.findViewById(R.id.settingsLayout);
+
+    if( linearLayout!=null )
+      {
+      for (int i=0; i< BaseEffect.Type.LENGTH; i++)
+        {
+        addSettingsSection(act,linearLayout,i);
+        }
+      }
+    else
+      {
+      android.util.Log.e("dialog_settings", "linearLayout NULL!");
+      }
+
+    return builder.create();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
+    {
+    int parentID = parent.getId();
+    int len = BaseEffect.Type.LENGTH;
+
+    if( parentID>=len && parentID< 2*len) // ith spinner's ID is equal to i+LENGTH (see createSettingSection)
+      {
+      BaseEffect.Type.getType(parentID-len).setCurrentType(pos);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onProgressChanged(SeekBar bar, int progress, boolean fromUser)
+    {
+    int barID = bar.getId();
+
+    if( barID>=0 && barID< BaseEffect.Type.LENGTH) // ith seekbar's ID is equal to i (see createSettingSection)
+      {
+      BaseEffect.Type.getType(barID).setCurrentPos(progress);
+      int ms = BaseEffect.Type.translatePos(progress);
+      mDurationText[barID].setText(getString(R.string.ms_placeholder,ms));
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onNothingSelected(AdapterView<?> parent) { }
+  public void onStartTrackingTouch(SeekBar bar) { }
+  public void onStopTrackingTouch(SeekBar bar)  { }
+  }
diff --git a/src/main/java/org/distorted/magic/RubikActivity.java b/src/main/java/org/distorted/magic/RubikActivity.java
index 148a5c94..97643f19 100644
--- a/src/main/java/org/distorted/magic/RubikActivity.java
+++ b/src/main/java/org/distorted/magic/RubikActivity.java
@@ -25,17 +25,20 @@ import android.preference.PreferenceManager;
 import android.support.v7.app.AppCompatActivity;
 import android.view.View;
 
+import org.distorted.dialog.RubikDialogAbout;
+import org.distorted.dialog.RubikDialogScores;
+import org.distorted.dialog.RubikDialogSettings;
 import org.distorted.effect.BaseEffect;
 import org.distorted.library.main.DistortedLibrary;
 
+import org.distorted.uistate.RubikState;
+import org.distorted.uistate.RubikStateAbstract;
+import org.distorted.uistate.RubikStatePlay;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 public class RubikActivity extends AppCompatActivity implements View.OnClickListener
 {
-    RubikState mCurrentState;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
     @Override
     protected void onCreate(Bundle savedState)
       {
@@ -66,7 +69,7 @@ public class RubikActivity extends AppCompatActivity implements View.OnClickList
       RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
       view.onResume();
       restorePreferences();
-      view.switchState(this, mCurrentState);
+      RubikState.setState(this);
       }
     
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -94,24 +97,14 @@ public class RubikActivity extends AppCompatActivity implements View.OnClickList
 
         if( success )
           {
-          view.markButton(id);
+          RubikStatePlay play = (RubikStatePlay)RubikState.PLAY.getStateClass();
+          play.markButton(this,id);
           }
         }
 
-      if( id == RubikSurfaceView.BUTTON_ID_BACK )
+      if( id == RubikStateAbstract.BUTTON_ID_BACK )
         {
-        mCurrentState = mCurrentState.getBack();
-
-        if( mCurrentState!=null )
-          {
-          RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-          view.switchState(this, mCurrentState);
-          }
-        else
-          {
-          mCurrentState = RubikState.MAIN;
-          finish();
-          }
+        RubikState.goBack(this);
         }
       }
 
@@ -122,16 +115,17 @@ public class RubikActivity extends AppCompatActivity implements View.OnClickList
       SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
       SharedPreferences.Editor editor = preferences.edit();
 
-      for (int i = 0; i< BaseEffect.Type.LENGTH; i++)
+      for (int i=0; i<BaseEffect.Type.LENGTH; i++)
         {
         BaseEffect.Type.getType(i).savePreferences(editor);
         }
 
-      editor.putInt("state", mCurrentState.ordinal() );
-
-      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      view.savePreferences(editor);
+      for (int i=0; i<RubikState.LENGTH; i++)
+        {
+        RubikState.getState(i).getStateClass().savePreferences(editor);
+        }
 
+      RubikState.savePreferences(editor);
       editor.apply();
       }
 
@@ -146,11 +140,12 @@ public class RubikActivity extends AppCompatActivity implements View.OnClickList
         BaseEffect.Type.getType(i).restorePreferences(preferences);
         }
 
-      int stateOrdinal = preferences.getInt("state", RubikState.MAIN.ordinal() );
-      mCurrentState = RubikState.getState(stateOrdinal);
+      for (int i=0; i< RubikState.LENGTH; i++)
+        {
+        RubikState.getState(i).getStateClass().restorePreferences(preferences);
+        }
 
-      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      view.restorePreferences(preferences);
+      RubikState.restorePreferences(preferences);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -159,9 +154,7 @@ public class RubikActivity extends AppCompatActivity implements View.OnClickList
 
     public void Play(View v)
       {
-      mCurrentState = RubikState.PLAY;
-      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      view.switchState(this,mCurrentState);
+      RubikState.switchState(this,RubikState.PLAY);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -176,8 +169,11 @@ public class RubikActivity extends AppCompatActivity implements View.OnClickList
 
     public void Scores(View v)
       {
+      RubikStatePlay play = (RubikStatePlay) RubikState.PLAY.getStateClass();
+      int tab = play.getButton();
+
       Bundle bundle = new Bundle();
-      bundle.putInt("tab", RubikSurfaceView.getRedButton());
+      bundle.putInt("tab", tab);
 
       RubikDialogScores scores = new RubikDialogScores();
       scores.setArguments(bundle);
@@ -197,7 +193,9 @@ public class RubikActivity extends AppCompatActivity implements View.OnClickList
     public void Scramble(View v)
       {
       RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      view.scramble();
+      RubikStatePlay play = (RubikStatePlay)RubikState.PLAY.getStateClass();
+      int scramble = play.getPicker();
+      view.getRenderer().scrambleCube(scramble);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/magic/RubikDialogAbout.java b/src/main/java/org/distorted/magic/RubikDialogAbout.java
deleted file mode 100644
index 647b2d05..00000000
--- a/src/main/java/org/distorted/magic/RubikDialogAbout.java
+++ /dev/null
@@ -1,81 +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.magic;
-
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.FragmentActivity;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.app.AppCompatDialogFragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.TextView;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogAbout extends AppCompatDialogFragment
-  {
-  @Override
-  public void onStart()
-    {
-    super.onStart();
-
-    Window window = getDialog().getWindow();
-    window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
-                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
-    window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @NonNull
-  @Override
-  public Dialog onCreateDialog(Bundle savedInstanceState)
-    {
-    FragmentActivity act = getActivity();
-    LayoutInflater inflater = act.getLayoutInflater();
-    AlertDialog.Builder builder = new AlertDialog.Builder(act);
-
-    TextView tv = (TextView) inflater.inflate(R.layout.dialog_title, null);
-    tv.setText(R.string.about);
-    builder.setCustomTitle(tv);
-
-    builder.setCancelable(true);
-    builder.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener()
-      {
-      @Override
-      public void onClick(DialogInterface dialog, int which)
-        {
-
-        }
-      });
-
-    final View view = inflater.inflate(R.layout.dialog_about, null);
-    TextView text = view.findViewById(R.id.about_version);
-    text.setText(R.string.app_version);
-    builder.setView(view);
-
-    return builder.create();
-    }
-  }
diff --git a/src/main/java/org/distorted/magic/RubikDialogMain.java b/src/main/java/org/distorted/magic/RubikDialogMain.java
deleted file mode 100644
index 9dd4624b..00000000
--- a/src/main/java/org/distorted/magic/RubikDialogMain.java
+++ /dev/null
@@ -1,69 +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.magic;
-
-import android.app.Dialog;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.FragmentActivity;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.app.AppCompatDialogFragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogMain extends AppCompatDialogFragment
-  {
-  @Override
-  public void onStart()
-    {
-    super.onStart();
-
-    Window window = getDialog().getWindow();
-    window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
-                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
-    window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @NonNull
-  @Override
-  public Dialog onCreateDialog(Bundle savedInstanceState)
-    {
-    FragmentActivity act = getActivity();
-    AlertDialog.Builder builder = new AlertDialog.Builder(act);
-    LayoutInflater inflater = act.getLayoutInflater();
-    final View view = inflater.inflate(R.layout.dialog_main, null);
-    builder.setView(view);
-
-    return builder.create();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static String getDialogTag()
-    {
-    return "DialogMain";
-    }
-  }
\ No newline at end of file
diff --git a/src/main/java/org/distorted/magic/RubikDialogScores.java b/src/main/java/org/distorted/magic/RubikDialogScores.java
deleted file mode 100644
index 10e594cf..00000000
--- a/src/main/java/org/distorted/magic/RubikDialogScores.java
+++ /dev/null
@@ -1,114 +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.magic;
-
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.view.ViewPager;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.app.AppCompatDialogFragment;
-import android.support.design.widget.TabLayout;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogScores extends AppCompatDialogFragment
-  {
-  RubikDialogScoresPagerAdapter mPagerAdapter;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public void onStart()
-    {
-    super.onStart();
-
-    Window window = getDialog().getWindow();
-    window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
-                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
-    window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @NonNull
-  @Override
-  public Dialog onCreateDialog(Bundle savedInstanceState)
-    {
-    FragmentActivity act = getActivity();
-    AlertDialog.Builder builder = new AlertDialog.Builder(act);
-
-    LayoutInflater layoutInflater = act.getLayoutInflater();
-    TextView tv = (TextView) layoutInflater.inflate(R.layout.dialog_title, null);
-    tv.setText(R.string.scores);
-    builder.setCustomTitle(tv);
-
-    builder.setCancelable(true);
-    builder.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener()
-      {
-      @Override
-      public void onClick(DialogInterface dialog, int which)
-        {
-
-        }
-      });
-
-    Bundle args = getArguments();
-    int curTab;
-
-    try
-      {
-      curTab = args.getInt("tab");
-      }
-    catch(Exception e)
-      {
-      curTab = 0;
-      }
-
-    LayoutInflater inflater = act.getLayoutInflater();
-    final View view = inflater.inflate(R.layout.dialog_scores, null);
-    builder.setView(view);
-
-    ViewPager viewPager = view.findViewById(R.id.viewpager);
-    TabLayout tabLayout = view.findViewById(R.id.sliding_tabs);
-    mPagerAdapter = new RubikDialogScoresPagerAdapter(act,viewPager);
-    tabLayout.setupWithViewPager(viewPager);
-
-    viewPager.setCurrentItem(curTab);
-
-    for (int i = 0; i< RubikSize.LENGTH; i++)
-      {
-      ImageView imageView = new ImageView(act);
-      imageView.setImageResource(RubikSize.getSize(i).getIconID());
-      TabLayout.Tab tab = tabLayout.getTabAt(i);
-      if(tab!=null) tab.setCustomView(imageView);
-      }
-
-    return builder.create();
-    }
-  }
diff --git a/src/main/java/org/distorted/magic/RubikDialogScoresPagerAdapter.java b/src/main/java/org/distorted/magic/RubikDialogScoresPagerAdapter.java
deleted file mode 100644
index 0d344f5e..00000000
--- a/src/main/java/org/distorted/magic/RubikDialogScoresPagerAdapter.java
+++ /dev/null
@@ -1,192 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// 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.magic;
-
-import android.support.annotation.NonNull;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.view.PagerAdapter;
-import android.support.v4.view.ViewPager;
-import android.view.View;
-import android.view.ViewGroup;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class RubikDialogScoresPagerAdapter extends PagerAdapter implements RubikScoresDownloader.Receiver
-  {
-  private FragmentActivity mAct;
-  private RubikDialogScoresView[] mViews;
-  private ViewPager mViewPager;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void receive(final int[][][] country, final String[][][] name, final String[][][] time)
-    {
-    prepareView();
-
-    int c = mViewPager.getCurrentItem();
-
-    addPage(mViews[c],country[c],name[c],time[c]);
-
-    for(int i=0; i<RubikSize.LENGTH; i++)
-      {
-      if( i==c ) continue;
-
-      addPage(mViews[i],country[i],name[i],time[i]);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void prepareView()
-    {
-    mAct.runOnUiThread(new Runnable()
-      {
-      @Override
-      public void run()
-        {
-        for(int i=0; i<RubikSize.LENGTH; i++)
-          {
-          mViews[i].prepareView(mAct);
-          }
-        }
-      });
-    try
-      {
-      Thread.sleep(200);
-      }
-    catch( InterruptedException ie)
-      {
-
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void addPage(final RubikDialogScoresView view, final int[][] country, final String[][] name, final String[][] time)
-    {
-    for(int section=0; section<RubikSurfaceView.MAX_SCRAMBLE; section++)
-      {
-      final int sec = section;
-      final int[] c = country[section];
-      final String[] n = name[section];
-      final String[] t = time[section];
-
-      mAct.runOnUiThread(new Runnable()
-        {
-        @Override
-        public void run()
-          {
-          view.addSection(mAct, sec, c, n, t);
-          }
-        });
-
-      try
-        {
-        Thread.sleep(60);
-        }
-      catch( InterruptedException ie)
-        {
-
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void exception(final String exce)
-    {
-    mAct.runOnUiThread(new Runnable()
-      {
-      @Override
-      public void run()
-        {
-        for(int i=0; i<RubikSize.LENGTH; i++)
-          {
-          mViews[i].exception(exce);
-          }
-        }
-      });
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  RubikDialogScoresPagerAdapter(FragmentActivity act, ViewPager viewPager)
-    {
-    mAct = act;
-    mViews = new RubikDialogScoresView[RubikSize.LENGTH];
-    mViewPager = viewPager;
-
-    viewPager.setAdapter(this);
-    viewPager.setOffscreenPageLimit( RubikSize.LENGTH-1 );
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  @NonNull
-  public Object instantiateItem(@NonNull ViewGroup collection, int position)
-    {
-    mViews[position] = new RubikDialogScoresView(mAct);
-    collection.addView(mViews[position]);
-
-    boolean allCreated = true;
-
-    for(int i=0; i<RubikSize.LENGTH; i++)
-      {
-      if( mViews[i]==null )
-        {
-        allCreated = false;
-        break;
-        }
-      }
-
-    if( allCreated )
-      {
-      RubikScoresDownloader downloader = new RubikScoresDownloader();
-      downloader.download(this, mAct.getResources(), mAct.getPackageName());
-      }
-
-    return mViews[position];
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public void destroyItem(ViewGroup collection, int position, @NonNull Object view)
-    {
-    collection.removeView((View) view);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public int getCount()
-    {
-    return RubikSize.LENGTH;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public boolean isViewFromObject(@NonNull View view, @NonNull Object object)
-    {
-    return view == object;
-    }
-  }
diff --git a/src/main/java/org/distorted/magic/RubikDialogScoresView.java b/src/main/java/org/distorted/magic/RubikDialogScoresView.java
deleted file mode 100644
index a649357e..00000000
--- a/src/main/java/org/distorted/magic/RubikDialogScoresView.java
+++ /dev/null
@@ -1,108 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// 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.magic;
-
-import android.content.Context;
-import android.support.v4.app.FragmentActivity;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogScoresView extends FrameLayout
-  {
-  LinearLayout mLayout;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public RubikDialogScoresView(Context context, AttributeSet attrs, int defStyle)
-    {
-    super(context, attrs, defStyle);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public RubikDialogScoresView(Context context, AttributeSet attrs)
-    {
-    super(context, attrs);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public RubikDialogScoresView(Context context)
-    {
-    super(context);
-
-    View view = inflate(context, R.layout.dialog_scores_downloading, null);
-    addView(view);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void exception(final String exce)
-    {
-    TextView text = findViewById(R.id.downloading_text);
-    text.setText(exce);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void prepareView(FragmentActivity act)
-    {
-    removeAllViews();
-
-    View tab = inflate(act, R.layout.dialog_scores_tab, null);
-    mLayout = tab.findViewById(R.id.tabLayout);
-    addView(tab);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void addSection(FragmentActivity act, int scramble, final int[] country, final String[] name, final String[] 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)));
-
-    for(int j=0; j<RubikScoresDownloader.MAX_PLACES; j++)
-      {
-      if( name[j] != null )
-        {
-        View row = inflate(act, R.layout.dialog_scores_scramble_row, null);
-
-        ImageView imgCoun = row.findViewById(R.id.scoresScrambleRowCountry);
-        TextView textName = row.findViewById(R.id.scoresScrambleRowName);
-        TextView textTime = row.findViewById(R.id.scoresScrambleRowTime);
-
-        imgCoun.setImageResource(country[j]);
-        textName.setText(name[j]);
-        textTime.setText(time[j]);
-
-        level.addView(row);
-        }
-      }
-
-    mLayout.addView(level);
-    }
-  }
diff --git a/src/main/java/org/distorted/magic/RubikDialogSettings.java b/src/main/java/org/distorted/magic/RubikDialogSettings.java
deleted file mode 100644
index 1a199d77..00000000
--- a/src/main/java/org/distorted/magic/RubikDialogSettings.java
+++ /dev/null
@@ -1,281 +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.magic;
-
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.content.ContextCompat;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.app.AppCompatDialogFragment;
-import android.util.DisplayMetrics;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.LinearLayout;
-import android.widget.SeekBar;
-import android.widget.Spinner;
-import android.widget.TextView;
-
-import org.distorted.effect.BaseEffect;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogSettings extends AppCompatDialogFragment implements SeekBar.OnSeekBarChangeListener, AdapterView.OnItemSelectedListener
-  {
-  private TextView[] mDurationText;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void addSettingsSection(FragmentActivity act, LinearLayout layout, int index)
-    {
-    BaseEffect.Type beType = BaseEffect.Type.getType(index);
-    DisplayMetrics metrics = act.getResources().getDisplayMetrics();
-    float scale = metrics.density;
-
-    int textH=32;
-    int layoH=36;
-    int margH=10;
-
-    ///// OUTER LAYOUT ///////////////////////////////////////////////////////////////////
-
-    int margin = (int)(scale*margH + 0.5f);
-    int color  = ContextCompat.getColor(act, R.color.grey);
-    LinearLayout outerLayout = new LinearLayout(act);
-    LinearLayout.LayoutParams outerLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT, 0.5f);
-    outerLayoutParams.topMargin    = margin;
-    outerLayoutParams.bottomMargin = 0;
-    outerLayoutParams.leftMargin   = margin;
-    outerLayoutParams.rightMargin  = margin;
-
-    outerLayout.setLayoutParams(outerLayoutParams);
-    outerLayout.setGravity(Gravity.CENTER|Gravity.FILL_HORIZONTAL);
-    outerLayout.setBackgroundColor(color);
-    outerLayout.setOrientation(LinearLayout.VERTICAL);
-    layout.addView(outerLayout);
-
-    ///// TEXT ///////////////////////////////////////////////////////////////////////////
-
-    int layoutHeight = (int)(scale*textH + 0.5f);
-    int padding      = (int)(scale*10    + 0.5f);
-
-    LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,layoutHeight);
-
-    TextView textView = new TextView(act);
-    textView.setText(beType.getText());
-    textView.setLayoutParams(textParams);
-    textView.setGravity(Gravity.CENTER);
-    textView.setPadding(padding,0,padding,0);
-    textView.setTextAppearance(android.R.style.TextAppearance_Small);
-    outerLayout.addView(textView);
-
-    ///// INNER LAYOUT1 //////////////////////////////////////////////////////////////////
-
-    int innerLayout1Height = (int)(scale*layoH + 0.5f);
-    LinearLayout innerLayout1 = new LinearLayout(act);
-    LinearLayout.LayoutParams innerLayout1Params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,innerLayout1Height);
-
-    innerLayout1.setLayoutParams(innerLayout1Params);
-    innerLayout1.setGravity(Gravity.CENTER|Gravity.FILL_HORIZONTAL);
-    innerLayout1.setOrientation(LinearLayout.HORIZONTAL);
-    outerLayout.addView(innerLayout1);
-
-    ///// STUFF INSIDE INNER LAYOUT1 /////////////////////////////////////////////////////
-
-    int text1Padding = (int)(scale*5 + 0.5f);
-    LinearLayout.LayoutParams text1LayoutParams = new LinearLayout.LayoutParams(0,layoutHeight,0.2f);
-
-    TextView text1View = new TextView(act);
-    text1View.setText(R.string.duration);
-    text1View.setLayoutParams(text1LayoutParams);
-    text1View.setGravity(Gravity.START|Gravity.CENTER);
-    text1View.setPadding(text1Padding,0,text1Padding,0);
-    text1View.setTextAppearance(android.R.style.TextAppearance_Small);
-    innerLayout1.addView(text1View);
-    //////////////////////////////////////////////////////////////////
-    int text2Padding = (int)(scale*5 + 0.5f);
-    LinearLayout.LayoutParams text2LayoutParams = new LinearLayout.LayoutParams(0,layoutHeight,0.2f);
-
-    mDurationText[index] = new TextView(act);
-    mDurationText[index].setLayoutParams(text2LayoutParams);
-    mDurationText[index].setGravity(Gravity.END|Gravity.CENTER);
-    mDurationText[index].setPadding(text2Padding,0,text2Padding,0);
-    mDurationText[index].setTextAppearance(android.R.style.TextAppearance_Small);
-    innerLayout1.addView(mDurationText[index]);
-    //////////////////////////////////////////////////////////////////
-    int seekPadding = (int)(scale*10 + 0.5f);
-    LinearLayout.LayoutParams seekLayoutParams = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,0.6f);
-
-    SeekBar seekBar = new SeekBar(act);
-    seekBar.setLayoutParams(seekLayoutParams);
-    seekBar.setPadding(seekPadding,0,seekPadding,0);
-    seekBar.setId(index);
-    innerLayout1.addView(seekBar);
-
-    seekBar.setOnSeekBarChangeListener(this);
-    seekBar.setProgress(beType.getCurrentPos());
-
-    ///// INNER LAYOUT2 //////////////////////////////////////////////////////////////////
-
-    int innerLayout2Height = (int)(scale*layoH + 0.5f);
-    LinearLayout innerLayout2 = new LinearLayout(act);
-    LinearLayout.LayoutParams innerLayout2Params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,innerLayout2Height);
-
-    innerLayout2.setLayoutParams(innerLayout2Params);
-    innerLayout2.setGravity(Gravity.CENTER|Gravity.FILL_HORIZONTAL);
-    innerLayout2.setOrientation(LinearLayout.HORIZONTAL);
-    outerLayout.addView(innerLayout2);
-
-    ///// STUFF INSIDE INNER LAYOUT2 /////////////////////////////////////////////////////
-
-    int text3Padding = (int)(scale*5 + 0.5f);
-    LinearLayout.LayoutParams text3LayoutParams = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,0.4f);
-
-    TextView text3View = new TextView(act);
-    text3View.setText(R.string.type);
-    text3View.setLayoutParams(text3LayoutParams);
-    text3View.setGravity(Gravity.START|Gravity.CENTER);
-    text3View.setPadding(text3Padding,0,text3Padding,0);
-    text3View.setTextAppearance(android.R.style.TextAppearance_Small);
-    innerLayout2.addView(text3View);
-    //////////////////////////////////////////////////////////////////
-    int spinnerPadding = (int)(scale*10 + 0.5f);
-    LinearLayout.LayoutParams spinnerLayoutParams = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,0.6f);
-
-    Spinner spinner = new Spinner(act);
-    spinner.setLayoutParams(spinnerLayoutParams);
-    spinner.setPadding(spinnerPadding,0,spinnerPadding,0);
-    spinner.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
-    spinner.setId(index+BaseEffect.Type.LENGTH);
-    innerLayout2.addView(spinner);
-
-    spinner.setOnItemSelectedListener(this);
-    String[] appear = BaseEffect.Type.getType(index).getNames();
-
-    ArrayAdapter<String> adapterType = new ArrayAdapter<>(act,android.R.layout.simple_spinner_item, appear);
-    adapterType.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-    spinner.setAdapter(adapterType);
-    spinner.setSelection(beType.getCurrentType());
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public RubikDialogSettings()
-    {
-    mDurationText = new TextView[BaseEffect.Type.LENGTH];
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public void onStart()
-    {
-    super.onStart();
-
-    Window window = getDialog().getWindow();
-    window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
-                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
-    window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @NonNull
-  @Override
-  public Dialog onCreateDialog(Bundle savedInstanceState)
-    {
-    FragmentActivity act = getActivity();
-    LayoutInflater inflater = act.getLayoutInflater();
-    AlertDialog.Builder builder = new AlertDialog.Builder(act);
-    TextView tv = (TextView) inflater.inflate(R.layout.dialog_title, null);
-    tv.setText(R.string.settings);
-    builder.setCustomTitle(tv);
-
-    builder.setCancelable(true);
-    builder.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener()
-      {
-      @Override
-      public void onClick(DialogInterface dialog, int which)
-        {
-
-        }
-      });
-
-    final View view = inflater.inflate(R.layout.dialog_settings, null);
-    builder.setView(view);
-
-    LinearLayout linearLayout = view.findViewById(R.id.settingsLayout);
-
-    if( linearLayout!=null )
-      {
-      for (int i=0; i< BaseEffect.Type.LENGTH; i++)
-        {
-        addSettingsSection(act,linearLayout,i);
-        }
-      }
-    else
-      {
-      android.util.Log.e("dialog_settings", "linearLayout NULL!");
-      }
-
-    return builder.create();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
-    {
-    int parentID = parent.getId();
-    int len = BaseEffect.Type.LENGTH;
-
-    if( parentID>=len && parentID< 2*len) // ith spinner's ID is equal to i+LENGTH (see createSettingSection)
-      {
-      BaseEffect.Type.getType(parentID-len).setCurrentType(pos);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onProgressChanged(SeekBar bar, int progress, boolean fromUser)
-    {
-    int barID = bar.getId();
-
-    if( barID>=0 && barID< BaseEffect.Type.LENGTH) // ith seekbar's ID is equal to i (see createSettingSection)
-      {
-      BaseEffect.Type.getType(barID).setCurrentPos(progress);
-      int ms = BaseEffect.Type.translatePos(progress);
-      mDurationText[barID].setText(getString(R.string.ms_placeholder,ms));
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onNothingSelected(AdapterView<?> parent) { }
-  public void onStartTrackingTouch(SeekBar bar) { }
-  public void onStopTrackingTouch(SeekBar bar)  { }
-  }
diff --git a/src/main/java/org/distorted/magic/RubikRenderer.java b/src/main/java/org/distorted/magic/RubikRenderer.java
index 833ea422..900ce850 100644
--- a/src/main/java/org/distorted/magic/RubikRenderer.java
+++ b/src/main/java/org/distorted/magic/RubikRenderer.java
@@ -30,6 +30,8 @@ import org.distorted.library.main.DistortedTexture;
 import org.distorted.library.mesh.MeshFlat;
 import org.distorted.library.message.EffectListener;
 import org.distorted.object.RubikCube;
+import org.distorted.uistate.RubikState;
+import org.distorted.uistate.RubikStatePlay;
 
 import javax.microedition.khronos.egl.EGLConfig;
 import javax.microedition.khronos.opengles.GL10;
@@ -82,7 +84,10 @@ public class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
       mEffectID = new long[BaseEffect.Type.LENGTH];
 
       mMesh= new MeshFlat(20,20);
-      mNextCubeSize = RubikSize.getSize(RubikSurfaceView.getRedButton()).getCubeSize();
+
+      RubikStatePlay play = (RubikStatePlay) RubikState.PLAY.getStateClass();
+      int size = play.getButton();
+      mNextCubeSize = RubikSize.getSize(size).getCubeSize();
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/magic/RubikScoresDownloader.java b/src/main/java/org/distorted/magic/RubikScoresDownloader.java
index 0a167f04..f59ae37e 100644
--- a/src/main/java/org/distorted/magic/RubikScoresDownloader.java
+++ b/src/main/java/org/distorted/magic/RubikScoresDownloader.java
@@ -26,17 +26,19 @@ import java.net.HttpURLConnection;
 import java.net.URL;
 import java.net.UnknownHostException;
 
+import org.distorted.uistate.RubikStatePlay;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-class RubikScoresDownloader implements Runnable
+public class RubikScoresDownloader implements Runnable
   {
-  interface Receiver
+  public interface Receiver
     {
     void receive(int[][][] country, String[][][] name, String[][][] time);
     void exception(String exception);
     }
 
-  static final int MAX_PLACES = 12;
+  public static final int MAX_PLACES = 12;
 
   private static final int DOWNLOAD   = 0;
   private static final int SUBMIT     = 1;
@@ -86,9 +88,9 @@ class RubikScoresDownloader implements Runnable
   private static String mPackageName;
 
   private static String mScores = "";
-  private static int[][][] mCountry = new int   [RubikSize.LENGTH][RubikSurfaceView.MAX_SCRAMBLE][MAX_PLACES];
-  private static String[][][] mName = new String[RubikSize.LENGTH][RubikSurfaceView.MAX_SCRAMBLE][MAX_PLACES];
-  private static String[][][] mTime = new String[RubikSize.LENGTH][RubikSurfaceView.MAX_SCRAMBLE][MAX_PLACES];
+  private static int[][][] mCountry = new int   [RubikSize.LENGTH][RubikStatePlay.MAX_SCRAMBLE][MAX_PLACES];
+  private static String[][][] mName = new String[RubikSize.LENGTH][RubikStatePlay.MAX_SCRAMBLE][MAX_PLACES];
+  private static String[][][] mTime = new String[RubikSize.LENGTH][RubikStatePlay.MAX_SCRAMBLE][MAX_PLACES];
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -129,7 +131,7 @@ class RubikScoresDownloader implements Runnable
         String country = row.substring(s5+1, s6);
         String realTime= String.valueOf(time/10.0f);
 
-        if(level>=0 && level<RubikSurfaceView.MAX_SCRAMBLE && place>=0 && place<MAX_PLACES)
+        if(level>=0 && level<RubikStatePlay.MAX_SCRAMBLE && place>=0 && place<MAX_PLACES)
           {
           int resID = mResources.getIdentifier( country, "drawable", mPackageName);
           mCountry[size][level][place] = resID!=0 ? resID:R.drawable.unk;
@@ -250,7 +252,7 @@ class RubikScoresDownloader implements Runnable
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  void download(Receiver receiver, Resources resources, String packageName)
+  public void download(Receiver receiver, Resources resources, String packageName)
     {
     mReceiver = receiver;
     mResources= resources;
diff --git a/src/main/java/org/distorted/magic/RubikSize.java b/src/main/java/org/distorted/magic/RubikSize.java
index 7001c377..a374bfbd 100644
--- a/src/main/java/org/distorted/magic/RubikSize.java
+++ b/src/main/java/org/distorted/magic/RubikSize.java
@@ -29,7 +29,7 @@ public enum RubikSize
   SIZE5 ( 5, R.drawable.button5 ),
   ;
 
-  static final int LENGTH = values().length;
+  public static final int LENGTH = values().length;
   private final int mCubeSize, mIconID;
   private static final RubikSize[] sizes;
 
@@ -47,7 +47,7 @@ public enum RubikSize
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  static RubikSize getSize(int ordinal)
+  public static RubikSize getSize(int ordinal)
     {
     return sizes[ordinal];
     }
@@ -62,14 +62,14 @@ public enum RubikSize
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  int getIconID()
+  public int getIconID()
     {
     return mIconID;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  int getCubeSize()
+  public int getCubeSize()
     {
     return mCubeSize;
     }
diff --git a/src/main/java/org/distorted/magic/RubikState.java b/src/main/java/org/distorted/magic/RubikState.java
deleted file mode 100644
index b091dc09..00000000
--- a/src/main/java/org/distorted/magic/RubikState.java
+++ /dev/null
@@ -1,66 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// 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.magic;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public enum RubikState
-  {
-  MAIN ( null ),
-  PLAY ( MAIN ),
-  ;
-
-  static final int LENGTH = values().length;
-  private final RubikState mBack;
-  private static final RubikState[] sizes;
-
-  static
-    {
-    int i = 0;
-    sizes = new RubikState[LENGTH];
-
-    for(RubikState size: RubikState.values())
-      {
-      sizes[i] = size;
-      i++;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static RubikState getState(int ordinal)
-    {
-    return sizes[ordinal];
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  RubikState(RubikState back)
-    {
-    mBack = back;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  RubikState getBack()
-    {
-    return mBack;
-    }
-  }
\ No newline at end of file
diff --git a/src/main/java/org/distorted/magic/RubikSurfaceView.java b/src/main/java/org/distorted/magic/RubikSurfaceView.java
index 2751d269..3c91af8f 100644
--- a/src/main/java/org/distorted/magic/RubikSurfaceView.java
+++ b/src/main/java/org/distorted/magic/RubikSurfaceView.java
@@ -21,24 +21,11 @@ package org.distorted.magic;
 
 import android.app.ActivityManager;
 import android.content.Context;
-import android.content.SharedPreferences;
 import android.content.pm.ConfigurationInfo;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
 import android.opengl.GLSurfaceView;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.content.ContextCompat;
 import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.view.LayoutInflater;
 import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
 
-import org.distorted.component.HorizontalNumberPicker;
 import org.distorted.library.type.Static2D;
 import org.distorted.library.type.Static4D;
 import org.distorted.object.RubikCube;
@@ -48,12 +35,6 @@ import org.distorted.object.RubikCubeMovement;
 
 public class RubikSurfaceView extends GLSurfaceView
 {
-    public static final int BUTTON_ID_BACK= 1023;
-
-    public static final int MIN_SCRAMBLE =  1;
-    public static final int DEF_SCRAMBLE =  1;
-    public static final int MAX_SCRAMBLE = 18;
-
     // Moving the finger from the middle of the vertical screen to the right edge will rotate a
     // given face by SWIPING_SENSITIVITY/2 degrees.
     private final static int SWIPING_SENSITIVITY  = 240;
@@ -70,41 +51,11 @@ public class RubikSurfaceView extends GLSurfaceView
     private float mX, mY;
     private int mScreenWidth, mScreenHeight, mScreenMin;
 
-    private HorizontalNumberPicker mPicker;
-    private int mPickerValue;
-    private RubikState mCurrentState;
-
-    private static int mButton = RubikSize.SIZE3.ordinal();
     private static Static4D mQuatCurrent    = new Static4D(0,0,0,1);
     private static Static4D mQuatAccumulated= new Static4D(-0.25189602f,0.3546389f,0.009657208f,0.90038127f);
     private static Static4D mTempCurrent    = new Static4D(0,0,0,1);
     private static Static4D mTempAccumulated= new Static4D(0,0,0,1);
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    void scramble()
-      {
-      int scramble = mPicker.getValue();
-      mRenderer.scrambleCube(scramble);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    void savePreferences(SharedPreferences.Editor editor)
-      {
-      if( mPicker!=null )
-        {
-        editor.putInt("scramble", mPicker.getValue() );
-        }
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    void restorePreferences(SharedPreferences preferences)
-      {
-      mPickerValue= preferences.getInt("scramble", DEF_SCRAMBLE);
-      }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
     void setScreenSize(int width, int height)
@@ -122,13 +73,6 @@ public class RubikSurfaceView extends GLSurfaceView
       return mRenderer;
       }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    static int getRedButton()
-      {
-      return mButton;
-      }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
     void setQuatAccumulated()
@@ -239,188 +183,6 @@ public class RubikSurfaceView extends GLSurfaceView
       return quatMultiply(tmp,quat);
       }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    void markButton(int button)
-      {
-      mButton = button;
-      RubikActivity act = (RubikActivity)getContext();
-
-      for(int b=0; b<RubikSize.LENGTH; b++)
-        {
-        Drawable d = act.findViewById(b).getBackground();
-
-        if( b==button )
-          {
-          d.setColorFilter(ContextCompat.getColor(act,R.color.red), PorterDuff.Mode.MULTIPLY);
-          }
-        else
-          {
-          d.clearColorFilter();
-          }
-        }
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    void switchState(RubikActivity act, RubikState state)
-      {
-      if( mCurrentState!=state )
-        {
-        switch(state)
-          {
-          case MAIN: enterMainState(act); break;
-          case PLAY: enterPlayState(act); break;
-          }
-
-        if( mCurrentState!=null )
-          {
-          switch(mCurrentState)
-            {
-            case MAIN: leaveMainState(act); break;
-            case PLAY: leavePlayState(act); break;
-            }
-          }
-
-        mCurrentState = state;
-        }
-      else
-        {
-        android.util.Log.e("act", "trying to change into same state "+state);
-        }
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private void leaveMainState(RubikActivity act)
-      {
-      FragmentManager mana = act.getSupportFragmentManager();
-      RubikDialogMain diag = (RubikDialogMain) mana.findFragmentByTag(RubikDialogMain.getDialogTag());
-
-      if( diag!=null )
-        {
-        diag.dismiss();
-        }
-      else
-        {
-        android.util.Log.e("act", "cannot find main dialog!");
-        }
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private void leavePlayState(RubikActivity act)
-      {
-      mPickerValue = mPicker.getValue();
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private void enterMainState(RubikActivity act)
-      {
-      FragmentManager mana = act.getSupportFragmentManager();
-      RubikDialogMain diag = (RubikDialogMain) mana.findFragmentByTag(RubikDialogMain.getDialogTag());
-
-      if( diag==null )
-        {
-        RubikDialogMain diag2 = new RubikDialogMain();
-        diag2.show( mana, RubikDialogMain.getDialogTag() );
-        }
-
-      LayoutInflater inflater = act.getLayoutInflater();
-
-      // TOP ////////////////////////////
-      LinearLayout layoutTop = act.findViewById(R.id.mainTitle);
-      layoutTop.removeAllViews();
-      final View viewTop = inflater.inflate(R.layout.main_title, null);
-      layoutTop.addView(viewTop);
-
-      // BOT ////////////////////////////
-      LinearLayout layoutBot = act.findViewById(R.id.mainBar);
-      layoutBot.removeAllViews();
-
-      DisplayMetrics metrics = getResources().getDisplayMetrics();
-      float scale = metrics.density;
-      int size = (int)(60*scale +0.5f);
-      int padding = (int)(5*scale + 0.5f);
-      LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,size,0.5f);
-
-      Button buttonL = new Button(act);
-      buttonL.setLayoutParams(params);
-      buttonL.setId(BUTTON_ID_BACK);
-      buttonL.setPadding(padding,0,padding,0);
-      buttonL.setText(R.string.back);
-      buttonL.setOnClickListener(act);
-      layoutBot.addView(buttonL);
-
-      Button buttonR = new Button(act);
-      buttonR.setLayoutParams(params);
-      buttonR.setId(BUTTON_ID_BACK);
-      buttonR.setPadding(padding,0,padding,0);
-      buttonR.setText(R.string.exit);
-      buttonR.setOnClickListener(act);
-      layoutBot.addView(buttonR);
-
-      buttonL.setVisibility(INVISIBLE);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private void enterPlayState(RubikActivity act)
-      {
-      LayoutInflater inflater = act.getLayoutInflater();
-
-      // TOP ////////////////////////////
-      LinearLayout layoutTop = act.findViewById(R.id.mainTitle);
-      layoutTop.removeAllViews();
-
-      final View viewTop = inflater.inflate(R.layout.play_title, null);
-      layoutTop.addView(viewTop);
-
-      // BOT ////////////////////////////
-      LinearLayout layoutBot = act.findViewById(R.id.mainBar);
-      layoutBot.removeAllViews();
-
-      DisplayMetrics metrics = getResources().getDisplayMetrics();
-      float scale = metrics.density;
-      int size = (int)(60*scale +0.5f);
-      int padding = (int)(3*scale + 0.5f);
-      ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(size,size);
-
-      for(int i=0; i<RubikSize.LENGTH; i++)
-        {
-        ImageButton button = new ImageButton(act);
-        button.setLayoutParams(params);
-        button.setId(i);
-        button.setPadding(padding,0,padding,0);
-        int iconID = RubikSize.getSize(i).getIconID();
-        button.setImageResource(iconID);
-        button.setOnClickListener(act);
-        layoutBot.addView(button);
-        }
-
-      ViewGroup.LayoutParams params2 = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,size);
-
-      Button button = new Button(act);
-      button.setLayoutParams(params2);
-      button.setId(BUTTON_ID_BACK);
-      button.setPadding(padding,0,padding,0);
-      button.setText(R.string.back);
-      button.setOnClickListener(act);
-      layoutBot.addView(button);
-
-      markButton(mButton);
-
-      mPicker = act.findViewById(R.id.rubikNumberPicker);
-
-      if( mPicker!=null )
-        {
-        mPicker.setMin(MIN_SCRAMBLE);
-        mPicker.setMax(MAX_SCRAMBLE);
-        mPicker.setValue(mPickerValue);
-        }
-      }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -434,8 +196,6 @@ public class RubikSurfaceView extends GLSurfaceView
         mRenderer = new RubikRenderer(this);
         mMovement = new RubikCubeMovement();
 
-        mCurrentState = null;
-
         final ActivityManager activityManager     = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
         final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
         setEGLContextClientVersion( (configurationInfo.reqGlEsVersion>>16) >= 3 ? 3:2 );
diff --git a/src/main/java/org/distorted/uistate/RubikState.java b/src/main/java/org/distorted/uistate/RubikState.java
new file mode 100644
index 00000000..21d96191
--- /dev/null
+++ b/src/main/java/org/distorted/uistate/RubikState.java
@@ -0,0 +1,139 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.uistate;
+
+import android.content.SharedPreferences;
+import org.distorted.magic.RubikActivity;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public enum RubikState
+  {
+  MAIN ( null , new RubikStateMain() ),
+  PLAY ( MAIN , new RubikStatePlay() ),
+  ;
+
+  public static final int LENGTH = values().length;
+  private final RubikState mBack;
+  private final RubikStateAbstract mClass;
+  private static final RubikState[] sizes;
+
+  private static RubikState mCurrentState;
+
+  static
+    {
+    int i = 0;
+    sizes = new RubikState[LENGTH];
+
+    for(RubikState size: RubikState.values())
+      {
+      sizes[i] = size;
+      i++;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static RubikState getState(int ordinal)
+    {
+    return sizes[ordinal];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static void savePreferences(SharedPreferences.Editor editor)
+    {
+    editor.putInt("state", mCurrentState.ordinal() );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static void restorePreferences(SharedPreferences preferences)
+    {
+    int stateOrdinal = preferences.getInt("state", RubikState.MAIN.ordinal() );
+    mCurrentState = getState(stateOrdinal);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static void goBack(RubikActivity act)
+    {
+    switchState(act, mCurrentState.getBack() );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static void setState(RubikActivity act)
+    {
+    mCurrentState.enterState(act);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static void switchState(RubikActivity act, RubikState state)
+    {
+    if( state!=null )
+      {
+      if( mCurrentState!=null ) mCurrentState.leaveState(act);
+      state.enterState(act);
+      mCurrentState = state;
+      }
+    else
+      {
+      act.finish();
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  RubikState(RubikState back, RubikStateAbstract clazz)
+    {
+    mBack = back;
+    mClass = clazz;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public RubikState getBack()
+    {
+    return mBack;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public RubikStateAbstract getStateClass()
+    {
+    return mClass;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void leaveState(RubikActivity act)
+    {
+    mClass.leaveState(act);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void enterState(RubikActivity act)
+    {
+    mClass.enterState(act);
+    }
+  }
\ No newline at end of file
diff --git a/src/main/java/org/distorted/uistate/RubikStateAbstract.java b/src/main/java/org/distorted/uistate/RubikStateAbstract.java
new file mode 100644
index 00000000..8e943c70
--- /dev/null
+++ b/src/main/java/org/distorted/uistate/RubikStateAbstract.java
@@ -0,0 +1,35 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.uistate;
+
+import android.content.SharedPreferences;
+import org.distorted.magic.RubikActivity;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public abstract class RubikStateAbstract
+  {
+  public static final int BUTTON_ID_BACK= 1023;
+
+  public abstract void enterState(RubikActivity act);
+  public abstract void leaveState(RubikActivity act);
+  public abstract void savePreferences(SharedPreferences.Editor editor);
+  public abstract void restorePreferences(SharedPreferences preferences);
+  }
diff --git a/src/main/java/org/distorted/uistate/RubikStateMain.java b/src/main/java/org/distorted/uistate/RubikStateMain.java
new file mode 100644
index 00000000..67240914
--- /dev/null
+++ b/src/main/java/org/distorted/uistate/RubikStateMain.java
@@ -0,0 +1,118 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.uistate;
+
+import android.content.SharedPreferences;
+import android.support.v4.app.FragmentManager;
+import android.util.DisplayMetrics;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import org.distorted.dialog.RubikDialogMain;
+import org.distorted.magic.R;
+import org.distorted.magic.RubikActivity;
+
+import static android.view.View.INVISIBLE;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class RubikStateMain extends RubikStateAbstract
+  {
+  public void leaveState(RubikActivity act)
+    {
+    FragmentManager mana = act.getSupportFragmentManager();
+    RubikDialogMain diag = (RubikDialogMain) mana.findFragmentByTag(RubikDialogMain.getDialogTag());
+
+    if( diag!=null )
+      {
+      diag.dismiss();
+      }
+    else
+      {
+      android.util.Log.e("act", "cannot find main dialog!");
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void enterState(RubikActivity act)
+    {
+    FragmentManager mana = act.getSupportFragmentManager();
+    RubikDialogMain diag = (RubikDialogMain) mana.findFragmentByTag(RubikDialogMain.getDialogTag());
+
+    if( diag==null )
+      {
+      RubikDialogMain diag2 = new RubikDialogMain();
+      diag2.show( mana, RubikDialogMain.getDialogTag() );
+      }
+
+    LayoutInflater inflater = act.getLayoutInflater();
+
+    // TOP ////////////////////////////
+    LinearLayout layoutTop = act.findViewById(R.id.mainTitle);
+    layoutTop.removeAllViews();
+    final View viewTop = inflater.inflate(R.layout.main_title, null);
+    layoutTop.addView(viewTop);
+
+    // BOT ////////////////////////////
+    LinearLayout layoutBot = act.findViewById(R.id.mainBar);
+    layoutBot.removeAllViews();
+
+    DisplayMetrics metrics = act.getResources().getDisplayMetrics();
+    float scale = metrics.density;
+    int size = (int)(60*scale +0.5f);
+    int padding = (int)(5*scale + 0.5f);
+    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,size,0.5f);
+
+    Button buttonL = new Button(act);
+    buttonL.setLayoutParams(params);
+    buttonL.setId(BUTTON_ID_BACK);
+    buttonL.setPadding(padding,0,padding,0);
+    buttonL.setText(R.string.back);
+    buttonL.setOnClickListener(act);
+    layoutBot.addView(buttonL);
+
+    Button buttonR = new Button(act);
+    buttonR.setLayoutParams(params);
+    buttonR.setId(BUTTON_ID_BACK);
+    buttonR.setPadding(padding,0,padding,0);
+    buttonR.setText(R.string.exit);
+    buttonR.setOnClickListener(act);
+    layoutBot.addView(buttonR);
+
+    buttonL.setVisibility(INVISIBLE);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void savePreferences(SharedPreferences.Editor editor)
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void restorePreferences(SharedPreferences preferences)
+    {
+
+    }
+  }
diff --git a/src/main/java/org/distorted/uistate/RubikStatePlay.java b/src/main/java/org/distorted/uistate/RubikStatePlay.java
new file mode 100644
index 00000000..273af1cd
--- /dev/null
+++ b/src/main/java/org/distorted/uistate/RubikStatePlay.java
@@ -0,0 +1,166 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.uistate;
+
+import android.content.SharedPreferences;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.support.v4.content.ContextCompat;
+import android.util.DisplayMetrics;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+import org.distorted.component.HorizontalNumberPicker;
+import org.distorted.magic.R;
+import org.distorted.magic.RubikActivity;
+import org.distorted.magic.RubikSize;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class RubikStatePlay extends RubikStateAbstract
+  {
+  private static final int MIN_SCRAMBLE =  1;
+  private static final int DEF_SCRAMBLE =  1;
+  public  static final int MAX_SCRAMBLE = 18;
+
+  private HorizontalNumberPicker mPicker;
+  private int mPickerValue;
+  private int mButton = RubikSize.SIZE3.ordinal();
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void leaveState(RubikActivity act)
+    {
+    mPickerValue = mPicker.getValue();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void enterState(RubikActivity act)
+    {
+    LayoutInflater inflater = act.getLayoutInflater();
+
+    // TOP ////////////////////////////
+    LinearLayout layoutTop = act.findViewById(R.id.mainTitle);
+    layoutTop.removeAllViews();
+
+    final View viewTop = inflater.inflate(R.layout.play_title, null);
+    layoutTop.addView(viewTop);
+
+    // BOT ////////////////////////////
+    LinearLayout layoutBot = act.findViewById(R.id.mainBar);
+    layoutBot.removeAllViews();
+
+    DisplayMetrics metrics = act.getResources().getDisplayMetrics();
+    float scale = metrics.density;
+    int size = (int)(60*scale +0.5f);
+    int padding = (int)(3*scale + 0.5f);
+    ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(size,size);
+
+    for(int i = 0; i< RubikSize.LENGTH; i++)
+      {
+      ImageButton button = new ImageButton(act);
+      button.setLayoutParams(params);
+      button.setId(i);
+      button.setPadding(padding,0,padding,0);
+      int iconID = RubikSize.getSize(i).getIconID();
+      button.setImageResource(iconID);
+      button.setOnClickListener(act);
+      layoutBot.addView(button);
+      }
+
+    ViewGroup.LayoutParams params2 = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,size);
+
+    Button button = new Button(act);
+    button.setLayoutParams(params2);
+    button.setId(BUTTON_ID_BACK);
+    button.setPadding(padding,0,padding,0);
+    button.setText(R.string.back);
+    button.setOnClickListener(act);
+    layoutBot.addView(button);
+
+    markButton(act,mButton);
+
+    mPicker = act.findViewById(R.id.rubikNumberPicker);
+
+    if( mPicker!=null )
+      {
+      mPicker.setMin(MIN_SCRAMBLE);
+      mPicker.setMax(MAX_SCRAMBLE);
+      mPicker.setValue(mPickerValue);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void savePreferences(SharedPreferences.Editor editor)
+    {
+    if( mPicker!=null )
+      {
+      editor.putInt("scramble", mPicker.getValue() );
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void restorePreferences(SharedPreferences preferences)
+    {
+    mPickerValue= preferences.getInt("scramble", DEF_SCRAMBLE);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getPicker()
+    {
+    return mPicker!=null ? mPicker.getValue() : 1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getButton()
+    {
+    return mButton;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void markButton(RubikActivity act, int button)
+    {
+    mButton = button;
+
+    for(int b=0; b<RubikSize.LENGTH; b++)
+      {
+      Drawable d = act.findViewById(b).getBackground();
+
+      if( b==button )
+        {
+        d.setColorFilter(ContextCompat.getColor(act,R.color.red), PorterDuff.Mode.MULTIPLY);
+        }
+      else
+        {
+        d.clearColorFilter();
+        }
+      }
+    }
+  }
