commit 1237d25d18665a5bed8591489655dc65ef08317b
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Wed Nov 24 17:36:59 2021 +0100

    Beginnings of the ConfigActivity.

diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index a91c501e..4be95db4 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -29,5 +29,6 @@
         </activity>
 
         <activity android:name="org.distorted.tutorials.TutorialActivity" android:exported="true" android:screenOrientation="portrait"/>
+        <activity android:name="org.distorted.config.ConfigActivity" android:exported="true" android:screenOrientation="portrait"/>
     </application>
 </manifest>
diff --git a/src/main/java/org/distorted/config/ConfigActivity.java b/src/main/java/org/distorted/config/ConfigActivity.java
new file mode 100644
index 00000000..a386ca10
--- /dev/null
+++ b/src/main/java/org/distorted/config/ConfigActivity.java
@@ -0,0 +1,304 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2019 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is free software: you can redistribute it and/or modify                            //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Magic Cube is distributed in the hope that it will be useful,                                 //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.config;
+
+import android.os.Build;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.view.DisplayCutout;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.google.firebase.analytics.FirebaseAnalytics;
+
+import org.distorted.dialogs.RubikDialogError;
+import org.distorted.dmesh.ObjectMesh;
+import org.distorted.jsons.ObjectJson;
+import org.distorted.library.main.DistortedLibrary;
+import org.distorted.main.R;
+import org.distorted.objectlib.main.ObjectControl;
+import org.distorted.objectlib.main.ObjectType;
+import org.distorted.tutorials.TutorialScreen;
+
+import java.io.InputStream;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class ConfigActivity extends AppCompatActivity
+{
+    private static final int ACTIVITY_NUMBER = 2;
+    private static final float RATIO_INSET= 0.08f;
+    public static final float BAR_RATIO = 0.17f;
+
+    public static final float DIALOG_BUTTON_SIZE  = 0.06f;
+    public static final float MENU_BIG_TEXT_SIZE  = 0.05f;
+
+    public static final int FLAGS =  View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                                   | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                                   | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+                                   | View.SYSTEM_UI_FLAG_FULLSCREEN
+                                   | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+
+    private FirebaseAnalytics mFirebaseAnalytics;
+    private static int mScreenWidth, mScreenHeight;
+    private int mCurrentApiVersion;
+    private ConfigScreen mScreen;
+    private int mObjectOrdinal;
+    private int mHeightUpperBar;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    @Override
+    protected void onCreate(Bundle savedState)
+      {
+      super.onCreate(savedState);
+      DistortedLibrary.onCreate(ACTIVITY_NUMBER);
+      setTheme(R.style.MaterialThemeNoActionBar);
+      setContentView(R.layout.config);
+
+      Bundle b = getIntent().getExtras();
+
+      if(b != null) mObjectOrdinal = b.getInt("obj");
+
+      mFirebaseAnalytics = FirebaseAnalytics.getInstance(this);
+
+      DisplayMetrics displaymetrics = new DisplayMetrics();
+      getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
+      mScreenWidth =displaymetrics.widthPixels;
+      mScreenHeight=displaymetrics.heightPixels;
+
+      hideNavigationBar();
+      cutoutHack();
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private void hideNavigationBar()
+      {
+      mCurrentApiVersion = Build.VERSION.SDK_INT;
+
+      if(mCurrentApiVersion >= Build.VERSION_CODES.KITKAT)
+        {
+        final View decorView = getWindow().getDecorView();
+
+        decorView.setSystemUiVisibility(FLAGS);
+
+        decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener()
+          {
+          @Override
+          public void onSystemUiVisibilityChange(int visibility)
+            {
+            if((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0)
+              {
+              decorView.setSystemUiVisibility(FLAGS);
+              }
+            }
+          });
+        }
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    @Override
+    public void onAttachedToWindow()
+      {
+      super.onAttachedToWindow();
+
+      int insetHeight = 0;
+
+      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
+        {
+        DisplayCutout cutout = getWindow().getDecorView().getRootWindowInsets().getDisplayCutout();
+        insetHeight = cutout!=null ? cutout.getSafeInsetTop() : 0;
+        }
+
+      LinearLayout layoutHid = findViewById(R.id.hiddenBar);
+      ViewGroup.LayoutParams paramsHid = layoutHid.getLayoutParams();
+      paramsHid.height = (int)(insetHeight*RATIO_INSET);
+      layoutHid.setLayoutParams(paramsHid);
+      mHeightUpperBar += paramsHid.height;
+
+      if( mScreen==null ) mScreen = new ConfigScreen();
+      mScreen.createScreen(this);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// do not avoid cutouts
+
+    private void cutoutHack()
+      {
+      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
+        {
+        getWindow().getAttributes().layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        }
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    @Override
+    public void onWindowFocusChanged(boolean hasFocus)
+      {
+      super.onWindowFocusChanged(hasFocus);
+
+      if(mCurrentApiVersion >= Build.VERSION_CODES.KITKAT && hasFocus)
+        {
+        getWindow().getDecorView().setSystemUiVisibility(FLAGS);
+        }
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+    
+    @Override
+    protected void onPause() 
+      {
+      super.onPause();
+      ConfigSurfaceView view = findViewById(R.id.configSurfaceView);
+      view.onPause();
+      DistortedLibrary.onPause(ACTIVITY_NUMBER);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+    
+    @Override
+    protected void onResume() 
+      {
+      super.onResume();
+      DistortedLibrary.onResume(ACTIVITY_NUMBER);
+      ConfigSurfaceView view = findViewById(R.id.configSurfaceView);
+      view.onResume();
+
+      if( mObjectOrdinal>=0 && mObjectOrdinal< ObjectType.NUM_OBJECTS )
+        {
+        ObjectType obj = ObjectType.getObject(mObjectOrdinal);
+        changeIfDifferent(obj,view.getObjectControl());
+        }
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+    
+    @Override
+    protected void onDestroy() 
+      {
+      super.onDestroy();
+      DistortedLibrary.onDestroy(ACTIVITY_NUMBER);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    void OpenGLError()
+      {
+      RubikDialogError errDiag = new RubikDialogError();
+      errDiag.show(getSupportFragmentManager(), null);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private void changeIfDifferent(ObjectType type,ObjectControl control)
+      {
+      InputStream jsonStream = ObjectJson.getStream(type,this);
+      InputStream meshStream = ObjectMesh.getStream(type,this);
+      control.changeIfDifferent(type.ordinal(),jsonStream,meshStream);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    ConfigScreen getState()
+      {
+      return mScreen;
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public FirebaseAnalytics getAnalytics()
+      {
+      return mFirebaseAnalytics;
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public int getHeightUpperBar()
+      {
+      return mHeightUpperBar;
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public int getScreenWidthInPixels()
+      {
+      return mScreenWidth;
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public ObjectControl getControl()
+      {
+      ConfigSurfaceView view = findViewById(R.id.configSurfaceView);
+      return view.getObjectControl();
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public static int getDrawableSize()
+      {
+      if( mScreenHeight<1000 )
+        {
+        return 0;
+        }
+      if( mScreenHeight<1600 )
+        {
+        return 1;
+        }
+      if( mScreenHeight<1900 )
+        {
+        return 2;
+        }
+
+      return 3;
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public static int getDrawable(int small, int medium, int big, int huge)
+      {
+      int size = getDrawableSize();
+
+      switch(size)
+        {
+        case 0 : return small;
+        case 1 : return medium;
+        case 2 : return big;
+        default: return huge;
+        }
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public boolean isVertical()
+      {
+      ConfigSurfaceView view = findViewById(R.id.configSurfaceView);
+      return view.isVertical();
+      }
+}
diff --git a/src/main/java/org/distorted/config/ConfigObjectLibInterface.java b/src/main/java/org/distorted/config/ConfigObjectLibInterface.java
new file mode 100644
index 00000000..dff776cd
--- /dev/null
+++ b/src/main/java/org/distorted/config/ConfigObjectLibInterface.java
@@ -0,0 +1,139 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2021 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is free software: you can redistribute it and/or modify                            //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Magic Cube is distributed in the hope that it will be useful,                                 //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.config;
+
+import com.google.firebase.crashlytics.FirebaseCrashlytics;
+
+import org.distorted.library.message.EffectMessageSender;
+import org.distorted.objectlib.BuildConfig;
+import org.distorted.objectlib.helpers.BlockController;
+import org.distorted.objectlib.helpers.ObjectLibInterface;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class ConfigObjectLibInterface implements ObjectLibInterface
+{
+  public void onWinEffectFinished(String debug, int scrambleNum) { }
+  public void onScrambleEffectFinished() { }
+  public void onBeginRotation() { }
+  public void onSolved() { }
+  public void onObjectCreated(long time) { }
+  public void onReplaceModeDown(int cubit, int face) { }
+  public void onReplaceModeUp() { }
+  public void onFinishRotation(int axis, int row, int angle) { }
+  public void failedToDrag() { }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void reportProblem(String problem, boolean reportException)
+    {
+    if( BuildConfig.DEBUG )
+      {
+      android.util.Log.e("interface", problem);
+      }
+    else
+      {
+      if( reportException )
+        {
+        Exception ex = new Exception(problem);
+        FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
+        crashlytics.setCustomKey("problem" , problem);
+        crashlytics.recordException(ex);
+        }
+      else
+        {
+        FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
+        crashlytics.log(problem);
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void reportUIProblem(int place, long pause, long resume, long time)
+    {
+    String error = "UI BLOCK "+place+" blocked for "+time;
+
+    if( BuildConfig.DEBUG )
+       {
+       android.util.Log.e("D", error);
+       }
+    else
+      {
+      Exception ex = new Exception(error);
+      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
+      crashlytics.setCustomKey("pause" , pause );
+      crashlytics.setCustomKey("resume", resume );
+      crashlytics.recordException(ex);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void reportTouchProblem(int place, long pause, long resume, long time)
+    {
+    String error = "TOUCH BLOCK "+place+" blocked for "+time;
+
+    if( BuildConfig.DEBUG )
+       {
+       android.util.Log.e("D", error);
+       }
+    else
+      {
+      Exception ex = new Exception(error);
+      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
+      crashlytics.setCustomKey("pause" , pause );
+      crashlytics.setCustomKey("resume", resume);
+      crashlytics.recordException(ex);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void reportThreadProblem(int place, long pause, long resume, long time)
+    {
+    String error = EffectMessageSender.reportState();
+
+    if( BuildConfig.DEBUG )
+       {
+       android.util.Log.e("D", error);
+       }
+    else
+      {
+      Exception ex = new Exception(error);
+      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
+      crashlytics.setCustomKey("pause" , pause  );
+      crashlytics.setCustomKey("resume", resume );
+      crashlytics.recordException(ex);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void reportBlockProblem(int type, int place, long pause, long resume, long time)
+    {
+    switch(type)
+      {
+      case BlockController.TYPE_UI    : reportUIProblem(place,pause,resume,time); break;
+      case BlockController.TYPE_TOUCH : reportTouchProblem(place,pause,resume,time); break;
+      case BlockController.TYPE_THREAD: reportThreadProblem(place,pause,resume,time); break;
+      }
+    }
+}
diff --git a/src/main/java/org/distorted/config/ConfigRenderer.java b/src/main/java/org/distorted/config/ConfigRenderer.java
new file mode 100644
index 00000000..acc691d2
--- /dev/null
+++ b/src/main/java/org/distorted/config/ConfigRenderer.java
@@ -0,0 +1,97 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2019 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is free software: you can redistribute it and/or modify                            //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Magic Cube is distributed in the hope that it will be useful,                                 //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.config;
+
+import android.opengl.GLSurfaceView;
+
+import org.distorted.library.effect.EffectType;
+import org.distorted.library.effect.VertexEffectQuaternion;
+import org.distorted.library.effect.VertexEffectRotate;
+import org.distorted.library.main.DistortedLibrary;
+import org.distorted.library.main.DistortedScreen;
+import org.distorted.objectlib.effects.BaseEffect;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class ConfigRenderer implements GLSurfaceView.Renderer, DistortedLibrary.ExceptionListener
+{
+   private final ConfigSurfaceView mView;
+   private final DistortedScreen mScreen;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   ConfigRenderer(ConfigSurfaceView v)
+     {
+     final float BRIGHTNESS = 0.30f;
+
+     mView = v;
+     mScreen = new DistortedScreen();
+     mScreen.glClearColor(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS, 1.0f);
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   @Override
+   public void onDrawFrame(GL10 glUnused)
+     {
+     long time = System.currentTimeMillis();
+     mView.getObjectControl().preRender();
+     mScreen.render(time);
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   @Override
+   public void onSurfaceChanged(GL10 glUnused, int width, int height)
+      {
+      mScreen.resize(width,height);
+      mView.setScreenSize(width,height);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   @Override
+   public void onSurfaceCreated(GL10 glUnused, EGLConfig config)
+      {
+      DistortedLibrary.setMax(EffectType.VERTEX,61);    // 60 Minx quaternions + rotate
+      VertexEffectRotate.enable();
+      VertexEffectQuaternion.enable();
+      BaseEffect.Type.enableEffects();
+
+      DistortedLibrary.onSurfaceCreated(mView.getContext(),this,1);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   public void distortedException(Exception ex)
+     {
+     android.util.Log.e("CONFIG", "unexpected exception: "+ex.getMessage() );
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   DistortedScreen getScreen()
+     {
+     return mScreen;
+     }
+}
diff --git a/src/main/java/org/distorted/config/ConfigScreen.java b/src/main/java/org/distorted/config/ConfigScreen.java
new file mode 100644
index 00000000..a26b40cb
--- /dev/null
+++ b/src/main/java/org/distorted/config/ConfigScreen.java
@@ -0,0 +1,73 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is free software: you can redistribute it and/or modify                            //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Magic Cube is distributed in the hope that it will be useful,                                 //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.config;
+
+import android.view.View;
+import android.widget.LinearLayout;
+
+import org.distorted.helpers.TransparentImageButton;
+import org.distorted.main.R;
+import org.distorted.main.RubikActivity;
+import org.distorted.objectlib.main.ObjectControl;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class ConfigScreen
+{
+  private TransparentImageButton mBackButton;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setupBackButton(final ConfigActivity act)
+    {
+    int icon = RubikActivity.getDrawable(R.drawable.ui_small_smallback,R.drawable.ui_medium_smallback, R.drawable.ui_big_smallback, R.drawable.ui_huge_smallback);
+    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT,1.0f);
+    mBackButton = new TransparentImageButton(act, icon, TransparentImageButton.GRAVITY_MIDDLE, params);
+
+    mBackButton.setOnClickListener( new View.OnClickListener()
+      {
+      @Override
+      public void onClick(View v)
+        {
+        ObjectControl control = act.getControl();
+        if( control!=null ) control.unblockEverything();
+        act.finish();
+        }
+      });
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void createScreen(final ConfigActivity act)
+    {
+    LinearLayout layout = act.findViewById(R.id.lowerBar);
+    layout.removeAllViews();
+    setupBackButton(act);
+    layout.addView(mBackButton);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+
+  public ConfigScreen()
+    {
+
+    }
+}
diff --git a/src/main/java/org/distorted/config/ConfigSurfaceView.java b/src/main/java/org/distorted/config/ConfigSurfaceView.java
new file mode 100644
index 00000000..155a2fd3
--- /dev/null
+++ b/src/main/java/org/distorted/config/ConfigSurfaceView.java
@@ -0,0 +1,138 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is free software: you can redistribute it and/or modify                            //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Magic Cube is distributed in the hope that it will be useful,                                 //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.config;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.pm.ConfigurationInfo;
+import android.opengl.GLES30;
+import android.opengl.GLSurfaceView;
+import android.util.AttributeSet;
+
+import com.google.firebase.crashlytics.FirebaseCrashlytics;
+
+import org.distorted.objectlib.main.ObjectControl;
+import org.distorted.objectlib.main.TwistyObjectNode;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class ConfigSurfaceView extends GLSurfaceView
+{
+    private ObjectControl mObjectController;
+    private ConfigRenderer mRenderer;
+    private int mScreenWidth, mScreenHeight;
+    private boolean mCreated;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    void setScreenSize(int width, int height)
+      {
+      mScreenWidth = width;
+      mScreenHeight= height;
+      mObjectController.setScreenSize(width,height);
+      mObjectController.setObjectScale(0.80f);
+
+      if( !mCreated )
+        {
+        mCreated = true;
+        mObjectController.createNode(width,height);
+        TwistyObjectNode objectNode = mObjectController.getNode();
+        mRenderer.getScreen().attach(objectNode);
+        }
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    boolean isVertical()
+      {
+      return mScreenHeight>mScreenWidth;
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    ObjectControl getObjectControl()
+      {
+      return mObjectController;
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public ConfigSurfaceView(Context context, AttributeSet attrs)
+      {
+      super(context,attrs);
+
+      mCreated = false;
+
+      if(!isInEditMode())
+        {
+        ConfigActivity act = (ConfigActivity)context;
+        ConfigObjectLibInterface ref = new ConfigObjectLibInterface();
+        mObjectController = new ObjectControl(act,ref);
+        mRenderer = new ConfigRenderer(this);
+
+        final ActivityManager activityManager= (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+
+        try
+          {
+          final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
+          int esVersion = configurationInfo.reqGlEsVersion>>16;
+          setEGLContextClientVersion(esVersion);
+          setRenderer(mRenderer);
+          }
+        catch(Exception ex)
+          {
+          act.OpenGLError();
+
+          String shading = GLES30.glGetString(GLES30.GL_SHADING_LANGUAGE_VERSION);
+          String version = GLES30.glGetString(GLES30.GL_VERSION);
+          String vendor  = GLES30.glGetString(GLES30.GL_VENDOR);
+          String renderer= GLES30.glGetString(GLES30.GL_RENDERER);
+
+          FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
+          crashlytics.setCustomKey("GLSL Version"  , shading );
+          crashlytics.setCustomKey("GL version"    , version );
+          crashlytics.setCustomKey("GL Vendor "    , vendor  );
+          crashlytics.setCustomKey("GLSL renderer" , renderer);
+          crashlytics.recordException(ex);
+          }
+        }
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    @Override
+    public void onPause()
+      {
+      super.onPause();
+      mObjectController.onPause();
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    @Override
+    public void onResume()
+      {
+      super.onResume();
+      mObjectController.onResume();
+      }
+}
+
diff --git a/src/main/java/org/distorted/main/RubikActivity.java b/src/main/java/org/distorted/main/RubikActivity.java
index 382d015e..803501bb 100644
--- a/src/main/java/org/distorted/main/RubikActivity.java
+++ b/src/main/java/org/distorted/main/RubikActivity.java
@@ -42,6 +42,7 @@ import androidx.appcompat.app.AppCompatActivity;
 
 import com.google.firebase.analytics.FirebaseAnalytics;
 
+import org.distorted.config.ConfigActivity;
 import org.distorted.dmesh.ObjectMesh;
 import org.distorted.jsons.ObjectJson;
 import org.distorted.library.main.DistortedLibrary;
@@ -90,6 +91,7 @@ public class RubikActivity extends AppCompatActivity
     public static final int FLAGS2=  View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
 
+    private static final int ACTIVITY_NUMBER = 0;
     private static final float RATIO_BAR  = 0.10f;
     private static final float RATIO_INSET= 0.08f;
 
@@ -106,7 +108,7 @@ public class RubikActivity extends AppCompatActivity
     protected void onCreate(Bundle savedState)
       {
       super.onCreate(savedState);
-      DistortedLibrary.onCreate(0);
+      DistortedLibrary.onCreate(ACTIVITY_NUMBER);
 
       setTheme(R.style.MaterialThemeNoActionBar);
       setContentView(R.layout.main);
@@ -225,7 +227,7 @@ public class RubikActivity extends AppCompatActivity
       super.onPause();
       RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
       view.onPause();
-      DistortedLibrary.onPause(0);
+      DistortedLibrary.onPause(ACTIVITY_NUMBER);
       RubikNetwork.onPause();
       savePreferences();
       }
@@ -236,7 +238,7 @@ public class RubikActivity extends AppCompatActivity
     protected void onResume() 
       {
       super.onResume();
-      DistortedLibrary.onResume(0);
+      DistortedLibrary.onResume(ACTIVITY_NUMBER);
       RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
       view.onResume();
       restorePreferences();
@@ -263,7 +265,7 @@ public class RubikActivity extends AppCompatActivity
     @Override
     protected void onDestroy() 
       {
-      DistortedLibrary.onDestroy(0);
+      DistortedLibrary.onDestroy(ACTIVITY_NUMBER);
       super.onDestroy();
       }
 
@@ -587,4 +589,13 @@ public class RubikActivity extends AppCompatActivity
       myIntent.putExtra("obj", object.ordinal());
       startActivity(myIntent);
       }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void switchConfig(ObjectType object)
+      {
+      Intent myIntent = new Intent(this, ConfigActivity.class);
+      myIntent.putExtra("obj", object.ordinal());
+      startActivity(myIntent);
+      }
 }
diff --git a/src/main/java/org/distorted/screens/RubikScreenPlay.java b/src/main/java/org/distorted/screens/RubikScreenPlay.java
index bb1954ba..6d0db2b9 100644
--- a/src/main/java/org/distorted/screens/RubikScreenPlay.java
+++ b/src/main/java/org/distorted/screens/RubikScreenPlay.java
@@ -350,7 +350,7 @@ public class RubikScreenPlay extends RubikScreenBase
       @Override
       public void onClick(View v)
         {
-        android.util.Log.e("D", "BUTTON DET CLICKED!!");
+        act.switchConfig(mObject);
         }
       });
     }
diff --git a/src/main/java/org/distorted/tutorials/TutorialActivity.java b/src/main/java/org/distorted/tutorials/TutorialActivity.java
index 7b90bd23..cea82e2e 100644
--- a/src/main/java/org/distorted/tutorials/TutorialActivity.java
+++ b/src/main/java/org/distorted/tutorials/TutorialActivity.java
@@ -50,6 +50,7 @@ public class TutorialActivity extends AppCompatActivity
 {
     private static final String URL = "https://www.youtube.com/embed/";
 
+    private static final int ACTIVITY_NUMBER = 1;
     public static final float BAR_RATIO = 0.17f;
     public static final float DIALOG_BUTTON_SIZE  = 0.06f;
     public static final float MENU_BIG_TEXT_SIZE  = 0.05f;
@@ -63,7 +64,7 @@ public class TutorialActivity extends AppCompatActivity
     private FirebaseAnalytics mFirebaseAnalytics;
     private static int mScreenWidth, mScreenHeight;
     private int mCurrentApiVersion;
-    private TutorialScreen mState;
+    private TutorialScreen mScreen;
     private String mURL;
     private int mObjectOrdinal;
     private TutorialWebView mWebView;
@@ -74,7 +75,7 @@ public class TutorialActivity extends AppCompatActivity
     protected void onCreate(Bundle savedState)
       {
       super.onCreate(savedState);
-      DistortedLibrary.onCreate(1);
+      DistortedLibrary.onCreate(ACTIVITY_NUMBER);
       setTheme(R.style.MaterialThemeNoActionBar);
       setContentView(R.layout.tutorial);
 
@@ -137,9 +138,9 @@ public class TutorialActivity extends AppCompatActivity
       paramsR.width = (int)(width*BAR_RATIO);
       viewR.setLayoutParams(paramsR);
 
-      if( mState==null ) mState = new TutorialScreen();
+      if( mScreen==null ) mScreen = new TutorialScreen();
 
-      mState.createRightPane(this);
+      mScreen.createRightPane(this);
 
       WebView videoView = findViewById(R.id.tutorialVideoView);
       mWebView = new TutorialWebView(videoView);
@@ -181,7 +182,7 @@ public class TutorialActivity extends AppCompatActivity
 
       if( mWebView!=null ) mWebView.onPause();
 
-      DistortedLibrary.onPause(1);
+      DistortedLibrary.onPause(ACTIVITY_NUMBER);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -190,7 +191,7 @@ public class TutorialActivity extends AppCompatActivity
     protected void onResume() 
       {
       super.onResume();
-      DistortedLibrary.onResume(1);
+      DistortedLibrary.onResume(ACTIVITY_NUMBER);
       TutorialSurfaceView view = findViewById(R.id.tutorialSurfaceView);
       view.onResume();
 
@@ -209,7 +210,7 @@ public class TutorialActivity extends AppCompatActivity
     protected void onDestroy() 
       {
       super.onDestroy();
-      DistortedLibrary.onDestroy(1);
+      DistortedLibrary.onDestroy(ACTIVITY_NUMBER);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -233,7 +234,7 @@ public class TutorialActivity extends AppCompatActivity
 
     TutorialScreen getState()
       {
-      return mState;
+      return mScreen;
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/res/layout/config.xml b/src/main/res/layout/config.xml
new file mode 100644
index 00000000..2f897b3a
--- /dev/null
+++ b/src/main/res/layout/config.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:id="@+id/hiddenBar"
+        android:layout_width="match_parent"
+        android:layout_height="100dp"
+        android:gravity="center"
+        android:orientation="horizontal"
+        android:background="@android:color/transparent">
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/upperBar"
+        android:layout_width="match_parent"
+        android:layout_height="100dp"
+        android:gravity="center"
+        android:orientation="horizontal"
+        android:background="@android:color/transparent">
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:orientation="vertical"
+        android:background="@android:color/transparent">
+
+        <org.distorted.config.ConfigSurfaceView
+           android:id="@+id/configSurfaceView"
+           android:layout_width="match_parent"
+           android:layout_height="0dp"
+           android:layout_weight="1"/>
+
+        <ScrollView
+           android:id="@+id/configScroll"
+           android:layout_width="match_parent"
+           android:layout_height="0dp"
+           android:layout_weight="1"/>
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/lowerBar"
+        android:layout_width="match_parent"
+        android:layout_height="100dp"
+        android:orientation="horizontal"
+        android:background="@android:color/transparent">
+    </LinearLayout>
+
+</LinearLayout>
