commit 8477cf44b79bdaf418a2ca63f011fe70cd3e42b2
Author: leszek <leszek@koltunski.pl>
Date:   Mon Dec 30 11:07:41 2024 +0100

    rename dialogs

diff --git a/src/main/java/org/distorted/bandaged/BandagedActivity.java b/src/main/java/org/distorted/bandaged/BandagedActivity.java
index 7095d8c0..cfeb8bcf 100644
--- a/src/main/java/org/distorted/bandaged/BandagedActivity.java
+++ b/src/main/java/org/distorted/bandaged/BandagedActivity.java
@@ -21,8 +21,8 @@ import android.os.Bundle;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
-import org.distorted.dialogs.RubikDialogError;
-import org.distorted.dialogs.RubikDialogMessage;
+import org.distorted.dialogs.DialogError;
+import org.distorted.dialogs.DialogMessage;
 import org.distorted.external.RubikFiles;
 import org.distorted.helpers.BaseActivity;
 import org.distorted.library.main.DistortedLibrary;
@@ -156,7 +156,7 @@ public class BandagedActivity extends BaseActivity
 
     void OpenGLError()
       {
-      RubikDialogError errDiag = new RubikDialogError();
+      DialogError errDiag = new DialogError();
       errDiag.show(getSupportFragmentManager(), null);
       }
 
@@ -184,9 +184,9 @@ public class BandagedActivity extends BaseActivity
         {
         Bundle bundle = new Bundle();
         bundle.putString("argument", getString(R.string.bandage_message) );
-        RubikDialogMessage diag = new RubikDialogMessage();
+        DialogMessage diag = new DialogMessage();
         diag.setArguments(bundle);
-        diag.show( getSupportFragmentManager(), RubikDialogMessage.getDialogTag() );
+        diag.show( getSupportFragmentManager(), DialogMessage.getDialogTag());
         }
       }
 
diff --git a/src/main/java/org/distorted/bandaged/BandagedObjectView.java b/src/main/java/org/distorted/bandaged/BandagedObjectView.java
index 5888a7af..bfc82bde 100644
--- a/src/main/java/org/distorted/bandaged/BandagedObjectView.java
+++ b/src/main/java/org/distorted/bandaged/BandagedObjectView.java
@@ -14,7 +14,7 @@ import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.LinearLayout;
 
-import org.distorted.dialogs.RubikDialogBandagedDelete;
+import org.distorted.dialogs.DialogBandagedDelete;
 import org.distorted.helpers.TransparentButton;
 import org.distorted.helpers.TransparentImageButton;
 import org.distorted.main.R;
@@ -71,9 +71,9 @@ public class BandagedObjectView
         {
         Bundle bundle = new Bundle();
         bundle.putString("argument", mName );
-        RubikDialogBandagedDelete dialog = new RubikDialogBandagedDelete();
+        DialogBandagedDelete dialog = new DialogBandagedDelete();
         dialog.setArguments(bundle);
-        dialog.show( act.getSupportFragmentManager(), RubikDialogBandagedDelete.getDialogTag() );
+        dialog.show( act.getSupportFragmentManager(), DialogBandagedDelete.getDialogTag());
         }
       });
     }
diff --git a/src/main/java/org/distorted/bandaged/BandagedRenderer.java b/src/main/java/org/distorted/bandaged/BandagedRenderer.java
index 818d6805..dae7306e 100644
--- a/src/main/java/org/distorted/bandaged/BandagedRenderer.java
+++ b/src/main/java/org/distorted/bandaged/BandagedRenderer.java
@@ -45,7 +45,7 @@ import org.distorted.objectlib.bandaged.LocallyBandagedList;
 import org.distorted.objectlib.json.JsonWriter;
 import org.distorted.objectlib.main.ObjectControl;
 import org.distorted.objectlib.main.TwistyObject;
-import org.distorted.dialogs.RubikDialogBandagedSave;
+import org.distorted.dialogs.DialogBandagedSave;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -456,7 +456,7 @@ public class BandagedRenderer implements GLSurfaceView.Renderer, DistortedLibrar
    public void displaySavingDialog()
      {
      BandagedActivity act = (BandagedActivity)mView.getContext();
-     RubikDialogBandagedSave saveDiag = new RubikDialogBandagedSave();
+     DialogBandagedSave saveDiag = new DialogBandagedSave();
      saveDiag.show(act.getSupportFragmentManager(), null);
      }
 
diff --git a/src/main/java/org/distorted/bandaged/BandagedScreen.java b/src/main/java/org/distorted/bandaged/BandagedScreen.java
index 966545f2..05cdb79b 100644
--- a/src/main/java/org/distorted/bandaged/BandagedScreen.java
+++ b/src/main/java/org/distorted/bandaged/BandagedScreen.java
@@ -237,7 +237,7 @@ public class BandagedScreen implements AdapterView.OnItemSelectedListener
 
   private void addObjects(BandagedActivity act, String objectString)
     {
-    if( objectString.length()>0 )
+    if( !objectString.isEmpty() )
       {
       String[] objects = objectString.split(" ");
       RubikFiles files = RubikFiles.getInstance();
diff --git a/src/main/java/org/distorted/config/ConfigActivity.java b/src/main/java/org/distorted/config/ConfigActivity.java
index 75be066b..19190dd1 100644
--- a/src/main/java/org/distorted/config/ConfigActivity.java
+++ b/src/main/java/org/distorted/config/ConfigActivity.java
@@ -12,8 +12,8 @@ package org.distorted.config;
 import android.os.Bundle;
 import android.content.SharedPreferences;
 
-import org.distorted.dialogs.RubikDialogError;
-import org.distorted.dialogs.RubikDialogMessage;
+import org.distorted.dialogs.DialogError;
+import org.distorted.dialogs.DialogMessage;
 import org.distorted.helpers.BaseActivity;
 import org.distorted.library.main.DistortedLibrary;
 import org.distorted.main.R;
@@ -130,7 +130,7 @@ public class ConfigActivity extends BaseActivity
 
     void OpenGLError()
       {
-      RubikDialogError errDiag = new RubikDialogError();
+      DialogError errDiag = new DialogError();
       errDiag.show(getSupportFragmentManager(), null);
       }
 
@@ -178,9 +178,9 @@ public class ConfigActivity extends BaseActivity
         {
         Bundle bundle = new Bundle();
         bundle.putString("argument", getString(R.string.config_message) );
-        RubikDialogMessage diag = new RubikDialogMessage();
+        DialogMessage diag = new DialogMessage();
         diag.setArguments(bundle);
-        diag.show( getSupportFragmentManager(), RubikDialogMessage.getDialogTag() );
+        diag.show( getSupportFragmentManager(), DialogMessage.getDialogTag());
         }
       }
 
diff --git a/src/main/java/org/distorted/dialogs/DialogAbandon.java b/src/main/java/org/distorted/dialogs/DialogAbandon.java
new file mode 100644
index 00000000..2e1e1380
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogAbandon.java
@@ -0,0 +1,48 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.app.Dialog;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.fragment.app.FragmentActivity;
+
+import org.distorted.main.R;
+import org.distorted.playui.PlayActivity;
+import org.distorted.playui.ScreenList;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogAbandon extends DialogAbstract
+  {
+  public int getResource()      { return R.layout.dialog_message; }
+  public int getTitleResource() { return -1; }
+  public boolean hasArgument()  { return false; }
+  public int getPositive()      { return R.string.yes; }
+  public int getNegative()      { return R.string.no; }
+  public void negativeAction()  { }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void positiveAction()
+    {
+    final PlayActivity act = (PlayActivity)getContext();
+    ScreenList.goBack(act);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
+    {
+    TextView mess = view.findViewById(R.id.dialog_message);
+    mess.setText(R.string.abandon_solve);
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogAbout.java b/src/main/java/org/distorted/dialogs/DialogAbout.java
new file mode 100644
index 00000000..97f9924b
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogAbout.java
@@ -0,0 +1,192 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2022 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.fragment.app.FragmentActivity;
+
+import org.distorted.main.BuildConfig;
+import org.distorted.main.R;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogAbout extends DialogAbstract
+  {
+  private static final String WHATS_NEW =
+      "1. Fix for 4x4 Penrose - now it correctly detects all solved states.\n" +
+      "2. Sorry for lack of activity lately - I've been busy with private stuff. Now I'm resuming development! \n\nNext: algorithmic solvers";
+
+  private static final String WHATS_COMING =
+      "1. Algorithmic solvers. (sub-optimal solvers for larger puzzles such as the 4x4)\n" +
+      "2. Support for sticker modes (Tartan Cube, Shepherd's Cube, etc).\n" +
+      "3. iOS version (no time for this, anyone can help? Code is open-source)\n" +
+      "4. More objects:\n    - Ghost Cubes\n    - more Mixups\n    - more Barrels\n";
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public void onResume()
+    {
+    super.onResume();
+
+    Window window = getDialog().getWindow();
+
+    if( window!=null )
+      {
+      WindowManager.LayoutParams params = window.getAttributes();
+      params.width  = (int)Math.min( mHeight*0.60f,mWidth*0.90f );
+      window.setAttributes(params);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getResource()      { return R.layout.dialog_about; }
+  public int getTitleResource() { return PARAMETRIC_TITLE; }
+  public boolean hasArgument()  { return true; }
+  public int getPositive()      { return R.string.ok; }
+  public int getNegative()      { return -1; }
+  public void positiveAction()  { }
+  public void negativeAction()  { }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private String findCurrentVersion(Activity act)
+    {
+    String version;
+    try
+      {
+      PackageInfo pInfo = act.getPackageManager().getPackageInfo( act.getPackageName(), 0);
+      version= pInfo.versionName;
+      }
+    catch (PackageManager.NameNotFoundException e)
+      {
+      version= "unknown";
+      }
+
+    return version;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  String getTitleString(FragmentActivity fact)
+    {
+    Resources res= getResources();
+    Activity act= (Activity) getContext();
+    String version= act!=null ? findCurrentVersion(act) : "unknown";
+    return res.getString(R.string.ab_placeholder,version);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
+    {
+    int width = (int)Math.min( mHeight*0.60f,mWidth*0.90f );
+    int sS= (int)(width*0.043f);
+    int bS= (int)(width*0.060f);
+
+    TextView shaV = view.findViewById(R.id.about_share_string);
+    TextView emaV = view.findViewById(R.id.about_mail_string);
+    TextView addV = view.findViewById(R.id.about_mail_address);
+
+    TextView newV = view.findViewById(R.id.about_new_message);
+    newV.setText(WHATS_NEW);
+    TextView comV = view.findViewById(R.id.about_coming_message);
+    comV.setText(WHATS_COMING);
+
+    LinearLayout layoutShare = view.findViewById(R.id.about_share_layout);
+
+    layoutShare.setOnClickListener(new View.OnClickListener()
+       {
+       @Override
+       public void onClick(View v)
+         {
+         share(act);
+         }
+       });
+
+    LinearLayout layoutEmail = view.findViewById(R.id.about_email_layout);
+
+    layoutEmail.setOnClickListener(new View.OnClickListener()
+       {
+       @Override
+       public void onClick(View v)
+         {
+         email(act);
+         }
+       });
+
+    shaV.setTextSize(TypedValue.COMPLEX_UNIT_PX, bS);
+    emaV.setTextSize(TypedValue.COMPLEX_UNIT_PX, bS);
+    addV.setTextSize(TypedValue.COMPLEX_UNIT_PX, sS);
+    newV.setTextSize(TypedValue.COMPLEX_UNIT_PX, sS);
+    comV.setTextSize(TypedValue.COMPLEX_UNIT_PX, sS);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void share(FragmentActivity act)
+    {
+    Resources res = act.getResources();
+    String name = res.getString(R.string.app_name);
+
+    Intent intent = new Intent();
+    intent.setAction(Intent.ACTION_SEND);
+    intent.putExtra(Intent.EXTRA_TEXT,
+    name+": https://play.google.com/store/apps/details?id=" + BuildConfig.APPLICATION_ID);
+    intent.setType("text/plain");
+
+    if (intent.resolveActivity(act.getPackageManager()) != null)
+      {
+      startActivity(intent);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void email(FragmentActivity act)
+    {
+    Resources res = act.getResources();
+    String[] email = { res.getString(R.string.email_address) };
+    String version = findCurrentVersion((Activity)act);
+    String name = res.getString(R.string.app_name);
+
+    Intent intent = new Intent(Intent.ACTION_SENDTO);
+    intent.setData(Uri.parse("mailto:")); // only email apps should handle this
+    intent.putExtra(Intent.EXTRA_EMAIL, email);
+    intent.putExtra(Intent.EXTRA_SUBJECT, name+" "+version);
+
+    if (intent.resolveActivity(act.getPackageManager()) != null)
+      {
+      startActivity(intent);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static String getDialogTag()
+    {
+    return "DialogAbout";
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogAbstract.java b/src/main/java/org/distorted/dialogs/DialogAbstract.java
new file mode 100644
index 00000000..cc8efb43
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogAbstract.java
@@ -0,0 +1,177 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2023 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatDialogFragment;
+import androidx.fragment.app.FragmentActivity;
+
+import org.distorted.helpers.BaseActivity;
+import org.distorted.main.MainActivity;
+import org.distorted.main.R;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+abstract public class DialogAbstract extends AppCompatDialogFragment
+  {
+  static final int PARAMETRIC_TITLE = -10000;
+
+  protected float mTitleSize, mButSize, mTextSize;
+  protected int mWidth, mHeight;
+  protected String mArgument;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  abstract int getResource();
+  abstract int getTitleResource();
+  abstract int getPositive();
+  abstract int getNegative();
+  abstract boolean hasArgument();
+  abstract void positiveAction();
+  abstract void negativeAction();
+  abstract void prepareBody(Dialog dialog, View view, FragmentActivity act, float size);
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  String getTitleString(FragmentActivity act)
+    {
+    return "";
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @NonNull
+  @Override
+  public Dialog onCreateDialog(Bundle savedInstanceState)
+    {
+    FragmentActivity act = getActivity();
+    LayoutInflater inflater = act.getLayoutInflater();
+    AlertDialog.Builder builder = new AlertDialog.Builder(act);
+
+    DisplayMetrics displaymetrics = new DisplayMetrics();
+    act.getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
+    mWidth    = displaymetrics.widthPixels;
+    mHeight   = displaymetrics.heightPixels;
+    mTitleSize= mHeight*0.032f;
+    mButSize  = mHeight*0.040f;
+    mTextSize = mHeight*0.025f;
+
+    if( hasArgument() )
+      {
+      Bundle args = getArguments();
+
+      try
+        {
+        mArgument = args!=null ? args.getString("argument") : "";
+        }
+      catch(Exception e)
+        {
+        mArgument = "";
+        }
+      }
+    else mArgument = "";
+
+    final View view = inflater.inflate(getResource(), null);
+    builder.setView(view);
+    builder.setCancelable(true);
+
+    int title = getTitleResource();
+    if( title>=0 || title==PARAMETRIC_TITLE )
+      {
+      TextView tv = (TextView) inflater.inflate(R.layout.dialog_title, null);
+      tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTitleSize);
+
+      if( title>=0 )
+        {
+        tv.setText(title);
+        }
+      else
+        {
+        String titleString = getTitleString(act);
+        tv.setText(titleString);
+        }
+
+      builder.setCustomTitle(tv);
+      }
+
+    int positive = getPositive();
+    if( positive>=0 )
+      {
+      builder.setPositiveButton( positive, new DialogInterface.OnClickListener()
+        {
+        @Override
+        public void onClick(DialogInterface dialog, int which)
+          {
+          positiveAction();
+          }
+        });
+      }
+
+    int negative = getNegative();
+    if( negative>=0 )
+      {
+      builder.setNegativeButton( negative, new DialogInterface.OnClickListener()
+        {
+        @Override
+        public void onClick(DialogInterface dialog, int which)
+          {
+          negativeAction();
+          }
+        });
+      }
+
+    Dialog dialog = builder.create();
+    dialog.setCanceledOnTouchOutside(false);
+
+    prepareBody(dialog,view,act,mTextSize);
+
+    Window window = dialog.getWindow();
+
+    if( window!=null )
+      {
+      BaseActivity bact = (BaseActivity) act;
+      int m = bact.getMediumColor();
+      window.setBackgroundDrawableResource(m);
+      window.getDecorView().setSystemUiVisibility(MainActivity.FLAGS);
+      }
+
+    dialog.setOnShowListener(new DialogInterface.OnShowListener()
+      {
+      @Override
+      public void onShow(DialogInterface dialog)
+        {
+        if( positive>=0 )
+          {
+          Button btnPositive = ((AlertDialog)dialog).getButton(Dialog.BUTTON_POSITIVE);
+          btnPositive.setTextSize(TypedValue.COMPLEX_UNIT_PX, mButSize);
+          }
+        if( negative>=0 )
+          {
+          Button btnNegative = ((AlertDialog)dialog).getButton(Dialog.BUTTON_NEGATIVE);
+          btnNegative.setTextSize(TypedValue.COMPLEX_UNIT_PX, mButSize);
+          }
+        }
+      });
+
+    return dialog;
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogBandagedDelete.java b/src/main/java/org/distorted/dialogs/DialogBandagedDelete.java
new file mode 100644
index 00000000..1a1e9b0c
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogBandagedDelete.java
@@ -0,0 +1,55 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2022 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.app.Dialog;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.fragment.app.FragmentActivity;
+
+import org.distorted.bandaged.BandagedActivity;
+import org.distorted.main.R;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogBandagedDelete extends DialogAbstract
+  {
+  public int getResource()      { return R.layout.dialog_delete_object; }
+  public int getTitleResource() { return R.string.delete_object; }
+  public boolean hasArgument()  { return true; }
+  public int getPositive()      { return R.string.yes; }
+  public int getNegative()      { return R.string.no; }
+  public void negativeAction()  { }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void positiveAction()
+    {
+    BandagedActivity bact = (BandagedActivity)getContext();
+    if( bact!=null ) bact.deleteObject(mArgument);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
+    {
+    TextView save = view.findViewById(R.id.delete_object_text);
+    save.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static String getDialogTag()
+    {
+    return "DialogBandagedDelete";
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogBandagedSave.java b/src/main/java/org/distorted/dialogs/DialogBandagedSave.java
new file mode 100644
index 00000000..a7d3f011
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogBandagedSave.java
@@ -0,0 +1,60 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2022 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.app.Dialog;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.fragment.app.FragmentActivity;
+
+import org.distorted.bandaged.BandagedActivity;
+import org.distorted.bandaged.BandagedRenderer;
+import org.distorted.main.R;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogBandagedSave extends DialogAbstract
+  {
+  public int getResource()      { return R.layout.dialog_save_object; }
+  public int getTitleResource() { return R.string.save_object; }
+  public boolean hasArgument()  { return false; }
+  public int getPositive()      { return R.string.yes; }
+  public int getNegative()      { return R.string.no; }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void positiveAction()
+    {
+    BandagedActivity bact = (BandagedActivity)getContext();
+
+    if( bact!=null )
+      {
+      BandagedRenderer rend = bact.getRenderer();
+      rend.saveObject();
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void negativeAction()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
+    {
+    TextView save = view.findViewById(R.id.save_object_text);
+    save.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogCreatorView.java b/src/main/java/org/distorted/dialogs/DialogCreatorView.java
new file mode 100644
index 00000000..847f638f
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogCreatorView.java
@@ -0,0 +1,70 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2023 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.distorted.main.MainActivity;
+import org.distorted.main.R;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogCreatorView
+  {
+  private final View mView;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public DialogCreatorView(final MainActivity act, final DialogCreators dialog,
+                           final int index, int icon, int title, int desc, int padding, int fontSize,
+                           LinearLayout.LayoutParams pView, LinearLayout.LayoutParams pText, LinearLayout.LayoutParams pButt)
+    {
+    mView = act.getLayoutInflater().inflate(R.layout.dialog_creators_pane, null);
+    mView.setLayoutParams(pView);
+    mView.setPadding(padding,padding,padding,padding);
+
+    TextView titleView = mView.findViewById(R.id.creators_pane_title);
+    titleView.setText(title);
+    TextView descView = mView.findViewById(R.id.creators_pane_description);
+    descView.setText(desc);
+
+    titleView.setLayoutParams(pText);
+
+    ImageView iconView = mView.findViewById(R.id.creators_pane_image);
+    iconView.setImageResource(icon);
+
+    Button button = mView.findViewById(R.id.creators_pane_button);
+
+    button.setOnClickListener( new View.OnClickListener()
+      {
+      @Override
+      public void onClick(View v)
+        {
+        dialog.dismiss();
+        act.switchToBandagedCreator(index);
+        }
+      });
+
+    button.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
+    button.setLayoutParams(pButt);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public View getView()
+    {
+    return mView;
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogCreators.java b/src/main/java/org/distorted/dialogs/DialogCreators.java
new file mode 100644
index 00000000..5997c77e
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogCreators.java
@@ -0,0 +1,140 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2023 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.app.Dialog;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.fragment.app.FragmentActivity;
+
+import org.distorted.main.MainActivity;
+import org.distorted.main.R;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogCreators extends DialogAbstract
+  {
+  private enum BandagedObjectDescription
+    {
+    CUBOID     ( org.distorted.objectlib.R.drawable.cu_232, R.string.creator_cuboid_title  , R.string.creator_cuboid_desc   ),
+    PYRAMINX   ( org.distorted.objectlib.R.drawable.pyra_3, R.string.creator_pyraminx_title, R.string.creator_pyraminx_desc ),
+    MEGAMINX   ( org.distorted.objectlib.R.drawable.mega_3, R.string.creator_megaminx_title, R.string.creator_megaminx_desc ),
+    OCTAHEDRON ( org.distorted.objectlib.R.drawable.diam_2, R.string.creator_octahedron_title, R.string.creator_octahedron_desc ),
+    SKEWB      ( org.distorted.objectlib.R.drawable.skew_2, R.string.creator_skewb_title, R.string.creator_skewb_desc ),
+    ;
+
+    public static final int NUM_OBJECTS = values().length;
+    private static final BandagedObjectDescription[] objects;
+
+    final int mIcon, mTitle, mDescription;
+
+    BandagedObjectDescription(int icon, int title, int descripton)
+      {
+      mIcon        = icon;
+      mTitle       = title;
+      mDescription = descripton;
+      }
+
+    static
+      {
+      int i=0;
+      objects = new BandagedObjectDescription[NUM_OBJECTS];
+      for( BandagedObjectDescription object: BandagedObjectDescription.values() ) objects[i++] = object;
+      }
+
+    ////////////////////////////////////////////////////////////////////////
+
+    static int getIcon(int ordinal)
+      {
+      return objects[ordinal].mIcon;
+      }
+
+    ////////////////////////////////////////////////////////////////////////
+
+    static int getTitle(int ordinal)
+      {
+      return objects[ordinal].mTitle;
+      }
+
+    ////////////////////////////////////////////////////////////////////////
+
+    static int getDescription(int ordinal)
+      {
+      return objects[ordinal].mDescription;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public void onResume()
+    {
+    super.onResume();
+
+    Window window = getDialog().getWindow();
+
+    if( window!=null )
+      {
+      WindowManager.LayoutParams params = window.getAttributes();
+      params.width  = (int)Math.min( mHeight*0.65f,mWidth*0.98f );
+      params.height = (int)( mHeight*0.90f);
+      window.setAttributes(params);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getResource()            { return R.layout.dialog_scrollable_panes; }
+  public int getTitleResource()       { return R.string.creators; }
+  public boolean hasArgument()        { return false; }
+  public int getPositive()            { return R.string.ok; }
+  public int getNegative()            { return -1; }
+  public void positiveAction()        { }
+  public void negativeAction()        { }
+  public static String getDialogTag() { return "DialogCreators"; }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
+    {
+    int margin= (int)(mHeight*0.010f);
+    int padd  = (int)(mHeight*0.010f);
+    int font  = (int)(mHeight*0.025f);
+
+    LinearLayout layout= view.findViewById(R.id.dialog_scrollable_main_layout);
+    TextView text  = view.findViewById(R.id.dialog_scrollable_message);
+    text.setVisibility(View.GONE);
+
+    LinearLayout.LayoutParams pV = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
+    pV.setMargins(margin, margin, margin, 0);
+    LinearLayout.LayoutParams pL = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
+    pL.setMargins(margin, margin, margin, margin);
+    LinearLayout.LayoutParams pT = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
+    pT.setMargins(0,0,0,2*margin);
+    LinearLayout.LayoutParams pB = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
+    pB.setMargins(0,2*margin,0,0);
+
+    int num = BandagedObjectDescription.NUM_OBJECTS;
+    MainActivity ract = (MainActivity) getContext();
+
+    for(int i=0; i<num; i++)
+      {
+      int icon        = BandagedObjectDescription.getIcon(i);
+      int title       = BandagedObjectDescription.getTitle(i);
+      int description = BandagedObjectDescription.getDescription(i);
+      DialogCreatorView pane = new DialogCreatorView(ract,this,i,icon,title,description, padd, font, (i==num-1?pL:pV),pT,pB);
+      layout.addView(pane.getView());
+      }
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogError.java b/src/main/java/org/distorted/dialogs/DialogError.java
new file mode 100644
index 00000000..6ffcbd93
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogError.java
@@ -0,0 +1,54 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.app.Dialog;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.fragment.app.FragmentActivity;
+
+import org.distorted.main.R;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogError extends DialogAbstract
+  {
+  public int getResource()      { return R.layout.dialog_error; }
+  public int getTitleResource() { return R.string.opengl_error; }
+  public boolean hasArgument()  { return false; }
+  public int getPositive()      { return R.string.ok; }
+  public int getNegative()      { return -1; }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void positiveAction()
+    {
+    final FragmentActivity act = getActivity();
+    if( act!=null ) act.finish();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void negativeAction()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
+    {
+    TextView text = view.findViewById(R.id.error_string);
+    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
+    text.setText(R.string.opengl_error_text);
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogExit.java b/src/main/java/org/distorted/dialogs/DialogExit.java
new file mode 100644
index 00000000..84aa09f9
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogExit.java
@@ -0,0 +1,39 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2023 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.view.View;
+
+import androidx.fragment.app.FragmentActivity;
+
+import org.distorted.main.R;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogExit extends DialogAbstract
+  {
+  public int getResource()      { return R.layout.exit_app; }
+  public int getTitleResource() { return -1; }
+  public boolean hasArgument()  { return false; }
+  public int getPositive()      { return R.string.yes; }
+  public int getNegative()      { return R.string.no; }
+  public void negativeAction()  { }
+  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size) { }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void positiveAction()
+    {
+    final Activity act = (Activity)getContext();
+    if( act!=null ) act.finish();
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogMessage.java b/src/main/java/org/distorted/dialogs/DialogMessage.java
new file mode 100644
index 00000000..78e1c54e
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogMessage.java
@@ -0,0 +1,46 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2024 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.app.Dialog;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.fragment.app.FragmentActivity;
+
+import org.distorted.main.R;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogMessage extends DialogAbstract
+  {
+  public int getResource()      { return R.layout.dialog_message; }
+  public int getTitleResource() { return -1; }
+  public boolean hasArgument()  { return true; }
+  public int getPositive()      { return R.string.ok; }
+  public int getNegative()      { return -1; }
+  public void negativeAction()  { }
+  public void positiveAction()  { }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
+    {
+    TextView mess = view.findViewById(R.id.dialog_message);
+    mess.setText(mArgument);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static String getDialogTag()
+    {
+    return "DialogBandageMessage";
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogNewRecord.java b/src/main/java/org/distorted/dialogs/DialogNewRecord.java
new file mode 100644
index 00000000..9a62396e
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogNewRecord.java
@@ -0,0 +1,87 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.app.Dialog;
+import android.os.Bundle;
+import androidx.fragment.app.FragmentActivity;
+
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.TextView;
+
+import org.distorted.main.R;
+import org.distorted.external.RubikScores;
+import org.distorted.playui.PlayActivity;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogNewRecord extends DialogAbstract
+  {
+  public int getResource()      { return R.layout.dialog_new_record; }
+  public int getTitleResource() { return R.string.new_record; }
+  public boolean hasArgument()  { return true; }
+  public int getPositive()      { return R.string.yes; }
+  public int getNegative()      { return R.string.no; }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void positiveAction()
+    {
+    RubikScores scores = RubikScores.getInstance();
+    String name = scores.getName();
+    Bundle bundle = new Bundle();
+    PlayActivity act = (PlayActivity)getActivity();
+
+    if( act!=null )
+      {
+      if(name.length()>0)
+        {
+        bundle.putString("argument", "true");
+        DialogScores scoresDiag = new DialogScores();
+        scoresDiag.setArguments(bundle);
+        scoresDiag.show(act.getSupportFragmentManager(), null);
+        }
+      else
+        {
+        bundle.putString("argument", name);
+        DialogSetName nameDiag = new DialogSetName();
+        nameDiag.setArguments(bundle);
+        nameDiag.show(act.getSupportFragmentManager(), null);
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void negativeAction()
+    {
+    PlayActivity act = (PlayActivity)getActivity();
+    if( act!=null ) act.finish();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
+    {
+    TextView text = view.findViewById(R.id.new_record_time);
+    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
+    text.setText(mArgument);
+    TextView submit = view.findViewById(R.id.new_record_submit);
+    submit.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static String getDialogTag()
+    {
+    return "DialogNewRecord";
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogPattern.java b/src/main/java/org/distorted/dialogs/DialogPattern.java
new file mode 100644
index 00000000..bd140f7b
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogPattern.java
@@ -0,0 +1,155 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2023 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.app.Dialog;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.ExpandableListView;
+
+import androidx.fragment.app.FragmentActivity;
+
+import org.distorted.main.R;
+import org.distorted.objectlib.main.ObjectControl;
+import org.distorted.objectlib.patterns.RubikPattern;
+import org.distorted.objectlib.patterns.RubikPatternList;
+import org.distorted.objects.RubikObject;
+import org.distorted.objects.RubikObjectList;
+import org.distorted.patternui.PatternActivity;
+import org.distorted.patternui.ScreenList;
+import org.distorted.patternui.ScreenPattern;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogPattern extends DialogAbstract
+  {
+  private ExpandableListView mListView;
+  private int mPatternOrdinal, mPos;
+  private int mExpandedGroup;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public void onResume()
+    {
+    super.onResume();
+
+    Window window = getDialog().getWindow();
+
+    if( window!=null )
+      {
+      WindowManager.LayoutParams params = window.getAttributes();
+      params.width  = (int)Math.min( mHeight*0.65f,mWidth*0.98f );
+      //params.height = (int)Math.min( mHeight*0.80f,mWidth*1.30f );
+      window.setAttributes(params);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getResource()      { return R.layout.dialog_pattern_single; }
+  public int getTitleResource() { return R.string.choose_pattern; }
+  public boolean hasArgument()  { return true; }
+  public int getPositive()      { return -1; }
+  public int getNegative()      { return -1; }
+  public void positiveAction()  { }
+  public void negativeAction()  { }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
+    {
+    int objectOrdinal = Integer.parseInt(mArgument);
+
+    if( objectOrdinal<0 )
+      {
+      android.util.Log.e("D", "object "+mArgument+" not found");
+      return;
+      }
+
+    RubikObject object = RubikObjectList.getObject(objectOrdinal);
+    int objectIndex = object==null ? -1 : object.getIndex();
+
+    mPatternOrdinal = RubikPatternList.getOrdinal(objectIndex);
+
+    if( mPatternOrdinal<0 )
+      {
+      android.util.Log.e("D", "patterns for object "+mArgument+" not found");
+      return;
+      }
+
+    android.util.Log.e("D", "prepareBody: object "+mArgument);
+
+    final PatternActivity pact = (PatternActivity)getContext();
+    int width = pact!=null ? pact.getScreenWidthInPixels() : 100;
+    final ObjectControl control = pact!=null ? pact.getControl() : null;
+
+    RubikPattern pattern = RubikPattern.getInstance();
+    mExpandedGroup = pattern.recallExpanded(mPatternOrdinal);
+
+    mListView = view.findViewById(R.id.patternListView);
+    DialogPatternListAdapter listAdapter = new DialogPatternListAdapter(act,mPatternOrdinal,width);
+    mListView.setAdapter(listAdapter);
+
+    if( mExpandedGroup>=0 ) mListView.expandGroup(mExpandedGroup);
+
+    int visible = pattern.recallVisiblePos(mPatternOrdinal);
+    mListView.setSelectionFromTop(visible,0);
+
+    mListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener()
+      {
+      @Override
+      public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id)
+        {
+        RubikPattern pattern = RubikPattern.getInstance();
+        int[][] moves = pattern.reInitialize(mPatternOrdinal, groupPosition, childPosition);
+        if( control!=null ) control.initializeObject(moves);
+
+        ScreenPattern state = (ScreenPattern) ScreenList.PATT.getScreenClass();
+        state.setPattern(pact, mPatternOrdinal, groupPosition, childPosition);
+
+        rememberState();
+        dismiss();
+
+        return false;
+        }
+      });
+
+    mListView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener()
+      {
+      @Override
+      public void onGroupExpand(int groupPosition)
+        {
+        if(mExpandedGroup!=-1 && groupPosition!=mExpandedGroup)
+          {
+          mListView.collapseGroup(mExpandedGroup);
+          }
+
+        mExpandedGroup = groupPosition;
+        }
+      });
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void rememberState()
+    {
+    RubikPattern pattern = RubikPattern.getInstance();
+    pattern.rememberState(mPatternOrdinal,mPos,mListView.getFirstVisiblePosition(),mExpandedGroup);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static String getDialogTag()
+    {
+    return "DialogPatternSingle";
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogPatternListAdapter.java b/src/main/java/org/distorted/dialogs/DialogPatternListAdapter.java
new file mode 100644
index 00000000..41f4d7c9
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogPatternListAdapter.java
@@ -0,0 +1,153 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2023 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.content.Context;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.TextView;
+import org.distorted.objectlib.patterns.RubikPattern;
+import org.distorted.main.R;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class DialogPatternListAdapter extends BaseExpandableListAdapter
+  {
+  private static final float PATTERN_CHILD_TEXT  = 0.038f;
+  private static final float PATTERN_GROUP_TEXT  = 0.060f;
+  private final Context mContext;
+  private final int mTab, mWidth;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public DialogPatternListAdapter(Context context, int tab, int width)
+    {
+    mContext = context;
+    mTab     = tab;
+    mWidth   = width;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public Object getChild(int groupPosition, int childPosition)
+    {
+    RubikPattern pattern = RubikPattern.getInstance();
+    return pattern.getPatternName(mTab,groupPosition,childPosition);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public long getChildId(int groupPosition, int childPosition)
+    {
+    return childPosition;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View view, ViewGroup parent)
+    {
+    String childName = (String) getChild(groupPosition, childPosition);
+
+    if (view == null)
+      {
+      LayoutInflater infalInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+      view = infalInflater.inflate(R.layout.dialog_pattern_child_item, null);
+      }
+
+    int size = (int)(mWidth*PATTERN_CHILD_TEXT);
+    TextView childItem = view.findViewById(R.id.child);
+    childItem.setText(childName);
+    childItem.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
+    return view;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public int getChildrenCount(int groupPosition)
+    {
+    RubikPattern pattern = RubikPattern.getInstance();
+    return pattern.getNumPatterns(mTab,groupPosition);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public Object getGroup(int groupPosition)
+    {
+    RubikPattern pattern = RubikPattern.getInstance();
+    return pattern.getCategoryName(mTab,groupPosition);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public int getGroupCount()
+    {
+    RubikPattern pattern = RubikPattern.getInstance();
+    return pattern.getNumCategories(mTab);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public long getGroupId(int groupPosition)
+    {
+    return groupPosition;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public View getGroupView(int groupPosition, boolean isLastChild, View view, ViewGroup parent)
+    {
+    String groupName = (String) getGroup(groupPosition);
+
+    if (view == null)
+      {
+      LayoutInflater inf = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+      view = inf.inflate(R.layout.dialog_pattern_group_item, null);
+      }
+
+    int size = (int)(mWidth*PATTERN_GROUP_TEXT);
+    TextView heading = view.findViewById(R.id.heading);
+    heading.setText(groupName);
+    heading.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
+    RubikPattern pattern = RubikPattern.getInstance();
+    int numPatterns = pattern.getNumPatterns(mTab,groupPosition);
+    TextView counter = view.findViewById(R.id.counter);
+    counter.setText(String.format("%d", numPatterns));
+    counter.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
+    return view;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public boolean hasStableIds()
+    {
+    return true;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public boolean isChildSelectable(int groupPosition, int childPosition)
+    {
+    return true;
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogScores.java b/src/main/java/org/distorted/dialogs/DialogScores.java
new file mode 100644
index 00000000..dd5900a3
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogScores.java
@@ -0,0 +1,98 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2019 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.app.Dialog;
+import androidx.fragment.app.FragmentActivity;
+import androidx.viewpager.widget.ViewPager;
+import com.google.android.material.tabs.TabLayout;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.ImageView;
+
+import org.distorted.main.R;
+import org.distorted.objects.RubikObject;
+import org.distorted.objects.RubikObjectList;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogScores extends DialogAbstract
+  {
+  private static final float TAB_WIDTH = 0.066f;
+  private static final float TAB_HEIGHT= 0.066f;
+
+  private DialogScoresPagerAdapter mPagerAdapter;
+
+  public interface ScoresInvoker
+    {
+    int getObjectOrdinal();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public void onResume()
+    {
+    super.onResume();
+
+    Window window = getDialog().getWindow();
+
+    if( window!=null )
+      {
+      WindowManager.LayoutParams params = window.getAttributes();
+      params.width  = (int)Math.min( mHeight*0.65f,mWidth*0.98f );
+      params.height = (int)( mHeight*0.90f);
+      window.setAttributes(params);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getResource()      { return R.layout.dialog_tabbed; }
+  public int getTitleResource() { return R.string.scores; }
+  public boolean hasArgument()  { return true; }
+  public int getPositive()      { return R.string.ok; }
+  public int getNegative()      { return -1; }
+  public void positiveAction()  { DialogScoresThread.getInstance().exit(); }
+  public void negativeAction()  { }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
+    {
+    final int tabHeight= (int)(mHeight*TAB_HEIGHT);
+    final int tabWidth = (int)(mHeight*TAB_WIDTH);
+
+    ViewPager viewPager = view.findViewById(R.id.viewpager);
+    TabLayout tabLayout = view.findViewById(R.id.sliding_tabs);
+    mPagerAdapter = new DialogScoresPagerAdapter(act,viewPager,mArgument.equals("true"), this);
+    tabLayout.setupWithViewPager(viewPager);
+
+    ScoresInvoker si = (ScoresInvoker) act;
+    int objectOrdinal = si.getObjectOrdinal();
+
+    viewPager.setCurrentItem(objectOrdinal);
+    int numObjects = RubikObjectList.getNumObjects();
+    ViewGroup.LayoutParams paramsView = new ViewGroup.LayoutParams( tabWidth,tabHeight );
+
+    for( int object=0; object<numObjects; object++ )
+      {
+      RubikObject robject = RubikObjectList.getObject(object);
+      ImageView imageView = new ImageView(act);
+      if( robject!=null ) robject.setIconTo(act,imageView);
+      imageView.setLayoutParams(paramsView);
+      TabLayout.Tab tab = tabLayout.getTabAt(object);
+      if( tab!=null ) tab.setCustomView(imageView);
+      }
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogScoresPagerAdapter.java b/src/main/java/org/distorted/dialogs/DialogScoresPagerAdapter.java
new file mode 100644
index 00000000..1e38a4c7
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogScoresPagerAdapter.java
@@ -0,0 +1,223 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.os.Bundle;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.FragmentActivity;
+import androidx.viewpager.widget.PagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.distorted.main.R;
+import org.distorted.external.RubikScores;
+import org.distorted.external.RubikNetwork;
+import org.distorted.objects.RubikObject;
+import org.distorted.objects.RubikObjectList;
+import static org.distorted.external.RubikScores.LEVELS_SHOWN;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class DialogScoresPagerAdapter extends PagerAdapter implements RubikNetwork.ScoresReceiver
+  {
+  private static final int NETWORK_NOT_READY = 0;
+  private static final int NETWORK_SUCCESS   = 1;
+  private static final int NETWORK_FAILURE   = 2;
+
+  private final FragmentActivity mAct;
+  private final DialogScores mDialog;
+  private final DialogScoresView[] mViews;
+  private final ViewPager mViewPager;
+  private final int mNumTabs;
+  private final boolean mIsSubmitting;
+  private final int[] mNumLevels;
+  private String[][][] mCountry, mName;
+  private int[][][] mTime;
+  private String mMessage;
+  private int mNetworkState;
+  private final Object mObj;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// we do not show level8 (i.e. level==7) - because in the new UI 2.0 this one does not exist anymore.
+
+  private boolean showLevel(int level)
+    {
+    return level!=7;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void receive(final String[][][] country, final String[][][] name, final int[][][] time)
+    {
+    synchronized(mObj)
+      {
+      DialogScoresThread thr = DialogScoresThread.getInstance();
+      thr.equip(mAct,mViewPager);
+
+      mNetworkState = NETWORK_SUCCESS;
+      mCountry = country;
+      mName = name;
+      mTime = time;
+
+      for(int t=0; t<mNumTabs; t++)
+        if( mViews[t]!=null )
+          {
+          int num = mNumLevels[t];
+          String[][] c = country[t];
+          String[][] n = name[t];
+          int[][] tm = time[t];
+          for(int l=0; l<=num; l++)
+            if( showLevel(l) ) thr.newWork(t, l, num, mViews[t], c[l], n[l], tm[l]);
+          }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void message(final String mess)
+    {
+    mNetworkState = NETWORK_FAILURE;
+    mMessage = mess;
+
+    mAct.runOnUiThread(new Runnable()
+      {
+      @Override
+      public void run()
+        {
+        for(int i=0; i<mNumTabs; i++)
+          if( mViews[i]!=null ) mViews[i].message(mess);
+        }
+      });
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void error(final String error)
+    {
+    char errorNumber = error.charAt(0);
+
+    switch(errorNumber)
+      {
+      case '1': message(mAct.getString(R.string.networkError));
+                break;
+      case '2': RubikScores scores = RubikScores.getInstance();
+                Bundle bundle = new Bundle();
+                bundle.putString("argument", scores.getName() );
+
+                DialogSetName nameDiag = new DialogSetName();
+                nameDiag.setArguments(bundle);
+                nameDiag.show(mAct.getSupportFragmentManager(), null);
+
+                mDialog.dismiss();
+
+                break;
+      case '3': message("Server error");
+                break;
+      default : message("Unexpected error");
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  DialogScoresPagerAdapter(FragmentActivity act, ViewPager viewPager, boolean isSubmitting, DialogScores diag)
+    {
+    mObj = new Object();
+    mAct = act;
+    mDialog = diag;
+    mNumTabs = RubikObjectList.getNumObjects();
+    mViews = new DialogScoresView[mNumTabs];
+    mViewPager = viewPager;
+    mIsSubmitting = isSubmitting;
+    mNetworkState = NETWORK_NOT_READY;
+
+    mNumLevels = new int[mNumTabs];
+
+    for(int i=0; i<mNumTabs; i++)
+      {
+      RubikObject object = RubikObjectList.getObject(i);
+      int numScramble = object==null ? 1 : object.getNumScramble();
+      mNumLevels[i] = Math.min(numScramble-1,LEVELS_SHOWN);
+      }
+
+    viewPager.setAdapter(this);
+    viewPager.setOffscreenPageLimit(1);
+
+    RubikNetwork network = RubikNetwork.getInstance();
+
+    if( mIsSubmitting )  network.submit  ( this, mAct );
+    else                 network.download( this, mAct );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  @NonNull
+  public Object instantiateItem(@NonNull ViewGroup collection, int position)
+    {
+    DialogScoresView view;
+    DisplayMetrics metrics = mAct.getResources().getDisplayMetrics();
+
+    synchronized(mObj)
+      {
+      view = new DialogScoresView(mAct, metrics.heightPixels, mIsSubmitting, mNetworkState==NETWORK_SUCCESS );
+      collection.addView(view);
+
+      if( mNetworkState==NETWORK_SUCCESS )
+        {
+        int num = mNumLevels[position];
+        String[][] c = mCountry[position];
+        String[][] n = mName[position];
+        int[][] tm = mTime[position];
+
+        DialogScoresThread thr = DialogScoresThread.getInstance();
+        thr.equip(mAct,mViewPager);
+
+        for(int l=0; l<=num; l++)
+          if( showLevel(l) ) thr.newWork(position, l, num, view, c[l], n[l], tm[l]);
+        }
+      else if( mNetworkState==NETWORK_FAILURE )
+        {
+        view.message(mMessage);
+        }
+      }
+
+    mViews[position] = view;
+
+    return view;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public void destroyItem(ViewGroup collection, int position, @NonNull Object view)
+    {
+    collection.removeView((View) view);
+    mViews[position] = null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public int getCount()
+    {
+    return mNumTabs;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public boolean isViewFromObject(@NonNull View view, @NonNull Object object)
+    {
+    return view == object;
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogScoresThread.java b/src/main/java/org/distorted/dialogs/DialogScoresThread.java
new file mode 100644
index 00000000..7639b8a6
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogScoresThread.java
@@ -0,0 +1,176 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2023 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.widget.LinearLayout;
+
+import androidx.fragment.app.FragmentActivity;
+import androidx.viewpager.widget.ViewPager;
+
+import org.distorted.main.R;
+
+import java.lang.ref.WeakReference;
+import java.util.Vector;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class DialogScoresThread extends Thread
+  {
+  private final Object mObject;
+  private final Vector<WorkLoad> mBuffers;
+  private WeakReference<FragmentActivity> mWeakAct;
+  private ViewPager mViewPager;
+  private boolean mRunning;
+
+  private static DialogScoresThread mThis;
+
+  private static class WorkLoad
+    {
+    int tab, level, numLevels;
+    DialogScoresView view;
+    String[] country, name;
+    int[] time;
+
+    WorkLoad(int t, int l, int nl, DialogScoresView v, String[] c, String[] n, int[] tm)
+      {
+      tab = t;
+      level = l;
+      numLevels = nl;
+      view = v;
+      country = c;
+      name = n;
+      time = tm;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private DialogScoresThread()
+    {
+    mObject  = new Object();
+    mRunning = true;
+    mBuffers = new Vector<>();
+    this.start();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  static DialogScoresThread getInstance()
+    {
+    if( mThis==null ) mThis = new DialogScoresThread();
+    return mThis;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void equip(FragmentActivity act, ViewPager pager)
+    {
+    mWeakAct = new WeakReference<>(act);
+    mViewPager = pager;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void newWork(int t, int l, int nl, DialogScoresView v, String[] c, String[] n, int[] tm)
+    {
+    synchronized(mObject)
+      {
+      WorkLoad load = new WorkLoad(t,l,nl,v,c,n,tm);
+      mBuffers.add(load);
+      mObject.notify();
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void exit()
+    {
+    synchronized(mObject)
+      {
+      mRunning = false;
+      mThis = null;
+      mObject.notify();
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void run()
+    {
+    WorkLoad load;
+
+    while(true)
+      {
+      synchronized(mObject)
+        {
+        do
+          {
+          load = getNextLoad();
+          if( load!=null ) process(load);
+          }
+        while(load!=null);
+
+        if( mRunning )
+          {
+          try { mObject.wait(); }
+          catch(InterruptedException ignored) { }
+          }
+        else break;
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private WorkLoad getNextLoad()
+    {
+    if( mViewPager==null ) return null;
+
+    int currentTab = mViewPager.getCurrentItem();
+    int size = mBuffers.size();
+
+    for(int i=0; i<size; i++)
+      {
+      WorkLoad load = mBuffers.get(i);
+      if( load.tab==currentTab ) return mBuffers.remove(i);
+      }
+
+    return size>0 ? mBuffers.remove(0) : null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void process(WorkLoad load)
+    {
+    final FragmentActivity act = mWeakAct.get();
+    int tab = load.tab;
+    int level = load.level;
+    int numLevels = load.numLevels;
+    DialogScoresView view = load.view;
+    String[] country = load.country;
+    String[] name = load.name;
+    int[] time = load.time;
+    String title = level==numLevels ? act.getString(R.string.levelM) : act.getString(R.string.lv_placeholder, level+1);
+
+    if( view!=null )
+      {
+      final LinearLayout section = view.createSection(act, tab, title, level, country, name, time);
+
+      act.runOnUiThread(new Runnable()
+        {
+        @Override
+        public void run()
+          {
+          view.addSection(act, section);
+          }
+        });
+      }
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogScoresView.java b/src/main/java/org/distorted/dialogs/DialogScoresView.java
new file mode 100644
index 00000000..8305c770
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogScoresView.java
@@ -0,0 +1,195 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.content.Context;
+import android.content.res.Resources;
+import androidx.fragment.app.FragmentActivity;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.distorted.helpers.BaseActivity;
+import org.distorted.main.R;
+import org.distorted.external.RubikScores;
+
+import static org.distorted.external.RubikNetwork.MAX_PLACES;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogScoresView extends FrameLayout
+  {
+  private static final float SCORES_LEVEL_TEXT = 0.035f;
+  private static final float SCORES_ITEM_TEXT  = 0.025f;
+
+  private LinearLayout mLayout=null;
+  private int mHeight;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public DialogScoresView(Context context, AttributeSet attrs, int defStyle)
+    {
+    super(context, attrs, defStyle);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public DialogScoresView(Context context, AttributeSet attrs)
+    {
+    super(context, attrs);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public DialogScoresView(Context context, int height, boolean isSubmitting, boolean downloadedAlready)
+    {
+    super(context);
+
+    mHeight = height;
+
+    if( !downloadedAlready )
+      {
+      View view = inflate(context, R.layout.dialog_scores_downloading, null);
+      addView(view);
+      TextView text = findViewById(R.id.message_text);
+      text.setText(context.getString(isSubmitting ? R.string.submitting : R.string.downloading));
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  LinearLayout createSection(FragmentActivity act, int tab, String title, int level, final String[] country, final String[] name, final int[] time)
+    {
+    LinearLayout levelLayout = (LinearLayout)inflate(act, R.layout.dialog_scores_scramble_title, null);
+    TextView text = levelLayout.findViewById(R.id.scoresScrambleTitle);
+    text.setText(title);
+
+    int size = (int)(mHeight*SCORES_LEVEL_TEXT);
+    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
+
+    Resources res = act.getResources();
+    String packageName = act.getPackageName();
+    RubikScores scores = RubikScores.getInstance();
+
+    boolean inserted = false;
+    int myRecordInMillis = scores.getRecord(tab, level);
+    String myRecord = ( myRecordInMillis<RubikScores.NO_RECORD ) ? formatRecord(myRecordInMillis) : "??";
+    String myName = scores.getName();
+    if( myName.length()==0 ) myName = act.getString(R.string.you);
+    int myCountryID = res.getIdentifier( scores.getCountry(), "drawable", packageName);
+    String theirTime;
+    int theirCountryID;
+
+    BaseActivity bact = (BaseActivity) act;
+    int selected = bact.getSelectedColor();
+    int height = (int)(mHeight*SCORES_ITEM_TEXT);
+    int white = res.getColor(R.color.white);
+    int red   = res.getColor(selected);
+    boolean equals;
+
+    for(int j=0; j<MAX_PLACES-1; j++)
+      {
+      if( name[j] != null )
+        {
+        if( myRecordInMillis<time[j] && !inserted )
+          {
+          inserted = true;
+          View row = createRow(act, myCountryID, myName, myRecord, height, red);
+          levelLayout.addView(row);
+          }
+
+        equals = name[j].equals(myName);
+
+        if( !inserted || !equals )
+          {
+          if( equals ) inserted=true;
+          theirCountryID = res.getIdentifier( country[j], "drawable", packageName);
+          theirTime = formatRecord(time[j]);
+          View row = createRow(act, theirCountryID, name[j], theirTime, height, equals ? red:white);
+          levelLayout.addView(row);
+          }
+        }
+      }
+
+    if( !inserted )
+      {
+      View row = createRow(act, myCountryID, myName, myRecord, height, red);
+      levelLayout.addView(row);
+      }
+
+    return levelLayout;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static String formatRecord(int time)
+    {
+    time /= 10;
+    int millis = time%100;
+    time /= 100;
+    int seconds = time%60;
+    int minutes = time/60;
+
+    String ret = minutes + (seconds<10 ? ":0" : ":") + seconds + (millis<10 ? ".0" : ".") + millis;
+    return minutes<10 ? "0"+ret : ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private View createRow(FragmentActivity act, int countryID, String name, String time, int height, int color)
+    {
+    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(countryID!=0 ? countryID : org.distorted.flags.R.drawable.unknown);
+    textName.setText(name);
+    textTime.setText(time);
+
+    textName.setTextColor(color);
+    textTime.setTextColor(color);
+
+    textName.setTextSize(TypedValue.COMPLEX_UNIT_PX, height);
+    textTime.setTextSize(TypedValue.COMPLEX_UNIT_PX, height);
+
+    return row;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// needs to run on UI thread
+
+  void addSection(FragmentActivity act, LinearLayout section)
+    {
+    if( mLayout==null )
+      {
+      removeAllViews();
+      View tab = inflate(act, R.layout.dialog_scores_tab, null);
+      mLayout = tab.findViewById(R.id.tabLayout);
+      addView(tab);
+      }
+
+    mLayout.addView(section);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// needs to run on UI thread
+
+  void message(final String mess)
+    {
+    TextView text = findViewById(R.id.message_text);
+    if( text!=null ) text.setText(mess);
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogSetName.java b/src/main/java/org/distorted/dialogs/DialogSetName.java
new file mode 100644
index 00000000..154c21c1
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogSetName.java
@@ -0,0 +1,143 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.app.Dialog;
+import android.os.Bundle;
+import androidx.fragment.app.FragmentActivity;
+import androidx.appcompat.app.AlertDialog;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import org.distorted.main.R;
+import org.distorted.external.RubikScores;
+import org.distorted.playui.PlayActivity;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogSetName extends DialogAbstract
+  {
+  private static final int MAX_NAME_LEN = 15;
+  private EditText mEdit;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public void onStart()
+    {
+    super.onStart();
+
+    AlertDialog dialog = (AlertDialog)getDialog();
+
+    if( dialog!=null )
+      {
+      Button positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE);
+
+      if( positiveButton!=null )
+        {
+        String editName = mEdit.getText().toString();
+        positiveButton.setEnabled(editName.length()>0);
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getResource()      { return R.layout.dialog_set_name; }
+  public int getTitleResource() { return mArgument.length()==0 ? R.string.choose_name : R.string.name_taken; }
+  public boolean hasArgument()  { return true; }
+  public int getPositive()      { return R.string.ok; }
+  public int getNegative()      { return -1; }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void positiveAction()
+    {
+    String name = mEdit.getText().toString();
+    int len = name.length();
+
+    if( len>0 )
+      {
+      if( len>MAX_NAME_LEN )
+        {
+        name = name.substring(0,MAX_NAME_LEN);
+        }
+
+      name = name.replace(' ', '_');
+
+      try
+        {
+        PlayActivity act = (PlayActivity)getActivity();
+        RubikScores.getInstance().setName(name);
+        Bundle bundle = new Bundle();
+        bundle.putString("argument", "true");
+        DialogScores scores = new DialogScores();
+        scores.setArguments(bundle);
+        scores.show(act.getSupportFragmentManager(), null);
+        }
+      catch(IllegalStateException ex)
+        {
+        android.util.Log.e("D", "IllegalStateException trying to display SetName");
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void negativeAction()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
+    {
+    TextView text = view.findViewById(R.id.set_name_message);
+    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
+    mEdit = view.findViewById(R.id.set_name);
+    mEdit.setHeight( (int)(2*mTitleSize) );
+    mEdit.setTextSize(TypedValue.COMPLEX_UNIT_PX, 1.5f*mTitleSize);
+
+    if( mArgument.length()==0 )
+      {
+      text.setText(R.string.new_name);
+      }
+    else
+      {
+      text.setText(act.getString(R.string.new_name_try_again, mArgument));
+      }
+
+    dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+
+    mEdit.requestFocus();
+
+    mEdit.addTextChangedListener(new TextWatcher()
+      {
+      @Override
+      public void afterTextChanged(Editable s) {}
+
+      @Override
+      public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+      @Override
+      public void onTextChanged(CharSequence s, int start, int before, int count)
+        {
+        ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(count>0);
+        }
+      });
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogSolved.java b/src/main/java/org/distorted/dialogs/DialogSolved.java
new file mode 100644
index 00000000..35ff33fc
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogSolved.java
@@ -0,0 +1,62 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.app.Dialog;
+import androidx.fragment.app.FragmentActivity;
+
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.TextView;
+
+import org.distorted.main.R;
+import org.distorted.playui.PlayActivity;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogSolved extends DialogAbstract
+  {
+  public int getResource()      { return R.layout.dialog_solved; }
+  public int getTitleResource() { return R.string.solved; }
+  public boolean hasArgument()  { return true; }
+  public int getPositive()      { return R.string.ok; }
+  public int getNegative()      { return -1; }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void positiveAction()
+    {
+    PlayActivity act = (PlayActivity)getActivity();
+    if( act!=null ) act.finish();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void negativeAction()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
+    {
+    TextView text = view.findViewById(R.id.solved_time);
+    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
+    text.setText(mArgument);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static String getDialogTag()
+    {
+    return "DialogSolved";
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogSolverError.java b/src/main/java/org/distorted/dialogs/DialogSolverError.java
new file mode 100644
index 00000000..b7988271
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogSolverError.java
@@ -0,0 +1,41 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.app.Dialog;
+import androidx.fragment.app.FragmentActivity;
+
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.TextView;
+
+import org.distorted.main.R;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogSolverError extends DialogAbstract
+  {
+  public int getResource()      { return R.layout.dialog_solver_error; }
+  public int getTitleResource() { return R.string.error; }
+  public boolean hasArgument()  { return true; }
+  public int getPositive()      { return R.string.ok; }
+  public int getNegative()      { return -1; }
+  public void positiveAction()  { }
+  public void negativeAction()  { }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
+    {
+    TextView text = view.findViewById(R.id.solver_error);
+    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
+    text.setText(mArgument);
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogSolverImpossible.java b/src/main/java/org/distorted/dialogs/DialogSolverImpossible.java
new file mode 100644
index 00000000..23934070
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogSolverImpossible.java
@@ -0,0 +1,41 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.app.Dialog;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.fragment.app.FragmentActivity;
+
+import org.distorted.main.R;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogSolverImpossible extends DialogAbstract
+  {
+  public int getResource()      { return R.layout.dialog_solver_error; }
+  public int getTitleResource() { return R.string.impossible_position; }
+  public boolean hasArgument()  { return true; }
+  public int getPositive()      { return R.string.ok; }
+  public int getNegative()      { return -1; }
+  public void positiveAction()  { }
+  public void negativeAction()  { }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
+    {
+    TextView text = view.findViewById(R.id.solver_error);
+    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
+    text.setText(mArgument);
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogSolverView.java b/src/main/java/org/distorted/dialogs/DialogSolverView.java
new file mode 100644
index 00000000..dbe16f9e
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogSolverView.java
@@ -0,0 +1,84 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2023 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.distorted.main.R;
+import org.distorted.objectlib.helpers.OperatingSystemInterface;
+import org.distorted.objectlib.main.TwistyObject;
+import org.distorted.objectlib.solvers.verifiers.SolverAbstract;
+import org.distorted.objectlib.solvers.verifiers.SolvingList;
+import org.distorted.solverui.ScreenList;
+import org.distorted.solverui.ScreenSetupPosition;
+import org.distorted.solverui.SolverActivity;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogSolverView
+  {
+  private final View mView;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public DialogSolverView(final SolverActivity act, final DialogSolvers dialog,
+                          int solverOrdinal, int title, int desc, int padding,
+                          int fontSize, LinearLayout.LayoutParams pView,
+                          LinearLayout.LayoutParams pText, LinearLayout.LayoutParams pButt)
+    {
+    mView = act.getLayoutInflater().inflate(R.layout.dialog_solvers_pane, null);
+    mView.setLayoutParams(pView);
+    mView.setPadding(padding,padding,padding,padding);
+
+    TextView titleView = mView.findViewById(R.id.solvers_pane_title);
+    titleView.setText(title);
+    TextView descView = mView.findViewById(R.id.solvers_pane_description);
+    descView.setText(desc);
+
+    titleView.setLayoutParams(pText);
+    Button button = mView.findViewById(R.id.solvers_pane_button);
+
+    button.setOnClickListener( new View.OnClickListener()
+      {
+      @Override
+      public void onClick(View v)
+        {
+        dialog.dismiss();
+        SolvingList list = SolvingList.getSolver(solverOrdinal);
+        OperatingSystemInterface os = act.getInterface();
+        TwistyObject object = act.getObject();
+        SolverAbstract solver = list.create(os,object);
+        ScreenSetupPosition screen = (ScreenSetupPosition)ScreenList.SVER.getScreenClass();
+
+        if( solver!=null )
+          {
+          int[] result = solver.validatePosition(object);
+          if( result[0]>=0 ) solver.solve(screen,result);
+          else screen.displayImpossibleDialog(result,solver.getFaceColors());
+          }
+        else screen.displayErrorDialog(act.getString(R.string.solver_generic_not_implemented));
+        }
+      });
+
+    button.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
+    button.setLayoutParams(pButt);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public View getView()
+    {
+    return mView;
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogSolvers.java b/src/main/java/org/distorted/dialogs/DialogSolvers.java
new file mode 100644
index 00000000..6ffe1a6c
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogSolvers.java
@@ -0,0 +1,101 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2022 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.app.Dialog;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import androidx.fragment.app.FragmentActivity;
+
+import org.distorted.main.R;
+import org.distorted.objectlib.metadata.ListObjects;
+import org.distorted.solverui.SolverActivity;
+import org.distorted.objectlib.solvers.verifiers.SolvingList;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogSolvers extends DialogAbstract
+  {
+  @Override
+  public void onResume()
+    {
+    super.onResume();
+
+    Window window = getDialog().getWindow();
+
+    if( window!=null )
+      {
+      WindowManager.LayoutParams params = window.getAttributes();
+      params.width  = (int)Math.min( mHeight*0.60f,mWidth*0.75f );
+      window.setAttributes(params);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getResource()            { return R.layout.dialog_scrollable_panes; }
+  public int getTitleResource()       { return R.string.solvers; }
+  public boolean hasArgument()        { return true; }
+  public int getPositive()            { return -1; }
+  public int getNegative()            { return -1; }
+  public static String getDialogTag() { return "DialogSolvers"; }
+  public void positiveAction()        { }
+  public void negativeAction()        { }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
+    {
+    int margin= (int)(mHeight*0.010f);
+    int padd  = (int)(mHeight*0.010f);
+    int font  = (int)(mHeight*0.025f);
+
+    LinearLayout layout= view.findViewById(R.id.dialog_scrollable_main_layout);
+    TextView text  = view.findViewById(R.id.dialog_scrollable_message);
+    text.setVisibility(View.GONE);
+
+    DisplayMetrics metrics = new DisplayMetrics();
+    act.getWindowManager().getDefaultDisplay().getMetrics(metrics);
+    float logicalDensity = metrics.density;
+    int px = (int) Math.ceil(10*logicalDensity);
+    ScrollView scroll = view.findViewById(R.id.updates_scroll);
+    scroll.setPadding(px,0,px,px);
+
+    LinearLayout.LayoutParams pV = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
+    pV.setMargins(margin, margin, margin, 0);
+    LinearLayout.LayoutParams pL = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
+    pL.setMargins(margin, margin, margin, margin);
+    LinearLayout.LayoutParams pT = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
+    pT.setMargins(0,0,0,2*margin);
+    LinearLayout.LayoutParams pB = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
+    pB.setMargins(0,2*margin,0,0);
+
+    SolverActivity sact = (SolverActivity) getContext();
+    int objectOrdinal = ListObjects.getObjectIndex(mArgument);
+    int[] solverOrdinals = SolvingList.getSolverOrdinals(objectOrdinal);
+    int len = solverOrdinals==null ? 0 : solverOrdinals.length;
+
+    for(int o=0; o<len; o++ )
+      {
+      int ord = solverOrdinals[o];
+      SolvingList solver = SolvingList.getSolver(ord);
+      int title  = solver.getTitle();
+      int description = solver.getDescription();
+      DialogSolverView pane = new DialogSolverView(sact,this,ord,title,description, padd, font, (o==len-1?pL:pV),pT,pB);
+      layout.addView(pane.getView());
+      }
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogTutorial.java b/src/main/java/org/distorted/dialogs/DialogTutorial.java
new file mode 100644
index 00000000..220210de
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogTutorial.java
@@ -0,0 +1,204 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2023 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import static android.view.View.inflate;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.fragment.app.FragmentActivity;
+
+import org.distorted.main.R;
+import org.distorted.objectlib.json.JsonReader;
+import org.distorted.objects.RubikObject;
+import org.distorted.objects.RubikObjectList;
+import org.distorted.tutorials.TutorialActivity;
+
+import java.io.InputStream;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogTutorial extends DialogAbstract
+  {
+  public static final float PANE_HEIGHT= 0.06f;
+  public static final float TEXT_SIZE  = 0.41f;
+
+  private Dialog mDialog;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public void onResume()
+    {
+    super.onResume();
+
+    Window window = getDialog().getWindow();
+
+    if( window!=null )
+      {
+      WindowManager.LayoutParams params = window.getAttributes();
+      params.width  = (int)Math.min( mHeight*0.67f,mWidth*0.98f );
+      //params.height = (int)Math.min( mHeight*0.85f,mWidth*1.30f );
+      window.setAttributes(params);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getResource()      { return R.layout.dialog_tutorial_single; }
+  public int getTitleResource() { return R.string.tutorials; }
+  public boolean hasArgument()  { return true; }
+  public int getPositive()      { return -1; }
+  public int getNegative()      { return -1; }
+  public void positiveAction()  { }
+  public void negativeAction()  { }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
+    {
+    mDialog = dialog;
+
+    int objectOrdinal = Integer.parseInt(mArgument);
+
+    if( objectOrdinal<0 )
+      {
+      android.util.Log.e("D", "object "+mArgument+" not found");
+      return;
+      }
+
+    RubikObject robject = RubikObjectList.getObject(objectOrdinal);
+    InputStream jsonStream = robject==null ? null : robject.getExtrasStream(act);
+    String[][] tutorials=null;
+
+    if( jsonStream!=null )
+      {
+      try
+        {
+        JsonReader reader = new JsonReader();
+        reader.parseJsonTutorial(jsonStream);
+        tutorials = reader.getTutorials();
+        }
+      catch(Exception ignored) { }
+      }
+
+    if( tutorials!=null )
+      {
+      int paneSize = (int)(mHeight*PANE_HEIGHT);
+      Resources res = act.getResources();
+      String packageName = act.getPackageName();
+
+      LinearLayout layout = view.findViewById(R.id.tutLayout);
+
+      TutorialActivity mact = (TutorialActivity)act;
+      int backgroundC = mact.getVeryDarkColor();
+
+      int colorB = res.getColor(backgroundC);
+      int colorT = res.getColor(R.color.white);
+
+      int numTuts = tutorials.length;
+
+      for( int t=0; t<numTuts; t++)
+        {
+        String[] tutorial = tutorials[t];
+
+        String coun = tutorial[0];
+        String url  = tutorial[1];
+        String desc = tutorial[2];
+        String auth = tutorial[3];
+
+        int countryID = res.getIdentifier(coun, "drawable", packageName);
+
+        View row = createRow(act, countryID, desc, url, auth, paneSize, colorB, colorT, t==(numTuts-1) );
+        layout.addView(row);
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private View createRow(final FragmentActivity act, int countryID, final String desc, final String url,
+                         final String auth, int size, int colorB, int colorT, boolean last)
+    {
+    float textSize = size*TEXT_SIZE;
+    View row = inflate( act, R.layout.dialog_tutorial_row, null);
+
+    LinearLayout layout = row.findViewById(R.id.tutorialLayout);
+    layout.setMinimumHeight(size);
+
+    Button butt = row.findViewById(R.id.tutorialButton);
+    butt.setText(R.string.view);
+    butt.setTextColor(colorT);
+    butt.setBackgroundColor(colorB);
+    butt.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+    butt.setHeight(size);
+
+    final TutorialActivity tact = (TutorialActivity)getContext();
+
+    butt.setOnClickListener( new View.OnClickListener()
+      {
+      @Override
+      public void onClick(View v)
+        {
+        mDialog.dismiss();
+        if( tact!=null ) tact.loadTutorial(url);
+        }
+      });
+
+    ImageView image = row.findViewById(R.id.tutorialCountry);
+    int id = countryID!=0 ? countryID : org.distorted.flags.R.drawable.unknown;
+    image.setImageResource(id);
+
+    TextView author = row.findViewById(R.id.tutorialAuthor);
+    author.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+    author.setText(auth);
+
+    TextView title  = row.findViewById(R.id.tutorialTitle);
+    title.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+    title.setText(desc);
+
+    if( last )
+      {
+      int m = (int)convertDpToPixel(10,act);
+      LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT);
+      params.bottomMargin = m;
+      params.leftMargin   = m;
+      params.rightMargin  = m;
+      layout.setLayoutParams(params);
+      }
+
+    return row;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static float convertDpToPixel(float dp, Context context)
+    {
+    return dp*((float) context.getResources().getDisplayMetrics().densityDpi/DisplayMetrics.DENSITY_DEFAULT);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static String getDialogTag()
+    {
+    return "DialogTutorialSingle";
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogUpdateView.java b/src/main/java/org/distorted/dialogs/DialogUpdateView.java
new file mode 100644
index 00000000..45361df9
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogUpdateView.java
@@ -0,0 +1,261 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2022 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.lang.ref.WeakReference;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import org.distorted.external.RubikFiles;
+import org.distorted.main.R;
+import org.distorted.external.RubikNetwork;
+import org.distorted.external.RubikUpdates;
+import org.distorted.objectlib.json.JsonReader;
+import org.distorted.objects.RubikObjectList;
+
+import static org.distorted.objects.RubikObjectList.SHOW_DOWNLOADED_DEBUG;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogUpdateView implements RubikNetwork.Downloadee
+  {
+  private ImageView mIcon;
+  private RubikUpdates.UpdateInfo mInfo;
+  private ProgressBar mBar;
+  private Button mButton;
+  private TextView mDescription;
+  private WeakReference<Activity> mAct;
+  private boolean mIconSaved;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public View createView(Activity act, RubikUpdates.UpdateInfo info, int fontSize, int padding,
+                         LinearLayout.LayoutParams pView, LinearLayout.LayoutParams pText, LinearLayout.LayoutParams pButt )
+    {
+    mIconSaved=false;
+    mAct = new WeakReference<>(act);
+    mInfo = info;
+    final RubikNetwork.Downloadee downloadee = this;
+    View view = act.getLayoutInflater().inflate(R.layout.dialog_updates_pane, null);
+    TextView title = view.findViewById(R.id.updates_pane_title);
+    title.setText(info.mObjectLongName);
+    mDescription = view.findViewById(R.id.updates_pane_description);
+    mDescription.setText(info.mDescription);
+
+    mIcon = view.findViewById(R.id.updates_pane_image);
+    mIcon.setImageResource(R.drawable.unknown_icon);
+
+    view.setLayoutParams(pView);
+
+    title.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
+    mDescription.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
+
+    title.setLayoutParams(pText);
+    mDescription.setLayoutParams(pText);
+
+    view.setPadding(padding,padding,padding,padding);
+
+    mBar    = view.findViewById(R.id.updates_pane_bar);
+    mButton = view.findViewById(R.id.updates_pane_button);
+
+    if( info.mPercent>=100 )
+      {
+      mBar.setVisibility(View.GONE);
+      mButton.setOnClickListener( new View.OnClickListener()
+        {
+        @Override
+        public void onClick(View v)
+          {
+          startDownload();
+          RubikNetwork network = RubikNetwork.getInstance();
+          network.downloadJSON(info,downloadee);
+          }
+        });
+
+      mButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
+      mButton.setLayoutParams(pButt);
+      mBar.setLayoutParams(pButt);
+      }
+    else
+      {
+      mButton.setVisibility(View.GONE);
+      mBar.setLayoutParams(pButt);
+      mBar.setProgress(info.mPercent);
+      }
+
+    return view;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void setIcon(Bitmap icon, boolean downloaded)
+    {
+    mIcon.setImageBitmap(icon);
+
+    if( downloaded )
+      {
+      String name = mInfo.mObjectShortName + ".png";
+      Activity act = mAct.get();
+      RubikFiles files = RubikFiles.getInstance();
+      mIconSaved = files.saveIcon(act,mInfo.mIcon, name);
+
+      if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "Saving icon "+name+" to a file "+mIconSaved);
+      }
+    else
+      {
+      mIconSaved = true;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void startDownload()
+    {
+    mDescription.setText(R.string.downloading);
+    mBar.setProgress(20);
+    mButton.setVisibility(View.GONE);
+    mBar.setVisibility(View.VISIBLE);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void displayError(int error)
+    {
+    Activity act = mAct.get();
+
+    act.runOnUiThread(new Runnable()
+      {
+      @Override
+      public void run()
+        {
+        mDescription.setTextColor(Color.parseColor("#ff0000"));
+        mDescription.setText(error);
+        }
+      });
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void makeProgress(int progress, int message)
+    {
+    Activity act = mAct.get();
+
+    act.runOnUiThread(new Runnable()
+      {
+      @Override
+      public void run()
+        {
+        mBar.setProgress(progress);
+        mDescription.setText(message);
+        }
+      });
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void jsonDownloaded()
+    {
+    if( mInfo.mUpdateObject && mInfo.mObjectStream==null )
+      {
+      displayError(R.string.networkError);
+      }
+    else
+      {
+      makeProgress(50,R.string.installing);
+      RubikFiles files = RubikFiles.getInstance();
+      boolean oSuccess, eSuccess=false;
+
+      if( mInfo.mObjectStream!=null )
+        {
+        String objectName = mInfo.mObjectShortName + "_object.json";
+        Activity act = mAct.get();
+        oSuccess = files.saveFile(act,mInfo.mObjectStream, objectName);
+
+        if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "Saving JSON "+objectName+" to a file "+oSuccess);
+
+        try
+          {
+          File file = new File(act.getFilesDir(), objectName);
+          InputStream stream = new FileInputStream(file);
+          JsonReader reader = new JsonReader();
+          reader.parseJsonFileMetadata(stream);
+          stream.close();
+
+          mInfo.mNumScrambles = reader.getNumScrambles();
+          mInfo.mPrice        = reader.getPrice();
+          float diff          = reader.getDifficulty();
+          int category        = reader.getCategory();
+          int year            = reader.getYearOfInvention();
+          String author       = reader.getAuthor();
+          boolean adjColors   = reader.getAdjustableColors();
+
+          if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "Read from JSON numScrambles="+mInfo.mNumScrambles+" price="+mInfo.mPrice);
+
+          if( mInfo.mExtrasStream!=null )
+            {
+            String name = mInfo.mObjectShortName + "_extras.json";
+            eSuccess = files.saveFile(act,mInfo.mExtrasStream, name);
+
+            if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "Saving Extras "+name+" to a file "+eSuccess);
+            }
+
+          if( mIconSaved || oSuccess || eSuccess )
+            {
+            makeProgress(75,R.string.configuring);
+
+            if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "1");
+
+            boolean success = RubikObjectList.addDownloadedObject(act, mInfo.mObjectShortName, mInfo.mNumScrambles, mInfo.mPrice, mInfo.mObjectMinorVersion,
+                                                                  mInfo.mExtrasMinorVersion, mIconSaved, oSuccess, eSuccess, diff, category, year, author,adjColors);
+            if( success )
+              {
+              if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "2");
+
+              RubikNetwork network = RubikNetwork.getInstance();
+              network.updateDone(mInfo.mObjectShortName);
+
+              if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "3");
+
+              makeProgress(100,R.string.success);
+
+              if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "4");
+              }
+            else
+              {
+              displayError(R.string.saveError);
+              }
+            }
+          else
+            {
+            displayError(R.string.saveError);
+            }
+          }
+        catch(Exception ex)
+          {
+          displayError(R.string.saveError);
+          files.deleteJsonObject(act,mInfo.mObjectShortName);
+          if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "Error reading numScrambles");
+          }
+        }
+      }
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogUpdates.java b/src/main/java/org/distorted/dialogs/DialogUpdates.java
new file mode 100644
index 00000000..5f7d8976
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogUpdates.java
@@ -0,0 +1,206 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2022 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import java.util.ArrayList;
+
+import android.app.Dialog;
+import android.graphics.Bitmap;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.fragment.app.FragmentActivity;
+
+import org.distorted.main.R;
+import org.distorted.external.RubikNetwork;
+import org.distorted.external.RubikUpdates;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogUpdates extends DialogAbstract implements RubikNetwork.IconReceiver, RubikNetwork.Updatee
+  {
+  private TextView mText;
+  private LinearLayout mLayout;
+  private int mMargin, mSize, mFontSize, mPadding;
+  private ArrayList<DialogUpdateView> mPanes;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public void onResume()
+    {
+    super.onResume();
+
+    Window window = getDialog().getWindow();
+
+    if( window!=null )
+      {
+      WindowManager.LayoutParams params = window.getAttributes();
+      params.width  = (int)Math.min( mHeight*0.65f,mWidth*0.98f );
+      window.setAttributes(params);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getResource()            { return R.layout.dialog_scrollable_panes; }
+  public int getTitleResource()       { return R.string.updates; }
+  public boolean hasArgument()        { return false; }
+  public int getPositive()            { return R.string.ok; }
+  public int getNegative()            { return -1; }
+  public void positiveAction()        { }
+  public void negativeAction()        { }
+  public static String getDialogTag() { return "DialogUpdates"; }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
+    {
+    if( mPanes==null ) mPanes = new ArrayList<>();
+
+    int minH  = (int)(mHeight*0.25f);
+    mMargin   = (int)(mHeight*0.01f);
+    mSize     = (int)(mHeight*0.14f);
+    mFontSize = (int)(mHeight*0.02f);
+    mPadding  = (int)(mHeight*0.01f);
+
+    mLayout= view.findViewById(R.id.dialog_scrollable_main_layout);
+    mText  = view.findViewById(R.id.dialog_scrollable_message);
+    mText.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
+    mText.setText( act.getString(R.string.downloading) );
+
+    mLayout.setMinimumHeight(minH);
+    mText.setMinimumHeight(minH);
+    view.setMinimumHeight(minH);
+
+    RubikNetwork network = RubikNetwork.getInstance();
+    network.signUpForUpdates(this);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void receiveUpdate(RubikUpdates updates, FragmentActivity act)
+    {
+    int numC = updates.getCompletedNumber();
+    int numS = updates.getStartedNumber();
+
+    if( numC+numS<=0 )
+      {
+      mText.setText(act.getString(R.string.no_updates));
+      }
+    else
+      {
+      int textH = (int)(mSize*0.27f);
+      int buttH = (int)(mSize*0.35f);
+
+      LinearLayout.LayoutParams pV = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, mSize );
+      pV.setMargins(mMargin, mMargin, mMargin, 0);
+      LinearLayout.LayoutParams pL = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, mSize );
+      pL.setMargins(mMargin, mMargin, mMargin, mMargin);
+      LinearLayout.LayoutParams pT = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, textH );
+      LinearLayout.LayoutParams pB = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, buttH );
+
+      mText.setVisibility(View.GONE);
+
+      if( mLayout!=null )
+        {
+        for(int i=0; i<numC; i++)
+          {
+          RubikUpdates.UpdateInfo info = updates.getCompletedUpdate(i);
+          DialogUpdateView rubikView = new DialogUpdateView();
+          View pane = rubikView.createView(act,info,mFontSize,mPadding,( (numS==0 && i==numC-1)?pL:pV),pT,pB);
+          mLayout.addView(pane);
+          mPanes.add(rubikView);
+          }
+
+        for(int i=0; i<numS; i++)
+          {
+          RubikUpdates.UpdateInfo info = updates.getStartedUpdate(i);
+          DialogUpdateView rubikView = new DialogUpdateView();
+          View pane = rubikView.createView(act,info,mFontSize,mPadding,(i==numS-1?pL:pV),pT,pB);
+          mLayout.addView(pane);
+          mPanes.add(rubikView);
+          }
+
+        RubikNetwork network = RubikNetwork.getInstance();
+        network.downloadIcons(act,this);
+        }
+      else
+        {
+        android.util.Log.e("D", "mainLayout NULL");
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void receiveUpdate(RubikUpdates updates)
+    {
+    FragmentActivity act = getActivity();
+
+    if( act!=null )
+      {
+      act.runOnUiThread(new Runnable()
+        {
+        @Override
+        public void run()
+          {
+          receiveUpdate(updates,act);
+          }
+        });
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void errorUpdate()
+    {
+    FragmentActivity act = getActivity();
+    if( act!=null ) mText.setText(act.getString(R.string.networkError));
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void objectDownloaded(String shortName)
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getType()
+    {
+    return 1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void iconDownloaded(int ordinal, Bitmap icon, boolean downloaded)
+    {
+    FragmentActivity act = getActivity();
+
+    if( act!=null )
+      {
+      act.runOnUiThread(new Runnable()
+        {
+        @Override
+        public void run()
+          {
+          DialogUpdateView view = mPanes.get(ordinal);
+          if( view!=null ) view.setIcon(icon,downloaded);
+          }
+        });
+      }
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogAbandon.java b/src/main/java/org/distorted/dialogs/RubikDialogAbandon.java
deleted file mode 100644
index fa85c75f..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogAbandon.java
+++ /dev/null
@@ -1,48 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2020 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.app.Dialog;
-import android.view.View;
-import android.widget.TextView;
-
-import androidx.fragment.app.FragmentActivity;
-
-import org.distorted.main.R;
-import org.distorted.playui.PlayActivity;
-import org.distorted.playui.ScreenList;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogAbandon extends RubikDialogAbstract
-  {
-  public int getResource()      { return R.layout.dialog_message; }
-  public int getTitleResource() { return -1; }
-  public boolean hasArgument()  { return false; }
-  public int getPositive()      { return R.string.yes; }
-  public int getNegative()      { return R.string.no; }
-  public void negativeAction()  { }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void positiveAction()
-    {
-    final PlayActivity act = (PlayActivity)getContext();
-    ScreenList.goBack(act);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
-    {
-    TextView mess = view.findViewById(R.id.dialog_message);
-    mess.setText(R.string.abandon_solve);
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogAbout.java b/src/main/java/org/distorted/dialogs/RubikDialogAbout.java
deleted file mode 100644
index 15476bd0..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogAbout.java
+++ /dev/null
@@ -1,192 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2022 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.fragment.app.FragmentActivity;
-
-import org.distorted.main.BuildConfig;
-import org.distorted.main.R;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogAbout extends RubikDialogAbstract
-  {
-  private static final String WHATS_NEW =
-      "1. Fix for 4x4 Penrose - now it correctly detects all solved states.\n" +
-      "2. Sorry for lack of activity lately - I've been busy with private stuff. Now I'm resuming development! \n\nNext: algorithmic solvers";
-
-  private static final String WHATS_COMING =
-      "1. Algorithmic solvers. (sub-optimal solvers for larger puzzles such as the 4x4)\n" +
-      "2. Support for sticker modes (Tartan Cube, Shepherd's Cube, etc).\n" +
-      "3. iOS version (no time for this, anyone can help? Code is open-source)\n" +
-      "4. More objects:\n    - Ghost Cubes\n    - more Mixups\n    - more Barrels\n";
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public void onResume()
-    {
-    super.onResume();
-
-    Window window = getDialog().getWindow();
-
-    if( window!=null )
-      {
-      WindowManager.LayoutParams params = window.getAttributes();
-      params.width  = (int)Math.min( mHeight*0.60f,mWidth*0.90f );
-      window.setAttributes(params);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getResource()      { return R.layout.dialog_about; }
-  public int getTitleResource() { return PARAMETRIC_TITLE; }
-  public boolean hasArgument()  { return true; }
-  public int getPositive()      { return R.string.ok; }
-  public int getNegative()      { return -1; }
-  public void positiveAction()  { }
-  public void negativeAction()  { }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private String findCurrentVersion(Activity act)
-    {
-    String version;
-    try
-      {
-      PackageInfo pInfo = act.getPackageManager().getPackageInfo( act.getPackageName(), 0);
-      version= pInfo.versionName;
-      }
-    catch (PackageManager.NameNotFoundException e)
-      {
-      version= "unknown";
-      }
-
-    return version;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  String getTitleString(FragmentActivity fact)
-    {
-    Resources res= getResources();
-    Activity act= (Activity) getContext();
-    String version= act!=null ? findCurrentVersion(act) : "unknown";
-    return res.getString(R.string.ab_placeholder,version);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
-    {
-    int width = (int)Math.min( mHeight*0.60f,mWidth*0.90f );
-    int sS= (int)(width*0.043f);
-    int bS= (int)(width*0.060f);
-
-    TextView shaV = view.findViewById(R.id.about_share_string);
-    TextView emaV = view.findViewById(R.id.about_mail_string);
-    TextView addV = view.findViewById(R.id.about_mail_address);
-
-    TextView newV = view.findViewById(R.id.about_new_message);
-    newV.setText(WHATS_NEW);
-    TextView comV = view.findViewById(R.id.about_coming_message);
-    comV.setText(WHATS_COMING);
-
-    LinearLayout layoutShare = view.findViewById(R.id.about_share_layout);
-
-    layoutShare.setOnClickListener(new View.OnClickListener()
-       {
-       @Override
-       public void onClick(View v)
-         {
-         share(act);
-         }
-       });
-
-    LinearLayout layoutEmail = view.findViewById(R.id.about_email_layout);
-
-    layoutEmail.setOnClickListener(new View.OnClickListener()
-       {
-       @Override
-       public void onClick(View v)
-         {
-         email(act);
-         }
-       });
-
-    shaV.setTextSize(TypedValue.COMPLEX_UNIT_PX, bS);
-    emaV.setTextSize(TypedValue.COMPLEX_UNIT_PX, bS);
-    addV.setTextSize(TypedValue.COMPLEX_UNIT_PX, sS);
-    newV.setTextSize(TypedValue.COMPLEX_UNIT_PX, sS);
-    comV.setTextSize(TypedValue.COMPLEX_UNIT_PX, sS);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void share(FragmentActivity act)
-    {
-    Resources res = act.getResources();
-    String name = res.getString(R.string.app_name);
-
-    Intent intent = new Intent();
-    intent.setAction(Intent.ACTION_SEND);
-    intent.putExtra(Intent.EXTRA_TEXT,
-    name+": https://play.google.com/store/apps/details?id=" + BuildConfig.APPLICATION_ID);
-    intent.setType("text/plain");
-
-    if (intent.resolveActivity(act.getPackageManager()) != null)
-      {
-      startActivity(intent);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void email(FragmentActivity act)
-    {
-    Resources res = act.getResources();
-    String[] email = { res.getString(R.string.email_address) };
-    String version = findCurrentVersion((Activity)act);
-    String name = res.getString(R.string.app_name);
-
-    Intent intent = new Intent(Intent.ACTION_SENDTO);
-    intent.setData(Uri.parse("mailto:")); // only email apps should handle this
-    intent.putExtra(Intent.EXTRA_EMAIL, email);
-    intent.putExtra(Intent.EXTRA_SUBJECT, name+" "+version);
-
-    if (intent.resolveActivity(act.getPackageManager()) != null)
-      {
-      startActivity(intent);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static String getDialogTag()
-    {
-    return "DialogAbout";
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogAbstract.java b/src/main/java/org/distorted/dialogs/RubikDialogAbstract.java
deleted file mode 100644
index e527f0dd..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogAbstract.java
+++ /dev/null
@@ -1,180 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2023 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.os.Bundle;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.Window;
-import android.widget.Button;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.AlertDialog;
-import androidx.appcompat.app.AppCompatDialogFragment;
-import androidx.fragment.app.FragmentActivity;
-
-import org.distorted.helpers.BaseActivity;
-import org.distorted.main.MainActivity;
-import org.distorted.main.R;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-abstract public class RubikDialogAbstract extends AppCompatDialogFragment
-  {
-  static final int PARAMETRIC_TITLE = -10000;
-
-  protected float mTitleSize, mButSize, mTextSize;
-  protected int mWidth, mHeight;
-  protected String mArgument;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  abstract int getResource();
-  abstract int getTitleResource();
-  abstract int getPositive();
-  abstract int getNegative();
-  abstract boolean hasArgument();
-  abstract void positiveAction();
-  abstract void negativeAction();
-  abstract void prepareBody(Dialog dialog, View view, FragmentActivity act, float size);
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  String getTitleString(FragmentActivity act)
-    {
-    return "";
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @NonNull
-  @Override
-  public Dialog onCreateDialog(Bundle savedInstanceState)
-    {
-    FragmentActivity act = getActivity();
-    LayoutInflater inflater = act.getLayoutInflater();
-    AlertDialog.Builder builder = new AlertDialog.Builder(act);
-
-    DisplayMetrics displaymetrics = new DisplayMetrics();
-    act.getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
-    mWidth    = displaymetrics.widthPixels;
-    mHeight   = displaymetrics.heightPixels;
-    mTitleSize= mHeight*0.032f;
-    mButSize  = mHeight*0.040f;
-    mTextSize = mHeight*0.025f;
-
-    if( hasArgument() )
-      {
-      Bundle args = getArguments();
-
-      try
-        {
-        mArgument = args!=null ? args.getString("argument") : "";
-        }
-      catch(Exception e)
-        {
-        mArgument = "";
-        }
-      }
-    else mArgument = "";
-
-    final View view = inflater.inflate(getResource(), null);
-    builder.setView(view);
-    builder.setCancelable(true);
-
-    int title = getTitleResource();
-    if( title>=0 || title==PARAMETRIC_TITLE )
-      {
-      TextView tv = (TextView) inflater.inflate(R.layout.dialog_title, null);
-      tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTitleSize);
-
-      if( title>=0 )
-        {
-        tv.setText(title);
-        }
-      else
-        {
-        String titleString = getTitleString(act);
-        tv.setText(titleString);
-        }
-
-      builder.setCustomTitle(tv);
-      }
-
-    int positive = getPositive();
-    if( positive>=0 )
-      {
-      builder.setPositiveButton( positive, new DialogInterface.OnClickListener()
-        {
-        @Override
-        public void onClick(DialogInterface dialog, int which)
-          {
-          positiveAction();
-          }
-        });
-      }
-
-    int negative = getNegative();
-    if( negative>=0 )
-      {
-      builder.setNegativeButton( negative, new DialogInterface.OnClickListener()
-        {
-        @Override
-        public void onClick(DialogInterface dialog, int which)
-          {
-          negativeAction();
-          }
-        });
-      }
-
-    Dialog dialog = builder.create();
-    dialog.setCanceledOnTouchOutside(false);
-
-    prepareBody(dialog,view,act,mTextSize);
-
-    Window window = dialog.getWindow();
-
-    if( window!=null )
-      {
-      BaseActivity bact = (BaseActivity) act;
-      int m = bact.getMediumColor();
-      window.setBackgroundDrawableResource(m);
-      window.getDecorView().setSystemUiVisibility(MainActivity.FLAGS);
-      }
-
-    dialog.setOnShowListener(new DialogInterface.OnShowListener()
-      {
-      @Override
-      public void onShow(DialogInterface dialog)
-        {
-        if( positive>=0 )
-          {
-          Button btnPositive = ((AlertDialog)dialog).getButton(Dialog.BUTTON_POSITIVE);
-          btnPositive.setTextSize(TypedValue.COMPLEX_UNIT_PX, mButSize);
-          }
-        if( negative>=0 )
-          {
-          Button btnNegative = ((AlertDialog)dialog).getButton(Dialog.BUTTON_NEGATIVE);
-          btnNegative.setTextSize(TypedValue.COMPLEX_UNIT_PX, mButSize);
-          }
-        }
-      });
-
-    return dialog;
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogBandagedDelete.java b/src/main/java/org/distorted/dialogs/RubikDialogBandagedDelete.java
deleted file mode 100644
index 65e2e2c0..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogBandagedDelete.java
+++ /dev/null
@@ -1,55 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2022 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.app.Dialog;
-import android.util.TypedValue;
-import android.view.View;
-import android.widget.TextView;
-
-import androidx.fragment.app.FragmentActivity;
-
-import org.distorted.bandaged.BandagedActivity;
-import org.distorted.main.R;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogBandagedDelete extends RubikDialogAbstract
-  {
-  public int getResource()      { return R.layout.dialog_delete_object; }
-  public int getTitleResource() { return R.string.delete_object; }
-  public boolean hasArgument()  { return true; }
-  public int getPositive()      { return R.string.yes; }
-  public int getNegative()      { return R.string.no; }
-  public void negativeAction()  { }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void positiveAction()
-    {
-    BandagedActivity bact = (BandagedActivity)getContext();
-    if( bact!=null ) bact.deleteObject(mArgument);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
-    {
-    TextView save = view.findViewById(R.id.delete_object_text);
-    save.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static String getDialogTag()
-    {
-    return "DialogBandagedDelete";
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogBandagedSave.java b/src/main/java/org/distorted/dialogs/RubikDialogBandagedSave.java
deleted file mode 100644
index e708affd..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogBandagedSave.java
+++ /dev/null
@@ -1,60 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2022 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.app.Dialog;
-import android.util.TypedValue;
-import android.view.View;
-import android.widget.TextView;
-
-import androidx.fragment.app.FragmentActivity;
-
-import org.distorted.bandaged.BandagedActivity;
-import org.distorted.bandaged.BandagedRenderer;
-import org.distorted.main.R;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogBandagedSave extends RubikDialogAbstract
-  {
-  public int getResource()      { return R.layout.dialog_save_object; }
-  public int getTitleResource() { return R.string.save_object; }
-  public boolean hasArgument()  { return false; }
-  public int getPositive()      { return R.string.yes; }
-  public int getNegative()      { return R.string.no; }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void positiveAction()
-    {
-    BandagedActivity bact = (BandagedActivity)getContext();
-
-    if( bact!=null )
-      {
-      BandagedRenderer rend = bact.getRenderer();
-      rend.saveObject();
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void negativeAction()
-    {
-
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
-    {
-    TextView save = view.findViewById(R.id.save_object_text);
-    save.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogCreatorView.java b/src/main/java/org/distorted/dialogs/RubikDialogCreatorView.java
deleted file mode 100644
index 49b0f07d..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogCreatorView.java
+++ /dev/null
@@ -1,70 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2023 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.util.TypedValue;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import org.distorted.main.MainActivity;
-import org.distorted.main.R;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogCreatorView
-  {
-  private final View mView;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public RubikDialogCreatorView(final MainActivity act, final RubikDialogCreators dialog,
-                                final int index, int icon, int title, int desc, int padding, int fontSize,
-                                LinearLayout.LayoutParams pView, LinearLayout.LayoutParams pText, LinearLayout.LayoutParams pButt )
-    {
-    mView = act.getLayoutInflater().inflate(R.layout.dialog_creators_pane, null);
-    mView.setLayoutParams(pView);
-    mView.setPadding(padding,padding,padding,padding);
-
-    TextView titleView = mView.findViewById(R.id.creators_pane_title);
-    titleView.setText(title);
-    TextView descView = mView.findViewById(R.id.creators_pane_description);
-    descView.setText(desc);
-
-    titleView.setLayoutParams(pText);
-
-    ImageView iconView = mView.findViewById(R.id.creators_pane_image);
-    iconView.setImageResource(icon);
-
-    Button button = mView.findViewById(R.id.creators_pane_button);
-
-    button.setOnClickListener( new View.OnClickListener()
-      {
-      @Override
-      public void onClick(View v)
-        {
-        dialog.dismiss();
-        act.switchToBandagedCreator(index);
-        }
-      });
-
-    button.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
-    button.setLayoutParams(pButt);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public View getView()
-    {
-    return mView;
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogCreators.java b/src/main/java/org/distorted/dialogs/RubikDialogCreators.java
deleted file mode 100644
index 32648bd3..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogCreators.java
+++ /dev/null
@@ -1,140 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2023 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.app.Dialog;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.fragment.app.FragmentActivity;
-
-import org.distorted.main.MainActivity;
-import org.distorted.main.R;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogCreators extends RubikDialogAbstract
-  {
-  private enum BandagedObjectDescription
-    {
-    CUBOID     ( org.distorted.objectlib.R.drawable.cu_232, R.string.creator_cuboid_title  , R.string.creator_cuboid_desc   ),
-    PYRAMINX   ( org.distorted.objectlib.R.drawable.pyra_3, R.string.creator_pyraminx_title, R.string.creator_pyraminx_desc ),
-    MEGAMINX   ( org.distorted.objectlib.R.drawable.mega_3, R.string.creator_megaminx_title, R.string.creator_megaminx_desc ),
-    OCTAHEDRON ( org.distorted.objectlib.R.drawable.diam_2, R.string.creator_octahedron_title, R.string.creator_octahedron_desc ),
-    SKEWB      ( org.distorted.objectlib.R.drawable.skew_2, R.string.creator_skewb_title, R.string.creator_skewb_desc ),
-    ;
-
-    public static final int NUM_OBJECTS = values().length;
-    private static final BandagedObjectDescription[] objects;
-
-    final int mIcon, mTitle, mDescription;
-
-    BandagedObjectDescription(int icon, int title, int descripton)
-      {
-      mIcon        = icon;
-      mTitle       = title;
-      mDescription = descripton;
-      }
-
-    static
-      {
-      int i=0;
-      objects = new BandagedObjectDescription[NUM_OBJECTS];
-      for( BandagedObjectDescription object: BandagedObjectDescription.values() ) objects[i++] = object;
-      }
-
-    ////////////////////////////////////////////////////////////////////////
-
-    static int getIcon(int ordinal)
-      {
-      return objects[ordinal].mIcon;
-      }
-
-    ////////////////////////////////////////////////////////////////////////
-
-    static int getTitle(int ordinal)
-      {
-      return objects[ordinal].mTitle;
-      }
-
-    ////////////////////////////////////////////////////////////////////////
-
-    static int getDescription(int ordinal)
-      {
-      return objects[ordinal].mDescription;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public void onResume()
-    {
-    super.onResume();
-
-    Window window = getDialog().getWindow();
-
-    if( window!=null )
-      {
-      WindowManager.LayoutParams params = window.getAttributes();
-      params.width  = (int)Math.min( mHeight*0.65f,mWidth*0.98f );
-      params.height = (int)( mHeight*0.90f);
-      window.setAttributes(params);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getResource()            { return R.layout.dialog_scrollable_panes; }
-  public int getTitleResource()       { return R.string.creators; }
-  public boolean hasArgument()        { return false; }
-  public int getPositive()            { return R.string.ok; }
-  public int getNegative()            { return -1; }
-  public void positiveAction()        { }
-  public void negativeAction()        { }
-  public static String getDialogTag() { return "DialogCreators"; }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
-    {
-    int margin= (int)(mHeight*0.010f);
-    int padd  = (int)(mHeight*0.010f);
-    int font  = (int)(mHeight*0.025f);
-
-    LinearLayout layout= view.findViewById(R.id.dialog_scrollable_main_layout);
-    TextView text  = view.findViewById(R.id.dialog_scrollable_message);
-    text.setVisibility(View.GONE);
-
-    LinearLayout.LayoutParams pV = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
-    pV.setMargins(margin, margin, margin, 0);
-    LinearLayout.LayoutParams pL = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
-    pL.setMargins(margin, margin, margin, margin);
-    LinearLayout.LayoutParams pT = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
-    pT.setMargins(0,0,0,2*margin);
-    LinearLayout.LayoutParams pB = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
-    pB.setMargins(0,2*margin,0,0);
-
-    int num = BandagedObjectDescription.NUM_OBJECTS;
-    MainActivity ract = (MainActivity) getContext();
-
-    for(int i=0; i<num; i++)
-      {
-      int icon        = BandagedObjectDescription.getIcon(i);
-      int title       = BandagedObjectDescription.getTitle(i);
-      int description = BandagedObjectDescription.getDescription(i);
-      RubikDialogCreatorView pane = new RubikDialogCreatorView(ract,this,i,icon,title,description, padd, font, (i==num-1?pL:pV),pT,pB);
-      layout.addView(pane.getView());
-      }
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogError.java b/src/main/java/org/distorted/dialogs/RubikDialogError.java
deleted file mode 100644
index b8d5a862..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogError.java
+++ /dev/null
@@ -1,54 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2020 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.app.Dialog;
-import android.util.TypedValue;
-import android.view.View;
-import android.widget.TextView;
-
-import androidx.fragment.app.FragmentActivity;
-
-import org.distorted.main.R;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogError extends RubikDialogAbstract
-  {
-  public int getResource()      { return R.layout.dialog_error; }
-  public int getTitleResource() { return R.string.opengl_error; }
-  public boolean hasArgument()  { return false; }
-  public int getPositive()      { return R.string.ok; }
-  public int getNegative()      { return -1; }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void positiveAction()
-    {
-    final FragmentActivity act = getActivity();
-    if( act!=null ) act.finish();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void negativeAction()
-    {
-
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
-    {
-    TextView text = view.findViewById(R.id.error_string);
-    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
-    text.setText(R.string.opengl_error_text);
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogExit.java b/src/main/java/org/distorted/dialogs/RubikDialogExit.java
deleted file mode 100644
index d766c5d4..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogExit.java
+++ /dev/null
@@ -1,39 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2023 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.view.View;
-
-import androidx.fragment.app.FragmentActivity;
-
-import org.distorted.main.R;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogExit extends RubikDialogAbstract
-  {
-  public int getResource()      { return R.layout.exit_app; }
-  public int getTitleResource() { return -1; }
-  public boolean hasArgument()  { return false; }
-  public int getPositive()      { return R.string.yes; }
-  public int getNegative()      { return R.string.no; }
-  public void negativeAction()  { }
-  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size) { }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void positiveAction()
-    {
-    final Activity act = (Activity)getContext();
-    if( act!=null ) act.finish();
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogMessage.java b/src/main/java/org/distorted/dialogs/RubikDialogMessage.java
deleted file mode 100644
index 0cfcf2e4..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogMessage.java
+++ /dev/null
@@ -1,46 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2024 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.app.Dialog;
-import android.view.View;
-import android.widget.TextView;
-
-import androidx.fragment.app.FragmentActivity;
-
-import org.distorted.main.R;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogMessage extends RubikDialogAbstract
-  {
-  public int getResource()      { return R.layout.dialog_message; }
-  public int getTitleResource() { return -1; }
-  public boolean hasArgument()  { return true; }
-  public int getPositive()      { return R.string.ok; }
-  public int getNegative()      { return -1; }
-  public void negativeAction()  { }
-  public void positiveAction()  { }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
-    {
-    TextView mess = view.findViewById(R.id.dialog_message);
-    mess.setText(mArgument);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static String getDialogTag()
-    {
-    return "DialogBandageMessage";
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogNewRecord.java b/src/main/java/org/distorted/dialogs/RubikDialogNewRecord.java
deleted file mode 100644
index 93ac47fa..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogNewRecord.java
+++ /dev/null
@@ -1,87 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2020 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.app.Dialog;
-import android.os.Bundle;
-import androidx.fragment.app.FragmentActivity;
-
-import android.util.TypedValue;
-import android.view.View;
-import android.widget.TextView;
-
-import org.distorted.main.R;
-import org.distorted.external.RubikScores;
-import org.distorted.playui.PlayActivity;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogNewRecord extends RubikDialogAbstract
-  {
-  public int getResource()      { return R.layout.dialog_new_record; }
-  public int getTitleResource() { return R.string.new_record; }
-  public boolean hasArgument()  { return true; }
-  public int getPositive()      { return R.string.yes; }
-  public int getNegative()      { return R.string.no; }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void positiveAction()
-    {
-    RubikScores scores = RubikScores.getInstance();
-    String name = scores.getName();
-    Bundle bundle = new Bundle();
-    PlayActivity act = (PlayActivity)getActivity();
-
-    if( act!=null )
-      {
-      if(name.length()>0)
-        {
-        bundle.putString("argument", "true");
-        RubikDialogScores scoresDiag = new RubikDialogScores();
-        scoresDiag.setArguments(bundle);
-        scoresDiag.show(act.getSupportFragmentManager(), null);
-        }
-      else
-        {
-        bundle.putString("argument", name);
-        RubikDialogSetName nameDiag = new RubikDialogSetName();
-        nameDiag.setArguments(bundle);
-        nameDiag.show(act.getSupportFragmentManager(), null);
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void negativeAction()
-    {
-    PlayActivity act = (PlayActivity)getActivity();
-    if( act!=null ) act.finish();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
-    {
-    TextView text = view.findViewById(R.id.new_record_time);
-    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
-    text.setText(mArgument);
-    TextView submit = view.findViewById(R.id.new_record_submit);
-    submit.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static String getDialogTag()
-    {
-    return "DialogNewRecord";
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogPattern.java b/src/main/java/org/distorted/dialogs/RubikDialogPattern.java
deleted file mode 100644
index 5b07e020..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogPattern.java
+++ /dev/null
@@ -1,155 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2023 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.app.Dialog;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.ExpandableListView;
-
-import androidx.fragment.app.FragmentActivity;
-
-import org.distorted.main.R;
-import org.distorted.objectlib.main.ObjectControl;
-import org.distorted.objectlib.patterns.RubikPattern;
-import org.distorted.objectlib.patterns.RubikPatternList;
-import org.distorted.objects.RubikObject;
-import org.distorted.objects.RubikObjectList;
-import org.distorted.patternui.PatternActivity;
-import org.distorted.patternui.ScreenList;
-import org.distorted.patternui.ScreenPattern;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogPattern extends RubikDialogAbstract
-  {
-  private ExpandableListView mListView;
-  private int mPatternOrdinal, mPos;
-  private int mExpandedGroup;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public void onResume()
-    {
-    super.onResume();
-
-    Window window = getDialog().getWindow();
-
-    if( window!=null )
-      {
-      WindowManager.LayoutParams params = window.getAttributes();
-      params.width  = (int)Math.min( mHeight*0.65f,mWidth*0.98f );
-      //params.height = (int)Math.min( mHeight*0.80f,mWidth*1.30f );
-      window.setAttributes(params);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getResource()      { return R.layout.dialog_pattern_single; }
-  public int getTitleResource() { return R.string.choose_pattern; }
-  public boolean hasArgument()  { return true; }
-  public int getPositive()      { return -1; }
-  public int getNegative()      { return -1; }
-  public void positiveAction()  { }
-  public void negativeAction()  { }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
-    {
-    int objectOrdinal = Integer.parseInt(mArgument);
-
-    if( objectOrdinal<0 )
-      {
-      android.util.Log.e("D", "object "+mArgument+" not found");
-      return;
-      }
-
-    RubikObject object = RubikObjectList.getObject(objectOrdinal);
-    int objectIndex = object==null ? -1 : object.getIndex();
-
-    mPatternOrdinal = RubikPatternList.getOrdinal(objectIndex);
-
-    if( mPatternOrdinal<0 )
-      {
-      android.util.Log.e("D", "patterns for object "+mArgument+" not found");
-      return;
-      }
-
-    android.util.Log.e("D", "prepareBody: object "+mArgument);
-
-    final PatternActivity pact = (PatternActivity)getContext();
-    int width = pact!=null ? pact.getScreenWidthInPixels() : 100;
-    final ObjectControl control = pact!=null ? pact.getControl() : null;
-
-    RubikPattern pattern = RubikPattern.getInstance();
-    mExpandedGroup = pattern.recallExpanded(mPatternOrdinal);
-
-    mListView = view.findViewById(R.id.patternListView);
-    RubikDialogPatternListAdapter listAdapter = new RubikDialogPatternListAdapter(act,mPatternOrdinal,width);
-    mListView.setAdapter(listAdapter);
-
-    if( mExpandedGroup>=0 ) mListView.expandGroup(mExpandedGroup);
-
-    int visible = pattern.recallVisiblePos(mPatternOrdinal);
-    mListView.setSelectionFromTop(visible,0);
-
-    mListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener()
-      {
-      @Override
-      public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id)
-        {
-        RubikPattern pattern = RubikPattern.getInstance();
-        int[][] moves = pattern.reInitialize(mPatternOrdinal, groupPosition, childPosition);
-        if( control!=null ) control.initializeObject(moves);
-
-        ScreenPattern state = (ScreenPattern) ScreenList.PATT.getScreenClass();
-        state.setPattern(pact, mPatternOrdinal, groupPosition, childPosition);
-
-        rememberState();
-        dismiss();
-
-        return false;
-        }
-      });
-
-    mListView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener()
-      {
-      @Override
-      public void onGroupExpand(int groupPosition)
-        {
-        if(mExpandedGroup!=-1 && groupPosition!=mExpandedGroup)
-          {
-          mListView.collapseGroup(mExpandedGroup);
-          }
-
-        mExpandedGroup = groupPosition;
-        }
-      });
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void rememberState()
-    {
-    RubikPattern pattern = RubikPattern.getInstance();
-    pattern.rememberState(mPatternOrdinal,mPos,mListView.getFirstVisiblePosition(),mExpandedGroup);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static String getDialogTag()
-    {
-    return "DialogPatternSingle";
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogPatternListAdapter.java b/src/main/java/org/distorted/dialogs/RubikDialogPatternListAdapter.java
deleted file mode 100644
index 29062b52..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogPatternListAdapter.java
+++ /dev/null
@@ -1,153 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2023 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.content.Context;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseExpandableListAdapter;
-import android.widget.TextView;
-import org.distorted.objectlib.patterns.RubikPattern;
-import org.distorted.main.R;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class RubikDialogPatternListAdapter extends BaseExpandableListAdapter
-  {
-  private static final float PATTERN_CHILD_TEXT  = 0.038f;
-  private static final float PATTERN_GROUP_TEXT  = 0.060f;
-  private final Context mContext;
-  private final int mTab, mWidth;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public RubikDialogPatternListAdapter(Context context, int tab, int width)
-    {
-    mContext = context;
-    mTab     = tab;
-    mWidth   = width;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public Object getChild(int groupPosition, int childPosition)
-    {
-    RubikPattern pattern = RubikPattern.getInstance();
-    return pattern.getPatternName(mTab,groupPosition,childPosition);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public long getChildId(int groupPosition, int childPosition)
-    {
-    return childPosition;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View view, ViewGroup parent)
-    {
-    String childName = (String) getChild(groupPosition, childPosition);
-
-    if (view == null)
-      {
-      LayoutInflater infalInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-      view = infalInflater.inflate(R.layout.dialog_pattern_child_item, null);
-      }
-
-    int size = (int)(mWidth*PATTERN_CHILD_TEXT);
-    TextView childItem = view.findViewById(R.id.child);
-    childItem.setText(childName);
-    childItem.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
-    return view;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public int getChildrenCount(int groupPosition)
-    {
-    RubikPattern pattern = RubikPattern.getInstance();
-    return pattern.getNumPatterns(mTab,groupPosition);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public Object getGroup(int groupPosition)
-    {
-    RubikPattern pattern = RubikPattern.getInstance();
-    return pattern.getCategoryName(mTab,groupPosition);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public int getGroupCount()
-    {
-    RubikPattern pattern = RubikPattern.getInstance();
-    return pattern.getNumCategories(mTab);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public long getGroupId(int groupPosition)
-    {
-    return groupPosition;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public View getGroupView(int groupPosition, boolean isLastChild, View view, ViewGroup parent)
-    {
-    String groupName = (String) getGroup(groupPosition);
-
-    if (view == null)
-      {
-      LayoutInflater inf = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-      view = inf.inflate(R.layout.dialog_pattern_group_item, null);
-      }
-
-    int size = (int)(mWidth*PATTERN_GROUP_TEXT);
-    TextView heading = view.findViewById(R.id.heading);
-    heading.setText(groupName);
-    heading.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
-    RubikPattern pattern = RubikPattern.getInstance();
-    int numPatterns = pattern.getNumPatterns(mTab,groupPosition);
-    TextView counter = view.findViewById(R.id.counter);
-    counter.setText(String.format("%d", numPatterns));
-    counter.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
-    return view;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public boolean hasStableIds()
-    {
-    return true;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public boolean isChildSelectable(int groupPosition, int childPosition)
-    {
-    return true;
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogScores.java b/src/main/java/org/distorted/dialogs/RubikDialogScores.java
deleted file mode 100644
index 82068044..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogScores.java
+++ /dev/null
@@ -1,98 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2019 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.app.Dialog;
-import androidx.fragment.app.FragmentActivity;
-import androidx.viewpager.widget.ViewPager;
-import com.google.android.material.tabs.TabLayout;
-
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.ImageView;
-
-import org.distorted.main.R;
-import org.distorted.objects.RubikObject;
-import org.distorted.objects.RubikObjectList;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogScores extends RubikDialogAbstract
-  {
-  private static final float TAB_WIDTH = 0.066f;
-  private static final float TAB_HEIGHT= 0.066f;
-
-  private RubikDialogScoresPagerAdapter mPagerAdapter;
-
-  public interface ScoresInvoker
-    {
-    int getObjectOrdinal();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public void onResume()
-    {
-    super.onResume();
-
-    Window window = getDialog().getWindow();
-
-    if( window!=null )
-      {
-      WindowManager.LayoutParams params = window.getAttributes();
-      params.width  = (int)Math.min( mHeight*0.65f,mWidth*0.98f );
-      params.height = (int)( mHeight*0.90f);
-      window.setAttributes(params);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getResource()      { return R.layout.dialog_tabbed; }
-  public int getTitleResource() { return R.string.scores; }
-  public boolean hasArgument()  { return true; }
-  public int getPositive()      { return R.string.ok; }
-  public int getNegative()      { return -1; }
-  public void positiveAction()  { RubikDialogScoresThread.getInstance().exit(); }
-  public void negativeAction()  { }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
-    {
-    final int tabHeight= (int)(mHeight*TAB_HEIGHT);
-    final int tabWidth = (int)(mHeight*TAB_WIDTH);
-
-    ViewPager viewPager = view.findViewById(R.id.viewpager);
-    TabLayout tabLayout = view.findViewById(R.id.sliding_tabs);
-    mPagerAdapter = new RubikDialogScoresPagerAdapter(act,viewPager,mArgument.equals("true"), this);
-    tabLayout.setupWithViewPager(viewPager);
-
-    ScoresInvoker si = (ScoresInvoker) act;
-    int objectOrdinal = si.getObjectOrdinal();
-
-    viewPager.setCurrentItem(objectOrdinal);
-    int numObjects = RubikObjectList.getNumObjects();
-    ViewGroup.LayoutParams paramsView = new ViewGroup.LayoutParams( tabWidth,tabHeight );
-
-    for( int object=0; object<numObjects; object++ )
-      {
-      RubikObject robject = RubikObjectList.getObject(object);
-      ImageView imageView = new ImageView(act);
-      if( robject!=null ) robject.setIconTo(act,imageView);
-      imageView.setLayoutParams(paramsView);
-      TabLayout.Tab tab = tabLayout.getTabAt(object);
-      if( tab!=null ) tab.setCustomView(imageView);
-      }
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogScoresPagerAdapter.java b/src/main/java/org/distorted/dialogs/RubikDialogScoresPagerAdapter.java
deleted file mode 100644
index 6cf9c74f..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogScoresPagerAdapter.java
+++ /dev/null
@@ -1,223 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2020 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.fragment.app.FragmentActivity;
-import androidx.viewpager.widget.PagerAdapter;
-import androidx.viewpager.widget.ViewPager;
-
-import android.util.DisplayMetrics;
-import android.view.View;
-import android.view.ViewGroup;
-
-import org.distorted.main.R;
-import org.distorted.external.RubikScores;
-import org.distorted.external.RubikNetwork;
-import org.distorted.objects.RubikObject;
-import org.distorted.objects.RubikObjectList;
-import static org.distorted.external.RubikScores.LEVELS_SHOWN;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class RubikDialogScoresPagerAdapter extends PagerAdapter implements RubikNetwork.ScoresReceiver
-  {
-  private static final int NETWORK_NOT_READY = 0;
-  private static final int NETWORK_SUCCESS   = 1;
-  private static final int NETWORK_FAILURE   = 2;
-
-  private final FragmentActivity mAct;
-  private final RubikDialogScores mDialog;
-  private final RubikDialogScoresView[] mViews;
-  private final ViewPager mViewPager;
-  private final int mNumTabs;
-  private final boolean mIsSubmitting;
-  private final int[] mNumLevels;
-  private String[][][] mCountry, mName;
-  private int[][][] mTime;
-  private String mMessage;
-  private int mNetworkState;
-  private final Object mObj;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// we do not show level8 (i.e. level==7) - because in the new UI 2.0 this one does not exist anymore.
-
-  private boolean showLevel(int level)
-    {
-    return level!=7;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void receive(final String[][][] country, final String[][][] name, final int[][][] time)
-    {
-    synchronized(mObj)
-      {
-      RubikDialogScoresThread thr = RubikDialogScoresThread.getInstance();
-      thr.equip(mAct,mViewPager);
-
-      mNetworkState = NETWORK_SUCCESS;
-      mCountry = country;
-      mName = name;
-      mTime = time;
-
-      for(int t=0; t<mNumTabs; t++)
-        if( mViews[t]!=null )
-          {
-          int num = mNumLevels[t];
-          String[][] c = country[t];
-          String[][] n = name[t];
-          int[][] tm = time[t];
-          for(int l=0; l<=num; l++)
-            if( showLevel(l) ) thr.newWork(t, l, num, mViews[t], c[l], n[l], tm[l]);
-          }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void message(final String mess)
-    {
-    mNetworkState = NETWORK_FAILURE;
-    mMessage = mess;
-
-    mAct.runOnUiThread(new Runnable()
-      {
-      @Override
-      public void run()
-        {
-        for(int i=0; i<mNumTabs; i++)
-          if( mViews[i]!=null ) mViews[i].message(mess);
-        }
-      });
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void error(final String error)
-    {
-    char errorNumber = error.charAt(0);
-
-    switch(errorNumber)
-      {
-      case '1': message(mAct.getString(R.string.networkError));
-                break;
-      case '2': RubikScores scores = RubikScores.getInstance();
-                Bundle bundle = new Bundle();
-                bundle.putString("argument", scores.getName() );
-
-                RubikDialogSetName nameDiag = new RubikDialogSetName();
-                nameDiag.setArguments(bundle);
-                nameDiag.show(mAct.getSupportFragmentManager(), null);
-
-                mDialog.dismiss();
-
-                break;
-      case '3': message("Server error");
-                break;
-      default : message("Unexpected error");
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  RubikDialogScoresPagerAdapter(FragmentActivity act, ViewPager viewPager, boolean isSubmitting, RubikDialogScores diag)
-    {
-    mObj = new Object();
-    mAct = act;
-    mDialog = diag;
-    mNumTabs = RubikObjectList.getNumObjects();
-    mViews = new RubikDialogScoresView[mNumTabs];
-    mViewPager = viewPager;
-    mIsSubmitting = isSubmitting;
-    mNetworkState = NETWORK_NOT_READY;
-
-    mNumLevels = new int[mNumTabs];
-
-    for(int i=0; i<mNumTabs; i++)
-      {
-      RubikObject object = RubikObjectList.getObject(i);
-      int numScramble = object==null ? 1 : object.getNumScramble();
-      mNumLevels[i] = Math.min(numScramble-1,LEVELS_SHOWN);
-      }
-
-    viewPager.setAdapter(this);
-    viewPager.setOffscreenPageLimit(1);
-
-    RubikNetwork network = RubikNetwork.getInstance();
-
-    if( mIsSubmitting )  network.submit  ( this, mAct );
-    else                 network.download( this, mAct );
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  @NonNull
-  public Object instantiateItem(@NonNull ViewGroup collection, int position)
-    {
-    RubikDialogScoresView view;
-    DisplayMetrics metrics = mAct.getResources().getDisplayMetrics();
-
-    synchronized(mObj)
-      {
-      view = new RubikDialogScoresView(mAct, metrics.heightPixels, mIsSubmitting, mNetworkState==NETWORK_SUCCESS );
-      collection.addView(view);
-
-      if( mNetworkState==NETWORK_SUCCESS )
-        {
-        int num = mNumLevels[position];
-        String[][] c = mCountry[position];
-        String[][] n = mName[position];
-        int[][] tm = mTime[position];
-
-        RubikDialogScoresThread thr = RubikDialogScoresThread.getInstance();
-        thr.equip(mAct,mViewPager);
-
-        for(int l=0; l<=num; l++)
-          if( showLevel(l) ) thr.newWork(position, l, num, view, c[l], n[l], tm[l]);
-        }
-      else if( mNetworkState==NETWORK_FAILURE )
-        {
-        view.message(mMessage);
-        }
-      }
-
-    mViews[position] = view;
-
-    return view;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public void destroyItem(ViewGroup collection, int position, @NonNull Object view)
-    {
-    collection.removeView((View) view);
-    mViews[position] = null;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public int getCount()
-    {
-    return mNumTabs;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public boolean isViewFromObject(@NonNull View view, @NonNull Object object)
-    {
-    return view == object;
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogScoresThread.java b/src/main/java/org/distorted/dialogs/RubikDialogScoresThread.java
deleted file mode 100644
index 634b7a63..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogScoresThread.java
+++ /dev/null
@@ -1,176 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2023 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.widget.LinearLayout;
-
-import androidx.fragment.app.FragmentActivity;
-import androidx.viewpager.widget.ViewPager;
-
-import org.distorted.main.R;
-
-import java.lang.ref.WeakReference;
-import java.util.Vector;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class RubikDialogScoresThread extends Thread
-  {
-  private final Object mObject;
-  private final Vector<WorkLoad> mBuffers;
-  private WeakReference<FragmentActivity> mWeakAct;
-  private ViewPager mViewPager;
-  private boolean mRunning;
-
-  private static RubikDialogScoresThread mThis;
-
-  private static class WorkLoad
-    {
-    int tab, level, numLevels;
-    RubikDialogScoresView view;
-    String[] country, name;
-    int[] time;
-
-    WorkLoad(int t, int l, int nl, RubikDialogScoresView v, String[] c, String[] n, int[] tm)
-      {
-      tab = t;
-      level = l;
-      numLevels = nl;
-      view = v;
-      country = c;
-      name = n;
-      time = tm;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private RubikDialogScoresThread()
-    {
-    mObject  = new Object();
-    mRunning = true;
-    mBuffers = new Vector<>();
-    this.start();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static RubikDialogScoresThread getInstance()
-    {
-    if( mThis==null ) mThis = new RubikDialogScoresThread();
-    return mThis;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void equip(FragmentActivity act, ViewPager pager)
-    {
-    mWeakAct = new WeakReference<>(act);
-    mViewPager = pager;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void newWork(int t, int l, int nl, RubikDialogScoresView v, String[] c, String[] n, int[] tm)
-    {
-    synchronized(mObject)
-      {
-      WorkLoad load = new WorkLoad(t,l,nl,v,c,n,tm);
-      mBuffers.add(load);
-      mObject.notify();
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void exit()
-    {
-    synchronized(mObject)
-      {
-      mRunning = false;
-      mThis = null;
-      mObject.notify();
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void run()
-    {
-    WorkLoad load;
-
-    while(true)
-      {
-      synchronized(mObject)
-        {
-        do
-          {
-          load = getNextLoad();
-          if( load!=null ) process(load);
-          }
-        while(load!=null);
-
-        if( mRunning )
-          {
-          try { mObject.wait(); }
-          catch(InterruptedException ignored) { }
-          }
-        else break;
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private WorkLoad getNextLoad()
-    {
-    if( mViewPager==null ) return null;
-
-    int currentTab = mViewPager.getCurrentItem();
-    int size = mBuffers.size();
-
-    for(int i=0; i<size; i++)
-      {
-      WorkLoad load = mBuffers.get(i);
-      if( load.tab==currentTab ) return mBuffers.remove(i);
-      }
-
-    return size>0 ? mBuffers.remove(0) : null;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void process(WorkLoad load)
-    {
-    final FragmentActivity act = mWeakAct.get();
-    int tab = load.tab;
-    int level = load.level;
-    int numLevels = load.numLevels;
-    RubikDialogScoresView view = load.view;
-    String[] country = load.country;
-    String[] name = load.name;
-    int[] time = load.time;
-    String title = level==numLevels ? act.getString(R.string.levelM) : act.getString(R.string.lv_placeholder, level+1);
-
-    if( view!=null )
-      {
-      final LinearLayout section = view.createSection(act, tab, title, level, country, name, time);
-
-      act.runOnUiThread(new Runnable()
-        {
-        @Override
-        public void run()
-          {
-          view.addSection(act, section);
-          }
-        });
-      }
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogScoresView.java b/src/main/java/org/distorted/dialogs/RubikDialogScoresView.java
deleted file mode 100644
index 0d776650..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogScoresView.java
+++ /dev/null
@@ -1,195 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2020 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.content.Context;
-import android.content.res.Resources;
-import androidx.fragment.app.FragmentActivity;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import org.distorted.helpers.BaseActivity;
-import org.distorted.main.R;
-import org.distorted.external.RubikScores;
-
-import static org.distorted.external.RubikNetwork.MAX_PLACES;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogScoresView extends FrameLayout
-  {
-  private static final float SCORES_LEVEL_TEXT = 0.035f;
-  private static final float SCORES_ITEM_TEXT  = 0.025f;
-
-  private LinearLayout mLayout=null;
-  private int mHeight;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  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, int height, boolean isSubmitting, boolean downloadedAlready )
-    {
-    super(context);
-
-    mHeight = height;
-
-    if( !downloadedAlready )
-      {
-      View view = inflate(context, R.layout.dialog_scores_downloading, null);
-      addView(view);
-      TextView text = findViewById(R.id.message_text);
-      text.setText(context.getString(isSubmitting ? R.string.submitting : R.string.downloading));
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  LinearLayout createSection(FragmentActivity act, int tab, String title, int level, final String[] country, final String[] name, final int[] time)
-    {
-    LinearLayout levelLayout = (LinearLayout)inflate(act, R.layout.dialog_scores_scramble_title, null);
-    TextView text = levelLayout.findViewById(R.id.scoresScrambleTitle);
-    text.setText(title);
-
-    int size = (int)(mHeight*SCORES_LEVEL_TEXT);
-    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
-
-    Resources res = act.getResources();
-    String packageName = act.getPackageName();
-    RubikScores scores = RubikScores.getInstance();
-
-    boolean inserted = false;
-    int myRecordInMillis = scores.getRecord(tab, level);
-    String myRecord = ( myRecordInMillis<RubikScores.NO_RECORD ) ? formatRecord(myRecordInMillis) : "??";
-    String myName = scores.getName();
-    if( myName.length()==0 ) myName = act.getString(R.string.you);
-    int myCountryID = res.getIdentifier( scores.getCountry(), "drawable", packageName);
-    String theirTime;
-    int theirCountryID;
-
-    BaseActivity bact = (BaseActivity) act;
-    int selected = bact.getSelectedColor();
-    int height = (int)(mHeight*SCORES_ITEM_TEXT);
-    int white = res.getColor(R.color.white);
-    int red   = res.getColor(selected);
-    boolean equals;
-
-    for(int j=0; j<MAX_PLACES-1; j++)
-      {
-      if( name[j] != null )
-        {
-        if( myRecordInMillis<time[j] && !inserted )
-          {
-          inserted = true;
-          View row = createRow(act, myCountryID, myName, myRecord, height, red);
-          levelLayout.addView(row);
-          }
-
-        equals = name[j].equals(myName);
-
-        if( !inserted || !equals )
-          {
-          if( equals ) inserted=true;
-          theirCountryID = res.getIdentifier( country[j], "drawable", packageName);
-          theirTime = formatRecord(time[j]);
-          View row = createRow(act, theirCountryID, name[j], theirTime, height, equals ? red:white);
-          levelLayout.addView(row);
-          }
-        }
-      }
-
-    if( !inserted )
-      {
-      View row = createRow(act, myCountryID, myName, myRecord, height, red);
-      levelLayout.addView(row);
-      }
-
-    return levelLayout;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static String formatRecord(int time)
-    {
-    time /= 10;
-    int millis = time%100;
-    time /= 100;
-    int seconds = time%60;
-    int minutes = time/60;
-
-    String ret = minutes + (seconds<10 ? ":0" : ":") + seconds + (millis<10 ? ".0" : ".") + millis;
-    return minutes<10 ? "0"+ret : ret;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private View createRow(FragmentActivity act, int countryID, String name, String time, int height, int color)
-    {
-    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(countryID!=0 ? countryID : org.distorted.flags.R.drawable.unknown);
-    textName.setText(name);
-    textTime.setText(time);
-
-    textName.setTextColor(color);
-    textTime.setTextColor(color);
-
-    textName.setTextSize(TypedValue.COMPLEX_UNIT_PX, height);
-    textTime.setTextSize(TypedValue.COMPLEX_UNIT_PX, height);
-
-    return row;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// needs to run on UI thread
-
-  void addSection(FragmentActivity act, LinearLayout section)
-    {
-    if( mLayout==null )
-      {
-      removeAllViews();
-      View tab = inflate(act, R.layout.dialog_scores_tab, null);
-      mLayout = tab.findViewById(R.id.tabLayout);
-      addView(tab);
-      }
-
-    mLayout.addView(section);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// needs to run on UI thread
-
-  void message(final String mess)
-    {
-    TextView text = findViewById(R.id.message_text);
-    if( text!=null ) text.setText(mess);
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogSetName.java b/src/main/java/org/distorted/dialogs/RubikDialogSetName.java
deleted file mode 100644
index f98c76b3..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogSetName.java
+++ /dev/null
@@ -1,143 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2020 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.app.Dialog;
-import android.os.Bundle;
-import androidx.fragment.app.FragmentActivity;
-import androidx.appcompat.app.AlertDialog;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.TextView;
-
-import org.distorted.main.R;
-import org.distorted.external.RubikScores;
-import org.distorted.playui.PlayActivity;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogSetName extends RubikDialogAbstract
-  {
-  private static final int MAX_NAME_LEN = 15;
-  private EditText mEdit;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public void onStart()
-    {
-    super.onStart();
-
-    AlertDialog dialog = (AlertDialog)getDialog();
-
-    if( dialog!=null )
-      {
-      Button positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE);
-
-      if( positiveButton!=null )
-        {
-        String editName = mEdit.getText().toString();
-        positiveButton.setEnabled(editName.length()>0);
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getResource()      { return R.layout.dialog_set_name; }
-  public int getTitleResource() { return mArgument.length()==0 ? R.string.choose_name : R.string.name_taken; }
-  public boolean hasArgument()  { return true; }
-  public int getPositive()      { return R.string.ok; }
-  public int getNegative()      { return -1; }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void positiveAction()
-    {
-    String name = mEdit.getText().toString();
-    int len = name.length();
-
-    if( len>0 )
-      {
-      if( len>MAX_NAME_LEN )
-        {
-        name = name.substring(0,MAX_NAME_LEN);
-        }
-
-      name = name.replace(' ', '_');
-
-      try
-        {
-        PlayActivity act = (PlayActivity)getActivity();
-        RubikScores.getInstance().setName(name);
-        Bundle bundle = new Bundle();
-        bundle.putString("argument", "true");
-        RubikDialogScores scores = new RubikDialogScores();
-        scores.setArguments(bundle);
-        scores.show(act.getSupportFragmentManager(), null);
-        }
-      catch(IllegalStateException ex)
-        {
-        android.util.Log.e("D", "IllegalStateException trying to display SetName");
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void negativeAction()
-    {
-
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
-    {
-    TextView text = view.findViewById(R.id.set_name_message);
-    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
-    mEdit = view.findViewById(R.id.set_name);
-    mEdit.setHeight( (int)(2*mTitleSize) );
-    mEdit.setTextSize(TypedValue.COMPLEX_UNIT_PX, 1.5f*mTitleSize);
-
-    if( mArgument.length()==0 )
-      {
-      text.setText(R.string.new_name);
-      }
-    else
-      {
-      text.setText(act.getString(R.string.new_name_try_again, mArgument));
-      }
-
-    dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
-
-    mEdit.requestFocus();
-
-    mEdit.addTextChangedListener(new TextWatcher()
-      {
-      @Override
-      public void afterTextChanged(Editable s) {}
-
-      @Override
-      public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
-
-      @Override
-      public void onTextChanged(CharSequence s, int start, int before, int count)
-        {
-        ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(count>0);
-        }
-      });
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogSolved.java b/src/main/java/org/distorted/dialogs/RubikDialogSolved.java
deleted file mode 100644
index 564558e4..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogSolved.java
+++ /dev/null
@@ -1,62 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2020 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.app.Dialog;
-import androidx.fragment.app.FragmentActivity;
-
-import android.util.TypedValue;
-import android.view.View;
-import android.widget.TextView;
-
-import org.distorted.main.R;
-import org.distorted.playui.PlayActivity;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogSolved extends RubikDialogAbstract
-  {
-  public int getResource()      { return R.layout.dialog_solved; }
-  public int getTitleResource() { return R.string.solved; }
-  public boolean hasArgument()  { return true; }
-  public int getPositive()      { return R.string.ok; }
-  public int getNegative()      { return -1; }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void positiveAction()
-    {
-    PlayActivity act = (PlayActivity)getActivity();
-    if( act!=null ) act.finish();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void negativeAction()
-    {
-
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
-    {
-    TextView text = view.findViewById(R.id.solved_time);
-    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
-    text.setText(mArgument);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static String getDialogTag()
-    {
-    return "DialogSolved";
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogSolverError.java b/src/main/java/org/distorted/dialogs/RubikDialogSolverError.java
deleted file mode 100644
index 5688801b..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogSolverError.java
+++ /dev/null
@@ -1,41 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2020 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.app.Dialog;
-import androidx.fragment.app.FragmentActivity;
-
-import android.util.TypedValue;
-import android.view.View;
-import android.widget.TextView;
-
-import org.distorted.main.R;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogSolverError extends RubikDialogAbstract
-  {
-  public int getResource()      { return R.layout.dialog_solver_error; }
-  public int getTitleResource() { return R.string.error; }
-  public boolean hasArgument()  { return true; }
-  public int getPositive()      { return R.string.ok; }
-  public int getNegative()      { return -1; }
-  public void positiveAction()  { }
-  public void negativeAction()  { }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
-    {
-    TextView text = view.findViewById(R.id.solver_error);
-    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
-    text.setText(mArgument);
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogSolverImpossible.java b/src/main/java/org/distorted/dialogs/RubikDialogSolverImpossible.java
deleted file mode 100644
index f909c199..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogSolverImpossible.java
+++ /dev/null
@@ -1,41 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2020 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.app.Dialog;
-import android.util.TypedValue;
-import android.view.View;
-import android.widget.TextView;
-
-import androidx.fragment.app.FragmentActivity;
-
-import org.distorted.main.R;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogSolverImpossible extends RubikDialogAbstract
-  {
-  public int getResource()      { return R.layout.dialog_solver_error; }
-  public int getTitleResource() { return R.string.impossible_position; }
-  public boolean hasArgument()  { return true; }
-  public int getPositive()      { return R.string.ok; }
-  public int getNegative()      { return -1; }
-  public void positiveAction()  { }
-  public void negativeAction()  { }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
-    {
-    TextView text = view.findViewById(R.id.solver_error);
-    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
-    text.setText(mArgument);
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogSolverView.java b/src/main/java/org/distorted/dialogs/RubikDialogSolverView.java
deleted file mode 100644
index a9d269f2..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogSolverView.java
+++ /dev/null
@@ -1,84 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2023 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.util.TypedValue;
-import android.view.View;
-import android.widget.Button;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import org.distorted.main.R;
-import org.distorted.objectlib.helpers.OperatingSystemInterface;
-import org.distorted.objectlib.main.TwistyObject;
-import org.distorted.objectlib.solvers.verifiers.SolverAbstract;
-import org.distorted.objectlib.solvers.verifiers.SolvingList;
-import org.distorted.solverui.ScreenList;
-import org.distorted.solverui.ScreenSetupPosition;
-import org.distorted.solverui.SolverActivity;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogSolverView
-  {
-  private final View mView;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public RubikDialogSolverView(final SolverActivity act, final RubikDialogSolvers dialog,
-                               int solverOrdinal, int title, int desc, int padding,
-                               int fontSize, LinearLayout.LayoutParams pView,
-                               LinearLayout.LayoutParams pText, LinearLayout.LayoutParams pButt )
-    {
-    mView = act.getLayoutInflater().inflate(R.layout.dialog_solvers_pane, null);
-    mView.setLayoutParams(pView);
-    mView.setPadding(padding,padding,padding,padding);
-
-    TextView titleView = mView.findViewById(R.id.solvers_pane_title);
-    titleView.setText(title);
-    TextView descView = mView.findViewById(R.id.solvers_pane_description);
-    descView.setText(desc);
-
-    titleView.setLayoutParams(pText);
-    Button button = mView.findViewById(R.id.solvers_pane_button);
-
-    button.setOnClickListener( new View.OnClickListener()
-      {
-      @Override
-      public void onClick(View v)
-        {
-        dialog.dismiss();
-        SolvingList list = SolvingList.getSolver(solverOrdinal);
-        OperatingSystemInterface os = act.getInterface();
-        TwistyObject object = act.getObject();
-        SolverAbstract solver = list.create(os,object);
-        ScreenSetupPosition screen = (ScreenSetupPosition)ScreenList.SVER.getScreenClass();
-
-        if( solver!=null )
-          {
-          int[] result = solver.validatePosition(object);
-          if( result[0]>=0 ) solver.solve(screen,result);
-          else screen.displayImpossibleDialog(result,solver.getFaceColors());
-          }
-        else screen.displayErrorDialog(act.getString(R.string.solver_generic_not_implemented));
-        }
-      });
-
-    button.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
-    button.setLayoutParams(pButt);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public View getView()
-    {
-    return mView;
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogSolvers.java b/src/main/java/org/distorted/dialogs/RubikDialogSolvers.java
deleted file mode 100644
index 389df9e2..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogSolvers.java
+++ /dev/null
@@ -1,101 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2022 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.app.Dialog;
-import android.util.DisplayMetrics;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.widget.ScrollView;
-import android.widget.TextView;
-
-import androidx.fragment.app.FragmentActivity;
-
-import org.distorted.main.R;
-import org.distorted.objectlib.metadata.ListObjects;
-import org.distorted.solverui.SolverActivity;
-import org.distorted.objectlib.solvers.verifiers.SolvingList;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogSolvers extends RubikDialogAbstract
-  {
-  @Override
-  public void onResume()
-    {
-    super.onResume();
-
-    Window window = getDialog().getWindow();
-
-    if( window!=null )
-      {
-      WindowManager.LayoutParams params = window.getAttributes();
-      params.width  = (int)Math.min( mHeight*0.60f,mWidth*0.75f );
-      window.setAttributes(params);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getResource()            { return R.layout.dialog_scrollable_panes; }
-  public int getTitleResource()       { return R.string.solvers; }
-  public boolean hasArgument()        { return true; }
-  public int getPositive()            { return -1; }
-  public int getNegative()            { return -1; }
-  public static String getDialogTag() { return "DialogSolvers"; }
-  public void positiveAction()        { }
-  public void negativeAction()        { }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
-    {
-    int margin= (int)(mHeight*0.010f);
-    int padd  = (int)(mHeight*0.010f);
-    int font  = (int)(mHeight*0.025f);
-
-    LinearLayout layout= view.findViewById(R.id.dialog_scrollable_main_layout);
-    TextView text  = view.findViewById(R.id.dialog_scrollable_message);
-    text.setVisibility(View.GONE);
-
-    DisplayMetrics metrics = new DisplayMetrics();
-    act.getWindowManager().getDefaultDisplay().getMetrics(metrics);
-    float logicalDensity = metrics.density;
-    int px = (int) Math.ceil(10*logicalDensity);
-    ScrollView scroll = view.findViewById(R.id.updates_scroll);
-    scroll.setPadding(px,0,px,px);
-
-    LinearLayout.LayoutParams pV = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
-    pV.setMargins(margin, margin, margin, 0);
-    LinearLayout.LayoutParams pL = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
-    pL.setMargins(margin, margin, margin, margin);
-    LinearLayout.LayoutParams pT = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
-    pT.setMargins(0,0,0,2*margin);
-    LinearLayout.LayoutParams pB = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
-    pB.setMargins(0,2*margin,0,0);
-
-    SolverActivity sact = (SolverActivity) getContext();
-    int objectOrdinal = ListObjects.getObjectIndex(mArgument);
-    int[] solverOrdinals = SolvingList.getSolverOrdinals(objectOrdinal);
-    int len = solverOrdinals==null ? 0 : solverOrdinals.length;
-
-    for(int o=0; o<len; o++ )
-      {
-      int ord = solverOrdinals[o];
-      SolvingList solver = SolvingList.getSolver(ord);
-      int title  = solver.getTitle();
-      int description = solver.getDescription();
-      RubikDialogSolverView pane = new RubikDialogSolverView(sact,this,ord,title,description, padd, font, (o==len-1?pL:pV),pT,pB);
-      layout.addView(pane.getView());
-      }
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogTutorial.java b/src/main/java/org/distorted/dialogs/RubikDialogTutorial.java
deleted file mode 100644
index 8e904ae5..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogTutorial.java
+++ /dev/null
@@ -1,204 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2023 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import static android.view.View.inflate;
-
-import android.app.Dialog;
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.fragment.app.FragmentActivity;
-
-import org.distorted.main.R;
-import org.distorted.objectlib.json.JsonReader;
-import org.distorted.objects.RubikObject;
-import org.distorted.objects.RubikObjectList;
-import org.distorted.tutorials.TutorialActivity;
-
-import java.io.InputStream;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogTutorial extends RubikDialogAbstract
-  {
-  public static final float PANE_HEIGHT= 0.06f;
-  public static final float TEXT_SIZE  = 0.41f;
-
-  private Dialog mDialog;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public void onResume()
-    {
-    super.onResume();
-
-    Window window = getDialog().getWindow();
-
-    if( window!=null )
-      {
-      WindowManager.LayoutParams params = window.getAttributes();
-      params.width  = (int)Math.min( mHeight*0.67f,mWidth*0.98f );
-      //params.height = (int)Math.min( mHeight*0.85f,mWidth*1.30f );
-      window.setAttributes(params);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getResource()      { return R.layout.dialog_tutorial_single; }
-  public int getTitleResource() { return R.string.tutorials; }
-  public boolean hasArgument()  { return true; }
-  public int getPositive()      { return -1; }
-  public int getNegative()      { return -1; }
-  public void positiveAction()  { }
-  public void negativeAction()  { }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
-    {
-    mDialog = dialog;
-
-    int objectOrdinal = Integer.parseInt(mArgument);
-
-    if( objectOrdinal<0 )
-      {
-      android.util.Log.e("D", "object "+mArgument+" not found");
-      return;
-      }
-
-    RubikObject robject = RubikObjectList.getObject(objectOrdinal);
-    InputStream jsonStream = robject==null ? null : robject.getExtrasStream(act);
-    String[][] tutorials=null;
-
-    if( jsonStream!=null )
-      {
-      try
-        {
-        JsonReader reader = new JsonReader();
-        reader.parseJsonTutorial(jsonStream);
-        tutorials = reader.getTutorials();
-        }
-      catch(Exception ignored) { }
-      }
-
-    if( tutorials!=null )
-      {
-      int paneSize = (int)(mHeight*PANE_HEIGHT);
-      Resources res = act.getResources();
-      String packageName = act.getPackageName();
-
-      LinearLayout layout = view.findViewById(R.id.tutLayout);
-
-      TutorialActivity mact = (TutorialActivity)act;
-      int backgroundC = mact.getVeryDarkColor();
-
-      int colorB = res.getColor(backgroundC);
-      int colorT = res.getColor(R.color.white);
-
-      int numTuts = tutorials.length;
-
-      for( int t=0; t<numTuts; t++)
-        {
-        String[] tutorial = tutorials[t];
-
-        String coun = tutorial[0];
-        String url  = tutorial[1];
-        String desc = tutorial[2];
-        String auth = tutorial[3];
-
-        int countryID = res.getIdentifier(coun, "drawable", packageName);
-
-        View row = createRow(act, countryID, desc, url, auth, paneSize, colorB, colorT, t==(numTuts-1) );
-        layout.addView(row);
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private View createRow(final FragmentActivity act, int countryID, final String desc, final String url,
-                         final String auth, int size, int colorB, int colorT, boolean last)
-    {
-    float textSize = size*TEXT_SIZE;
-    View row = inflate( act, R.layout.dialog_tutorial_row, null);
-
-    LinearLayout layout = row.findViewById(R.id.tutorialLayout);
-    layout.setMinimumHeight(size);
-
-    Button butt = row.findViewById(R.id.tutorialButton);
-    butt.setText(R.string.view);
-    butt.setTextColor(colorT);
-    butt.setBackgroundColor(colorB);
-    butt.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-    butt.setHeight(size);
-
-    final TutorialActivity tact = (TutorialActivity)getContext();
-
-    butt.setOnClickListener( new View.OnClickListener()
-      {
-      @Override
-      public void onClick(View v)
-        {
-        mDialog.dismiss();
-        if( tact!=null ) tact.loadTutorial(url);
-        }
-      });
-
-    ImageView image = row.findViewById(R.id.tutorialCountry);
-    int id = countryID!=0 ? countryID : org.distorted.flags.R.drawable.unknown;
-    image.setImageResource(id);
-
-    TextView author = row.findViewById(R.id.tutorialAuthor);
-    author.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-    author.setText(auth);
-
-    TextView title  = row.findViewById(R.id.tutorialTitle);
-    title.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-    title.setText(desc);
-
-    if( last )
-      {
-      int m = (int)convertDpToPixel(10,act);
-      LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT);
-      params.bottomMargin = m;
-      params.leftMargin   = m;
-      params.rightMargin  = m;
-      layout.setLayoutParams(params);
-      }
-
-    return row;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static float convertDpToPixel(float dp, Context context)
-    {
-    return dp*((float) context.getResources().getDisplayMetrics().densityDpi/DisplayMetrics.DENSITY_DEFAULT);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static String getDialogTag()
-    {
-    return "DialogTutorialSingle";
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogUpdateView.java b/src/main/java/org/distorted/dialogs/RubikDialogUpdateView.java
deleted file mode 100644
index 3ed88519..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogUpdateView.java
+++ /dev/null
@@ -1,261 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2022 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.lang.ref.WeakReference;
-
-import android.app.Activity;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.util.TypedValue;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import org.distorted.external.RubikFiles;
-import org.distorted.main.R;
-import org.distorted.external.RubikNetwork;
-import org.distorted.external.RubikUpdates;
-import org.distorted.objectlib.json.JsonReader;
-import org.distorted.objects.RubikObjectList;
-
-import static org.distorted.objects.RubikObjectList.SHOW_DOWNLOADED_DEBUG;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogUpdateView implements RubikNetwork.Downloadee
-  {
-  private ImageView mIcon;
-  private RubikUpdates.UpdateInfo mInfo;
-  private ProgressBar mBar;
-  private Button mButton;
-  private TextView mDescription;
-  private WeakReference<Activity> mAct;
-  private boolean mIconSaved;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public View createView(Activity act, RubikUpdates.UpdateInfo info, int fontSize, int padding,
-                         LinearLayout.LayoutParams pView, LinearLayout.LayoutParams pText, LinearLayout.LayoutParams pButt )
-    {
-    mIconSaved=false;
-    mAct = new WeakReference<>(act);
-    mInfo = info;
-    final RubikNetwork.Downloadee downloadee = this;
-    View view = act.getLayoutInflater().inflate(R.layout.dialog_updates_pane, null);
-    TextView title = view.findViewById(R.id.updates_pane_title);
-    title.setText(info.mObjectLongName);
-    mDescription = view.findViewById(R.id.updates_pane_description);
-    mDescription.setText(info.mDescription);
-
-    mIcon = view.findViewById(R.id.updates_pane_image);
-    mIcon.setImageResource(R.drawable.unknown_icon);
-
-    view.setLayoutParams(pView);
-
-    title.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
-    mDescription.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
-
-    title.setLayoutParams(pText);
-    mDescription.setLayoutParams(pText);
-
-    view.setPadding(padding,padding,padding,padding);
-
-    mBar    = view.findViewById(R.id.updates_pane_bar);
-    mButton = view.findViewById(R.id.updates_pane_button);
-
-    if( info.mPercent>=100 )
-      {
-      mBar.setVisibility(View.GONE);
-      mButton.setOnClickListener( new View.OnClickListener()
-        {
-        @Override
-        public void onClick(View v)
-          {
-          startDownload();
-          RubikNetwork network = RubikNetwork.getInstance();
-          network.downloadJSON(info,downloadee);
-          }
-        });
-
-      mButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
-      mButton.setLayoutParams(pButt);
-      mBar.setLayoutParams(pButt);
-      }
-    else
-      {
-      mButton.setVisibility(View.GONE);
-      mBar.setLayoutParams(pButt);
-      mBar.setProgress(info.mPercent);
-      }
-
-    return view;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void setIcon(Bitmap icon, boolean downloaded)
-    {
-    mIcon.setImageBitmap(icon);
-
-    if( downloaded )
-      {
-      String name = mInfo.mObjectShortName + ".png";
-      Activity act = mAct.get();
-      RubikFiles files = RubikFiles.getInstance();
-      mIconSaved = files.saveIcon(act,mInfo.mIcon, name);
-
-      if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "Saving icon "+name+" to a file "+mIconSaved);
-      }
-    else
-      {
-      mIconSaved = true;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void startDownload()
-    {
-    mDescription.setText(R.string.downloading);
-    mBar.setProgress(20);
-    mButton.setVisibility(View.GONE);
-    mBar.setVisibility(View.VISIBLE);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void displayError(int error)
-    {
-    Activity act = mAct.get();
-
-    act.runOnUiThread(new Runnable()
-      {
-      @Override
-      public void run()
-        {
-        mDescription.setTextColor(Color.parseColor("#ff0000"));
-        mDescription.setText(error);
-        }
-      });
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void makeProgress(int progress, int message)
-    {
-    Activity act = mAct.get();
-
-    act.runOnUiThread(new Runnable()
-      {
-      @Override
-      public void run()
-        {
-        mBar.setProgress(progress);
-        mDescription.setText(message);
-        }
-      });
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void jsonDownloaded()
-    {
-    if( mInfo.mUpdateObject && mInfo.mObjectStream==null )
-      {
-      displayError(R.string.networkError);
-      }
-    else
-      {
-      makeProgress(50,R.string.installing);
-      RubikFiles files = RubikFiles.getInstance();
-      boolean oSuccess, eSuccess=false;
-
-      if( mInfo.mObjectStream!=null )
-        {
-        String objectName = mInfo.mObjectShortName + "_object.json";
-        Activity act = mAct.get();
-        oSuccess = files.saveFile(act,mInfo.mObjectStream, objectName);
-
-        if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "Saving JSON "+objectName+" to a file "+oSuccess);
-
-        try
-          {
-          File file = new File(act.getFilesDir(), objectName);
-          InputStream stream = new FileInputStream(file);
-          JsonReader reader = new JsonReader();
-          reader.parseJsonFileMetadata(stream);
-          stream.close();
-
-          mInfo.mNumScrambles = reader.getNumScrambles();
-          mInfo.mPrice        = reader.getPrice();
-          float diff          = reader.getDifficulty();
-          int category        = reader.getCategory();
-          int year            = reader.getYearOfInvention();
-          String author       = reader.getAuthor();
-          boolean adjColors   = reader.getAdjustableColors();
-
-          if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "Read from JSON numScrambles="+mInfo.mNumScrambles+" price="+mInfo.mPrice);
-
-          if( mInfo.mExtrasStream!=null )
-            {
-            String name = mInfo.mObjectShortName + "_extras.json";
-            eSuccess = files.saveFile(act,mInfo.mExtrasStream, name);
-
-            if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "Saving Extras "+name+" to a file "+eSuccess);
-            }
-
-          if( mIconSaved || oSuccess || eSuccess )
-            {
-            makeProgress(75,R.string.configuring);
-
-            if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "1");
-
-            boolean success = RubikObjectList.addDownloadedObject(act, mInfo.mObjectShortName, mInfo.mNumScrambles, mInfo.mPrice, mInfo.mObjectMinorVersion,
-                                                                  mInfo.mExtrasMinorVersion, mIconSaved, oSuccess, eSuccess, diff, category, year, author,adjColors);
-            if( success )
-              {
-              if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "2");
-
-              RubikNetwork network = RubikNetwork.getInstance();
-              network.updateDone(mInfo.mObjectShortName);
-
-              if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "3");
-
-              makeProgress(100,R.string.success);
-
-              if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "4");
-              }
-            else
-              {
-              displayError(R.string.saveError);
-              }
-            }
-          else
-            {
-            displayError(R.string.saveError);
-            }
-          }
-        catch(Exception ex)
-          {
-          displayError(R.string.saveError);
-          files.deleteJsonObject(act,mInfo.mObjectShortName);
-          if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "Error reading numScrambles");
-          }
-        }
-      }
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogUpdates.java b/src/main/java/org/distorted/dialogs/RubikDialogUpdates.java
deleted file mode 100644
index eb56604e..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogUpdates.java
+++ /dev/null
@@ -1,206 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2022 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import java.util.ArrayList;
-
-import android.app.Dialog;
-import android.graphics.Bitmap;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.fragment.app.FragmentActivity;
-
-import org.distorted.main.R;
-import org.distorted.external.RubikNetwork;
-import org.distorted.external.RubikUpdates;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogUpdates extends RubikDialogAbstract implements RubikNetwork.IconReceiver, RubikNetwork.Updatee
-  {
-  private TextView mText;
-  private LinearLayout mLayout;
-  private int mMargin, mSize, mFontSize, mPadding;
-  private ArrayList<RubikDialogUpdateView> mPanes;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public void onResume()
-    {
-    super.onResume();
-
-    Window window = getDialog().getWindow();
-
-    if( window!=null )
-      {
-      WindowManager.LayoutParams params = window.getAttributes();
-      params.width  = (int)Math.min( mHeight*0.65f,mWidth*0.98f );
-      window.setAttributes(params);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getResource()            { return R.layout.dialog_scrollable_panes; }
-  public int getTitleResource()       { return R.string.updates; }
-  public boolean hasArgument()        { return false; }
-  public int getPositive()            { return R.string.ok; }
-  public int getNegative()            { return -1; }
-  public void positiveAction()        { }
-  public void negativeAction()        { }
-  public static String getDialogTag() { return "DialogUpdates"; }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
-    {
-    if( mPanes==null ) mPanes = new ArrayList<>();
-
-    int minH  = (int)(mHeight*0.25f);
-    mMargin   = (int)(mHeight*0.01f);
-    mSize     = (int)(mHeight*0.14f);
-    mFontSize = (int)(mHeight*0.02f);
-    mPadding  = (int)(mHeight*0.01f);
-
-    mLayout= view.findViewById(R.id.dialog_scrollable_main_layout);
-    mText  = view.findViewById(R.id.dialog_scrollable_message);
-    mText.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
-    mText.setText( act.getString(R.string.downloading) );
-
-    mLayout.setMinimumHeight(minH);
-    mText.setMinimumHeight(minH);
-    view.setMinimumHeight(minH);
-
-    RubikNetwork network = RubikNetwork.getInstance();
-    network.signUpForUpdates(this);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void receiveUpdate(RubikUpdates updates, FragmentActivity act)
-    {
-    int numC = updates.getCompletedNumber();
-    int numS = updates.getStartedNumber();
-
-    if( numC+numS<=0 )
-      {
-      mText.setText(act.getString(R.string.no_updates));
-      }
-    else
-      {
-      int textH = (int)(mSize*0.27f);
-      int buttH = (int)(mSize*0.35f);
-
-      LinearLayout.LayoutParams pV = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, mSize );
-      pV.setMargins(mMargin, mMargin, mMargin, 0);
-      LinearLayout.LayoutParams pL = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, mSize );
-      pL.setMargins(mMargin, mMargin, mMargin, mMargin);
-      LinearLayout.LayoutParams pT = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, textH );
-      LinearLayout.LayoutParams pB = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, buttH );
-
-      mText.setVisibility(View.GONE);
-
-      if( mLayout!=null )
-        {
-        for(int i=0; i<numC; i++)
-          {
-          RubikUpdates.UpdateInfo info = updates.getCompletedUpdate(i);
-          RubikDialogUpdateView rubikView = new RubikDialogUpdateView();
-          View pane = rubikView.createView(act,info,mFontSize,mPadding,( (numS==0 && i==numC-1)?pL:pV),pT,pB);
-          mLayout.addView(pane);
-          mPanes.add(rubikView);
-          }
-
-        for(int i=0; i<numS; i++)
-          {
-          RubikUpdates.UpdateInfo info = updates.getStartedUpdate(i);
-          RubikDialogUpdateView rubikView = new RubikDialogUpdateView();
-          View pane = rubikView.createView(act,info,mFontSize,mPadding,(i==numS-1?pL:pV),pT,pB);
-          mLayout.addView(pane);
-          mPanes.add(rubikView);
-          }
-
-        RubikNetwork network = RubikNetwork.getInstance();
-        network.downloadIcons(act,this);
-        }
-      else
-        {
-        android.util.Log.e("D", "mainLayout NULL");
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void receiveUpdate(RubikUpdates updates)
-    {
-    FragmentActivity act = getActivity();
-
-    if( act!=null )
-      {
-      act.runOnUiThread(new Runnable()
-        {
-        @Override
-        public void run()
-          {
-          receiveUpdate(updates,act);
-          }
-        });
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void errorUpdate()
-    {
-    FragmentActivity act = getActivity();
-    if( act!=null ) mText.setText(act.getString(R.string.networkError));
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void objectDownloaded(String shortName)
-    {
-
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getType()
-    {
-    return 1;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void iconDownloaded(int ordinal, Bitmap icon, boolean downloaded)
-    {
-    FragmentActivity act = getActivity();
-
-    if( act!=null )
-      {
-      act.runOnUiThread(new Runnable()
-        {
-        @Override
-        public void run()
-          {
-          RubikDialogUpdateView view = mPanes.get(ordinal);
-          if( view!=null ) view.setIcon(icon,downloaded);
-          }
-        });
-      }
-    }
-  }
diff --git a/src/main/java/org/distorted/info/InfoActivity.java b/src/main/java/org/distorted/info/InfoActivity.java
index ad7c1c62..6920986a 100644
--- a/src/main/java/org/distorted/info/InfoActivity.java
+++ b/src/main/java/org/distorted/info/InfoActivity.java
@@ -18,7 +18,7 @@ import org.distorted.library.main.DistortedLibrary;
 import org.distorted.objectlib.main.InitAssets;
 import org.distorted.objectlib.main.ObjectControl;
 import org.distorted.main.R;
-import org.distorted.dialogs.RubikDialogError;
+import org.distorted.dialogs.DialogError;
 import org.distorted.objectlib.main.TwistyObject;
 import org.distorted.objects.RubikObject;
 import org.distorted.objects.RubikObjectList;
@@ -93,7 +93,7 @@ public class InfoActivity extends BaseActivity
 
     void OpenGLError()
       {
-      RubikDialogError errDiag = new RubikDialogError();
+      DialogError errDiag = new DialogError();
       errDiag.show(getSupportFragmentManager(), null);
       }
 
diff --git a/src/main/java/org/distorted/main/MainActivity.java b/src/main/java/org/distorted/main/MainActivity.java
index 4eef2c94..49048e8f 100644
--- a/src/main/java/org/distorted/main/MainActivity.java
+++ b/src/main/java/org/distorted/main/MainActivity.java
@@ -31,11 +31,11 @@ import org.distorted.bandaged.BandagedActivity;
 import org.distorted.config.ConfigActivity;
 import org.distorted.helpers.BaseActivity;
 import org.distorted.info.InfoActivity;
-import org.distorted.dialogs.RubikDialogAbout;
-import org.distorted.dialogs.RubikDialogCreators;
-import org.distorted.dialogs.RubikDialogExit;
-import org.distorted.dialogs.RubikDialogScores;
-import org.distorted.dialogs.RubikDialogUpdates;
+import org.distorted.dialogs.DialogAbout;
+import org.distorted.dialogs.DialogCreators;
+import org.distorted.dialogs.DialogExit;
+import org.distorted.dialogs.DialogScores;
+import org.distorted.dialogs.DialogUpdates;
 import org.distorted.external.RubikNetwork;
 import org.distorted.external.RubikScores;
 import org.distorted.external.RubikUpdates;
@@ -49,7 +49,7 @@ import org.distorted.tutorials.TutorialActivity;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-public class MainActivity extends BaseActivity implements RubikNetwork.Updatee, RubikDialogScores.ScoresInvoker
+public class MainActivity extends BaseActivity implements RubikNetwork.Updatee, DialogScores.ScoresInvoker
 {
     private boolean mJustStarted;
     private FirebaseAnalytics mFirebaseAnalytics;
@@ -168,9 +168,9 @@ public class MainActivity extends BaseActivity implements RubikNetwork.Updatee,
       {
       Bundle bundle = new Bundle();
       bundle.putString("argument",mOldVersion);
-      RubikDialogAbout newDialog = new RubikDialogAbout();
+      DialogAbout newDialog = new DialogAbout();
       newDialog.setArguments(bundle);
-      newDialog.show(getSupportFragmentManager(), RubikDialogAbout.getDialogTag() );
+      newDialog.show(getSupportFragmentManager(), DialogAbout.getDialogTag());
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -355,7 +355,7 @@ public class MainActivity extends BaseActivity implements RubikNetwork.Updatee,
       {
       Bundle sBundle = new Bundle();
       sBundle.putString("argument", "false");
-      RubikDialogScores scores = new RubikDialogScores();
+      DialogScores scores = new DialogScores();
       scores.setArguments(sBundle);
       scores.show(getSupportFragmentManager(), null);
       }
@@ -377,15 +377,15 @@ public class MainActivity extends BaseActivity implements RubikNetwork.Updatee,
 
     public void onUpdates(View v)
       {
-      RubikDialogUpdates diag = new RubikDialogUpdates();
-      diag.show( getSupportFragmentManager(), RubikDialogUpdates.getDialogTag() );
+      DialogUpdates diag = new DialogUpdates();
+      diag.show( getSupportFragmentManager(), DialogUpdates.getDialogTag());
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
     public void onExit(View v)
       {
-      RubikDialogExit diag = new RubikDialogExit();
+      DialogExit diag = new DialogExit();
       diag.show(getSupportFragmentManager(), null);
       }
 
@@ -393,7 +393,7 @@ public class MainActivity extends BaseActivity implements RubikNetwork.Updatee,
 
     public void onAbout(View v)
       {
-      RubikDialogAbout diag = new RubikDialogAbout();
+      DialogAbout diag = new DialogAbout();
       diag.show(getSupportFragmentManager(), null);
       }
 
@@ -401,8 +401,8 @@ public class MainActivity extends BaseActivity implements RubikNetwork.Updatee,
 
     public void onBandage(View v)
       {
-      RubikDialogCreators diag = new RubikDialogCreators();
-      diag.show(getSupportFragmentManager(), RubikDialogCreators.getDialogTag() );
+      DialogCreators diag = new DialogCreators();
+      diag.show(getSupportFragmentManager(), DialogCreators.getDialogTag());
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/patternui/PatternActivity.java b/src/main/java/org/distorted/patternui/PatternActivity.java
index 456ef46c..57e49943 100644
--- a/src/main/java/org/distorted/patternui/PatternActivity.java
+++ b/src/main/java/org/distorted/patternui/PatternActivity.java
@@ -16,7 +16,7 @@ import android.view.DisplayCutout;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
-import org.distorted.dialogs.RubikDialogError;
+import org.distorted.dialogs.DialogError;
 import org.distorted.helpers.BaseActivity;
 import org.distorted.library.main.DistortedLibrary;
 import org.distorted.library.main.DistortedScreen;
@@ -148,7 +148,7 @@ public class PatternActivity extends BaseActivity
 
     void OpenGLError()
       {
-      RubikDialogError errDiag = new RubikDialogError();
+      DialogError errDiag = new DialogError();
       errDiag.show(getSupportFragmentManager(), null);
       }
 
diff --git a/src/main/java/org/distorted/patternui/ScreenPattern.java b/src/main/java/org/distorted/patternui/ScreenPattern.java
index 4d47e6c2..74ca1060 100644
--- a/src/main/java/org/distorted/patternui/ScreenPattern.java
+++ b/src/main/java/org/distorted/patternui/ScreenPattern.java
@@ -17,7 +17,7 @@ import android.view.View;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
-import org.distorted.dialogs.RubikDialogPattern;
+import org.distorted.dialogs.DialogPattern;
 import org.distorted.helpers.TransparentButton;
 import org.distorted.helpers.TransparentImageButton;
 import org.distorted.main.R;
@@ -127,9 +127,9 @@ public class ScreenPattern extends ScreenAbstract
     int ordinal = act.getObjectOrdinal();
     Bundle bundle = new Bundle();
     bundle.putString("argument", String.valueOf(ordinal) );
-    RubikDialogPattern diag = new RubikDialogPattern();
+    DialogPattern diag = new DialogPattern();
     diag.setArguments(bundle);
-    diag.show( act.getSupportFragmentManager(), RubikDialogPattern.getDialogTag() );
+    diag.show( act.getSupportFragmentManager(), DialogPattern.getDialogTag());
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/playui/PlayActivity.java b/src/main/java/org/distorted/playui/PlayActivity.java
index 9b222d7a..dea8c277 100644
--- a/src/main/java/org/distorted/playui/PlayActivity.java
+++ b/src/main/java/org/distorted/playui/PlayActivity.java
@@ -20,14 +20,14 @@ import android.widget.LinearLayout;
 
 import com.google.firebase.analytics.FirebaseAnalytics;
 
-import org.distorted.dialogs.RubikDialogScores;
+import org.distorted.dialogs.DialogScores;
 import org.distorted.external.RubikScores;
 import org.distorted.helpers.BaseActivity;
 import org.distorted.library.main.DistortedLibrary;
 import org.distorted.objectlib.main.InitAssets;
 import org.distorted.objectlib.main.ObjectControl;
 import org.distorted.objectlib.main.TwistyObject;
-import org.distorted.dialogs.RubikDialogError;
+import org.distorted.dialogs.DialogError;
 import org.distorted.external.RubikFiles;
 import org.distorted.main.R;
 import org.distorted.objects.RubikObject;
@@ -36,7 +36,7 @@ import org.distorted.os.OSInterface;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-public class PlayActivity extends BaseActivity implements RubikDialogScores.ScoresInvoker
+public class PlayActivity extends BaseActivity implements DialogScores.ScoresInvoker
 {
     public static final float TITLE_TEXT_SIZE= 0.060f;
     private static final int ACTIVITY_NUMBER = 6;
@@ -244,7 +244,7 @@ public class PlayActivity extends BaseActivity implements RubikDialogScores.Scor
 
     void OpenGLError()
       {
-      RubikDialogError errDiag = new RubikDialogError();
+      DialogError errDiag = new DialogError();
       errDiag.show(getSupportFragmentManager(), null);
       }
 
diff --git a/src/main/java/org/distorted/playui/PlayLibInterface.java b/src/main/java/org/distorted/playui/PlayLibInterface.java
index 636481e3..80adc7c2 100644
--- a/src/main/java/org/distorted/playui/PlayLibInterface.java
+++ b/src/main/java/org/distorted/playui/PlayLibInterface.java
@@ -28,9 +28,9 @@ import com.google.android.gms.tasks.Task;
 import com.google.firebase.analytics.FirebaseAnalytics;
 import com.google.firebase.crashlytics.FirebaseCrashlytics;
 
-import org.distorted.dialogs.RubikDialogNewRecord;
-import org.distorted.dialogs.RubikDialogScoresView;
-import org.distorted.dialogs.RubikDialogSolved;
+import org.distorted.dialogs.DialogNewRecord;
+import org.distorted.dialogs.DialogScoresView;
+import org.distorted.dialogs.DialogSolved;
 import org.distorted.external.RubikNetwork;
 import org.distorted.external.RubikScores;
 import org.distorted.library.message.EffectMessageSender;
@@ -139,7 +139,7 @@ public class PlayLibInterface implements ObjectLibInterface
   private Bundle createDialogBundle()
     {
     Bundle bundle = new Bundle();
-    String arg = RubikDialogScoresView.formatRecord(mNewRecord);
+    String arg = DialogScoresView.formatRecord(mNewRecord);
     bundle.putString("argument", arg );
     return bundle;
     }
@@ -312,14 +312,14 @@ public class PlayLibInterface implements ObjectLibInterface
         {
         case RECORD_FIRST  :
         case RECORD_NEW    : Bundle byes = createDialogBundle();
-                             RubikDialogNewRecord dyes = new RubikDialogNewRecord();
+                             DialogNewRecord dyes = new DialogNewRecord();
                              dyes.setArguments(byes);
-                             dyes.show( act.getSupportFragmentManager(), RubikDialogNewRecord.getDialogTag() );
+                             dyes.show( act.getSupportFragmentManager(), DialogNewRecord.getDialogTag());
                              break;
         case RECORD_NOT_NEW: Bundle bno = createDialogBundle();
-                             RubikDialogSolved dno = new RubikDialogSolved();
+                             DialogSolved dno = new DialogSolved();
                              dno.setArguments(bno);
-                             dno.show( act.getSupportFragmentManager(), RubikDialogSolved.getDialogTag() );
+                             dno.show( act.getSupportFragmentManager(), DialogSolved.getDialogTag());
         break;
         }
 
diff --git a/src/main/java/org/distorted/playui/ScreenDone.java b/src/main/java/org/distorted/playui/ScreenDone.java
index fd61e289..696680aa 100644
--- a/src/main/java/org/distorted/playui/ScreenDone.java
+++ b/src/main/java/org/distorted/playui/ScreenDone.java
@@ -18,8 +18,8 @@ import android.widget.TextView;
 
 import androidx.fragment.app.FragmentManager;
 
-import org.distorted.dialogs.RubikDialogNewRecord;
-import org.distorted.dialogs.RubikDialogSolved;
+import org.distorted.dialogs.DialogNewRecord;
+import org.distorted.dialogs.DialogSolved;
 import org.distorted.helpers.TransparentImageButton;
 import org.distorted.main.R;
 
@@ -86,8 +86,8 @@ public class ScreenDone extends ScreenAbstract
         ScreenList.goBack(act);
 
         FragmentManager mana = act.getSupportFragmentManager();
-        RubikDialogNewRecord diag1 = (RubikDialogNewRecord) mana.findFragmentByTag(RubikDialogNewRecord.getDialogTag());
-        RubikDialogSolved    diag2 = (RubikDialogSolved   ) mana.findFragmentByTag(RubikDialogSolved.getDialogTag());
+        DialogNewRecord diag1 = (DialogNewRecord) mana.findFragmentByTag(DialogNewRecord.getDialogTag());
+        DialogSolved diag2 = (DialogSolved) mana.findFragmentByTag(DialogSolved.getDialogTag());
 
         if( diag1 !=null ) diag1.dismiss();
         if( diag2 !=null ) diag2.dismiss();
diff --git a/src/main/java/org/distorted/playui/ScreenSolving.java b/src/main/java/org/distorted/playui/ScreenSolving.java
index 979ac2ca..7f14c007 100644
--- a/src/main/java/org/distorted/playui/ScreenSolving.java
+++ b/src/main/java/org/distorted/playui/ScreenSolving.java
@@ -16,7 +16,7 @@ import android.view.View;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
-import org.distorted.dialogs.RubikDialogAbandon;
+import org.distorted.dialogs.DialogAbandon;
 import org.distorted.external.RubikScores;
 import org.distorted.helpers.TransparentImageButton;
 import org.distorted.main.R;
@@ -92,7 +92,7 @@ public class ScreenSolving extends ScreenBase
         {
         if( mMovesController.getNumMoves() > MOVES_THRESHHOLD )
           {
-          RubikDialogAbandon abaDiag = new RubikDialogAbandon();
+          DialogAbandon abaDiag = new DialogAbandon();
           abaDiag.show(act.getSupportFragmentManager(), null);
           }
         else
diff --git a/src/main/java/org/distorted/solverui/ScreenSetupPosition.java b/src/main/java/org/distorted/solverui/ScreenSetupPosition.java
index 301c45d6..3fb995fd 100644
--- a/src/main/java/org/distorted/solverui/ScreenSetupPosition.java
+++ b/src/main/java/org/distorted/solverui/ScreenSetupPosition.java
@@ -26,9 +26,9 @@ import android.widget.LinearLayout;
 
 import androidx.core.content.ContextCompat;
 
-import org.distorted.dialogs.RubikDialogSolverError;
-import org.distorted.dialogs.RubikDialogSolverImpossible;
-import org.distorted.dialogs.RubikDialogSolvers;
+import org.distorted.dialogs.DialogSolverError;
+import org.distorted.dialogs.DialogSolverImpossible;
+import org.distorted.dialogs.DialogSolvers;
 import org.distorted.helpers.TransparentImageButton;
 import org.distorted.main.MainActivity;
 import org.distorted.main.R;
@@ -348,9 +348,9 @@ public class ScreenSetupPosition extends ScreenAbstract implements ResultScreen
             String upperName = objList.name();
             Bundle bundle = new Bundle();
             bundle.putString("argument", upperName );
-            RubikDialogSolvers solv = new RubikDialogSolvers();
+            DialogSolvers solv = new DialogSolvers();
             solv.setArguments(bundle);
-            solv.show( act.getSupportFragmentManager(), RubikDialogSolvers.getDialogTag() );
+            solv.show( act.getSupportFragmentManager(), DialogSolvers.getDialogTag());
             return false;
             }
           }
@@ -584,7 +584,7 @@ public class ScreenSetupPosition extends ScreenAbstract implements ResultScreen
 
     if( act!=null )
       {
-      RubikDialogSolverError dialog = new RubikDialogSolverError();
+      DialogSolverError dialog = new DialogSolverError();
       Bundle bundle = new Bundle();
       bundle.putString("argument", message );
       dialog.setArguments(bundle);
@@ -601,7 +601,7 @@ public class ScreenSetupPosition extends ScreenAbstract implements ResultScreen
 
     if( act!=null )
       {
-      RubikDialogSolverImpossible dialog = new RubikDialogSolverImpossible();
+      DialogSolverImpossible dialog = new DialogSolverImpossible();
       Bundle bundle = new Bundle();
       bundle.putString("argument", message );
       dialog.setArguments(bundle);
diff --git a/src/main/java/org/distorted/solverui/SolverActivity.java b/src/main/java/org/distorted/solverui/SolverActivity.java
index eb063b85..876c6f54 100644
--- a/src/main/java/org/distorted/solverui/SolverActivity.java
+++ b/src/main/java/org/distorted/solverui/SolverActivity.java
@@ -16,8 +16,8 @@ import android.view.DisplayCutout;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
-import org.distorted.dialogs.RubikDialogError;
-import org.distorted.dialogs.RubikDialogMessage;
+import org.distorted.dialogs.DialogError;
+import org.distorted.dialogs.DialogMessage;
 import org.distorted.helpers.BaseActivity;
 import org.distorted.library.main.DistortedLibrary;
 import org.distorted.library.main.DistortedScreen;
@@ -160,9 +160,9 @@ public class SolverActivity extends BaseActivity
         {
         Bundle bundle = new Bundle();
         bundle.putString("argument", getString(R.string.solver_message) );
-        RubikDialogMessage diag = new RubikDialogMessage();
+        DialogMessage diag = new DialogMessage();
         diag.setArguments(bundle);
-        diag.show( getSupportFragmentManager(), RubikDialogMessage.getDialogTag() );
+        diag.show( getSupportFragmentManager(), DialogMessage.getDialogTag());
         }
       }
 
@@ -177,7 +177,7 @@ public class SolverActivity extends BaseActivity
 
     void OpenGLError()
       {
-      RubikDialogError errDiag = new RubikDialogError();
+      DialogError errDiag = new DialogError();
       errDiag.show(getSupportFragmentManager(), null);
       }
 
diff --git a/src/main/java/org/distorted/tutorials/TutorialActivity.java b/src/main/java/org/distorted/tutorials/TutorialActivity.java
index 6740393f..ec908eb2 100644
--- a/src/main/java/org/distorted/tutorials/TutorialActivity.java
+++ b/src/main/java/org/distorted/tutorials/TutorialActivity.java
@@ -16,7 +16,7 @@ import android.view.ViewGroup;
 import android.webkit.WebView;
 import android.widget.LinearLayout;
 
-import org.distorted.dialogs.RubikDialogTutorial;
+import org.distorted.dialogs.DialogTutorial;
 import org.distorted.helpers.BaseActivity;
 import org.distorted.library.main.DistortedLibrary;
 
@@ -25,7 +25,7 @@ import org.distorted.objectlib.main.ObjectControl;
 import org.distorted.objectlib.main.TwistyObject;
 
 import org.distorted.main.R;
-import org.distorted.dialogs.RubikDialogError;
+import org.distorted.dialogs.DialogError;
 import org.distorted.objects.RubikObject;
 import org.distorted.objects.RubikObjectList;
 import org.distorted.os.OSInterface;
@@ -88,9 +88,9 @@ public class TutorialActivity extends BaseActivity
         {
         Bundle bundle = new Bundle();
         bundle.putString("argument", String.valueOf(mObjectOrdinal));
-        RubikDialogTutorial diag = new RubikDialogTutorial();
+        DialogTutorial diag = new DialogTutorial();
         diag.setArguments(bundle);
-        diag.show(getSupportFragmentManager(), RubikDialogTutorial.getDialogTag());
+        diag.show(getSupportFragmentManager(), DialogTutorial.getDialogTag());
         }
       }
 
@@ -166,7 +166,7 @@ public class TutorialActivity extends BaseActivity
 
     void OpenGLError()
       {
-      RubikDialogError errDiag = new RubikDialogError();
+      DialogError errDiag = new DialogError();
       errDiag.show(getSupportFragmentManager(), null);
       }
 
