commit 5b22f901e66c176b3f373ec16d7daedadbc29ffd
Author: leszek <leszek@koltunski.pl>
Date:   Wed Feb 14 14:31:15 2024 +0100

    rename the 'Config' package to 'Info'

diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 5fc75c60..2416ce07 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -31,7 +31,7 @@
         </activity>
 
         <activity android:name="org.distorted.tutorials.TutorialActivity" android:exported="false" android:screenOrientation="portrait"/>
-        <activity android:name="org.distorted.config.ConfigActivity" android:exported="false" android:screenOrientation="portrait"/>
+        <activity android:name="org.distorted.info.InfoActivity" android:exported="false" android:screenOrientation="portrait"/>
         <activity android:name="org.distorted.bandaged.BandagedActivity" android:exported="false" android:screenOrientation="portrait"/>
         <activity android:name="org.distorted.purchase.PurchaseActivity" android:exported="false" android:screenOrientation="portrait"/>
         <activity android:name="org.distorted.solverui.SolverActivity" android:exported="false" android:screenOrientation="portrait"/>
diff --git a/src/main/java/org/distorted/config/ConfigActivity.java b/src/main/java/org/distorted/config/ConfigActivity.java
deleted file mode 100644
index d52b4cc5..00000000
--- a/src/main/java/org/distorted/config/ConfigActivity.java
+++ /dev/null
@@ -1,239 +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.config;
-
-import java.io.InputStream;
-
-import android.os.Build;
-import android.os.Bundle;
-import android.util.DisplayMetrics;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import androidx.appcompat.app.AppCompatActivity;
-
-import org.distorted.library.main.DistortedLibrary;
-import org.distorted.main.MainActivity;
-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.objectlib.main.TwistyObject;
-import org.distorted.objects.RubikObject;
-import org.distorted.objects.RubikObjectList;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class ConfigActivity extends AppCompatActivity
-{
-    private static final int ACTIVITY_NUMBER = 1;
-    private static final float RATIO_BAR  = MainActivity.RATIO_BAR;
-    public static final int FLAGS = MainActivity.FLAGS;
-
-    private static int mScreenWidth, mScreenHeight;
-    private int mCurrentApiVersion;
-    private ConfigScreen mScreen;
-    private int mObjectOrdinal;
-    private int mHeightBar;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    @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");
-
-      DisplayMetrics displaymetrics = new DisplayMetrics();
-      getWindowManager().getDefaultDisplay().getRealMetrics(displaymetrics);
-      mScreenWidth =displaymetrics.widthPixels;
-      mScreenHeight=displaymetrics.heightPixels;
-
-      hideNavigationBar();
-      cutoutHack();
-      computeBarHeights();
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// this does not include possible insets
-
-    private void computeBarHeights()
-      {
-      int barHeight = (int)(mScreenHeight*RATIO_BAR);
-      mHeightBar = barHeight;
-
-      LinearLayout layout = findViewById(R.id.lowerBar);
-      ViewGroup.LayoutParams params = layout.getLayoutParams();
-      params.height = barHeight;
-      layout.setLayoutParams(params);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    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);
-              }
-            }
-          });
-        }
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// 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( mScreen==null ) mScreen = new ConfigScreen();
-      mScreen.onAttachedToWindow(this,mObjectOrdinal);
-
-      if( mObjectOrdinal>=0 && mObjectOrdinal< RubikObjectList.getNumObjects() )
-        {
-        RubikObject object = RubikObjectList.getObject(mObjectOrdinal);
-        changeIfDifferent(object,mObjectOrdinal,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(RubikObject object,int ordinal,ObjectControl control)
-      {
-      if( object!=null )
-        {
-        int iconMode           = TwistyObject.MODE_NORM;
-        InputStream jsonStream = object.getObjectStream(this);
-        InputStream meshStream = object.getMeshStream(this);
-        String name            = object.getUpperName();
-        InitAssets asset       = new InitAssets(jsonStream,meshStream,null);
-        control.changeIfDifferent(ordinal,name,iconMode,asset);
-        }
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public void changeObject(int ordinal)
-      {
-      mObjectOrdinal = ordinal;
-      RubikObject object = RubikObjectList.getObject(ordinal);
-      ConfigSurfaceView view = findViewById(R.id.configSurfaceView);
-      ObjectControl control = view.getObjectControl();
-      changeIfDifferent(object,ordinal,control);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public int getHeightBar()
-      {
-      return mHeightBar;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public int getScreenWidthInPixels()
-      {
-      return mScreenWidth;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public int getScreenHeightInPixels()
-      {
-      return mScreenHeight;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public ObjectControl getControl()
-      {
-      ConfigSurfaceView view = findViewById(R.id.configSurfaceView);
-      return view.getObjectControl();
-      }
-}
diff --git a/src/main/java/org/distorted/config/ConfigObjectLibInterface.java b/src/main/java/org/distorted/config/ConfigObjectLibInterface.java
deleted file mode 100644
index 419b2bed..00000000
--- a/src/main/java/org/distorted/config/ConfigObjectLibInterface.java
+++ /dev/null
@@ -1,130 +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.config;
-
-import com.google.firebase.crashlytics.FirebaseCrashlytics;
-
-import org.distorted.library.message.EffectMessageSender;
-import org.distorted.main.BuildConfig;
-import org.distorted.objectlib.helpers.BlockController;
-import org.distorted.objectlib.helpers.ObjectLibInterface;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class ConfigObjectLibInterface implements ObjectLibInterface
-{
-  public void onWinEffectFinished(long startTime, long endTime, 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 onRemoveRotation(int axis, int row, int angle) { }
-  public void failedToDrag() { }
-  public void reportJSONError(String error, int ordinal) { }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  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 reportScramblingProblem(int place, long pause, long resume, long time)
-    {
-    String error = "SCRAMBLING 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 reportRotationProblem(int place, long pause, long resume, long time)
-    {
-    String error = "ROTATION 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_SCRAMBLING: reportScramblingProblem(place,pause,resume,time); break;
-      case BlockController.TYPE_ROTATION  : reportRotationProblem(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
deleted file mode 100644
index 75bb7d83..00000000
--- a/src/main/java/org/distorted/config/ConfigRenderer.java
+++ /dev/null
@@ -1,108 +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.config;
-
-import android.content.res.Resources;
-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 org.distorted.objectlib.main.ObjectControl;
-
-import java.io.InputStream;
-
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.opengles.GL10;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class ConfigRenderer implements GLSurfaceView.Renderer, DistortedLibrary.LibraryUser
-{
-   private final ConfigSurfaceView mView;
-   private final Resources mResources;
-   private final DistortedScreen mScreen;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   ConfigRenderer(ConfigSurfaceView v)
-     {
-     final float BRIGHTNESS = 0.333f;
-
-     mView = v;
-     mResources = v.getResources();
-     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);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   DistortedScreen getScreen()
-     {
-     return mScreen;
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   @Override
-   public void onSurfaceCreated(GL10 glUnused, EGLConfig config)
-      {
-      DistortedLibrary.setMax(EffectType.VERTEX, ObjectControl.MAX_QUATS+1);
-      VertexEffectRotate.enable();
-      VertexEffectQuaternion.enable();
-      BaseEffect.Type.enableEffects();
-
-      DistortedLibrary.onSurfaceCreated(this,1);
-      DistortedLibrary.setCull(true);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   public void distortedException(Exception ex)
-     {
-     android.util.Log.e("Config", "unexpected exception: "+ex.getMessage() );
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   public InputStream localFile(int fileID)
-      {
-      return mResources.openRawResource(fileID);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   public void logMessage(String message)
-      {
-      android.util.Log.e("Config", message );
-      }
-}
diff --git a/src/main/java/org/distorted/config/ConfigScreen.java b/src/main/java/org/distorted/config/ConfigScreen.java
deleted file mode 100644
index 62e8d0f8..00000000
--- a/src/main/java/org/distorted/config/ConfigScreen.java
+++ /dev/null
@@ -1,303 +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.config;
-
-import android.os.Build;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.GridLayout;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.PopupWindow;
-import android.widget.ScrollView;
-import android.widget.TextView;
-
-import org.distorted.helpers.ObjectGridCreator;
-
-import org.distorted.helpers.TransparentImageButton;
-import org.distorted.main.MainActivity;
-import org.distorted.main.R;
-import org.distorted.objects.RubikObject;
-import org.distorted.objects.RubikObjectList;
-
-import static android.view.View.inflate;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class ConfigScreen
-{
-  private static final float TEXT_SIZE = 0.0310f;
-  private static final float PADDING   = 0.0060f;
-  private static final float MARGIN    = 0.0024f;
-  private static final int[] mLocation = new int[2];
-  private static final float POPUP_W  = 0.8f;
-
-  private TransparentImageButton mBackButton, mObjectButton, mPrevButton, mNextButton;
-  private TextView mMovesText;
-  private PopupWindow mObjectPopup;
-  private ConfigScreenPane mPane;
-  private int mObjectOrdinal;
-  private float mButtonSize;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setupObjectWindow(final ConfigActivity act, final float width, final float height)
-    {
-    int numObjects= RubikObjectList.getNumObjects();
-    ScrollView view = (ScrollView)inflate( act, R.layout.popup_object_simple, null);
-    GridLayout objectGrid = view.findViewById(R.id.objectGrid);
-
-    int[] objects = new int[numObjects];
-    for(int i=0; i<numObjects; i++) objects[i] = i;
-
-    ObjectGridCreator creator = new ObjectGridCreator(width);
-    creator.createObjectGridClassic(objectGrid,act,objects);
-
-    for(int child=0; child<numObjects; child++)
-      {
-      final RubikObject obj = RubikObjectList.getObject(child);
-      View v = objectGrid.getChildAt(child);
-      ImageButton button = creator.getButton(obj,v);
-      final int ordinal = child;
-
-      button.setOnClickListener( new View.OnClickListener()
-        {
-        @Override
-        public void onClick(View v)
-          {
-          if( mObjectOrdinal!=ordinal )
-            {
-            mObjectOrdinal = ordinal;
-            act.changeObject(mObjectOrdinal);
-            mMovesText.setText(act.getString(R.string.mo_placeholder,mObjectOrdinal+1,numObjects));
-            mPane.updatePane(act,mObjectOrdinal);
-            }
-
-          mObjectPopup.dismiss();
-          }
-        });
-      }
-
-    mObjectPopup = new PopupWindow(act);
-    mObjectPopup.setFocusable(true);
-    mObjectPopup.setContentView(view);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setupBackButton(final ConfigActivity act)
-    {
-    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT);
-    mBackButton = new TransparentImageButton(act,R.drawable.ui_smallback,params);
-
-    mBackButton.setOnClickListener( new View.OnClickListener()
-      {
-      @Override
-      public void onClick(View v)
-        {
-        act.finish();
-        }
-      });
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setupObjectButton(final ConfigActivity act, final int width, final int height)
-    {
-    final int margin= (int)(height*MARGIN);
-    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT);
-    mObjectButton = new TransparentImageButton(act,R.drawable.ui_cube_menu,params);
-
-    mObjectButton.setOnClickListener( new View.OnClickListener()
-      {
-      @Override
-      public void onClick(View view)
-        {
-        if( mObjectPopup==null ) setupObjectWindow(act,width,height);
-        View popupView = mObjectPopup.getContentView();
-        popupView.setSystemUiVisibility(MainActivity.FLAGS);
-        displayPopup(act,view,mObjectPopup,width,height,margin,margin);
-        }
-      });
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// work around lame bugs in Android's version <= 10 pop-up and split-screen modes
-
-  private void displayPopup(ConfigActivity act, View view, PopupWindow window, int w, int h, int xoff, int yoff)
-    {
-    View topLayout = act.findViewById(R.id.mainLayout);
-    boolean isFullScreen;
-
-    if( topLayout!=null )
-      {
-      topLayout.getLocationOnScreen(mLocation);
-      isFullScreen = (mLocation[1]==0);
-      }
-    else
-      {
-      isFullScreen = true;
-      }
-
-    try
-      {
-      // if on Android 11 or we are fullscreen
-      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R || isFullScreen )
-        {
-        window.showAsDropDown(view, xoff, yoff, Gravity.CENTER);
-        window.update(view, w, h);
-        }
-      else  // Android 10 or below in pop-up mode or split-screen mode
-        {
-        view.getLocationOnScreen(mLocation);
-        int width  = view.getWidth();
-        int height = view.getHeight();
-        int x = mLocation[0]+(width-w)/2;
-        int y = mLocation[1]+height+yoff;
-
-        window.showAsDropDown(view);
-        window.update(x,y,w,h);
-        }
-      }
-    catch( IllegalArgumentException iae )
-      {
-      // ignore, this means window is 'not attached to window manager' -
-      // which most probably is because we are already exiting the app.
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void prevObject(ConfigActivity act, int numObjects)
-    {
-    mObjectOrdinal--;
-    if( mObjectOrdinal<0 ) mObjectOrdinal=numObjects-1;
-
-    act.changeObject(mObjectOrdinal);
-    mPane.updatePane(act,mObjectOrdinal);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void nextObject(ConfigActivity act, int numObjects)
-    {
-    mObjectOrdinal++;
-    if( mObjectOrdinal>=numObjects ) mObjectOrdinal=0;
-
-    act.changeObject(mObjectOrdinal);
-    mPane.updatePane(act,mObjectOrdinal);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setupPrevButton(final ConfigActivity act)
-    {
-    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,1.0f);
-    mPrevButton = new TransparentImageButton(act,R.drawable.ui_left,params);
-
-    mPrevButton.setOnClickListener( new View.OnClickListener()
-      {
-      @Override
-      public void onClick(View v)
-        {
-        int numObjects = RubikObjectList.getNumObjects();
-        prevObject(act,numObjects);
-        mMovesText.setText(act.getString(R.string.mo_placeholder,mObjectOrdinal+1,numObjects));
-        }
-      });
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setupNextButton(final ConfigActivity act)
-    {
-    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,1.0f);
-    mNextButton = new TransparentImageButton(act,R.drawable.ui_right,params);
-
-    mNextButton.setOnClickListener( new View.OnClickListener()
-      {
-      @Override
-      public void onClick(View v)
-        {
-        int numObjects = RubikObjectList.getNumObjects();
-        nextObject(act,numObjects);
-        mMovesText.setText(act.getString(R.string.mo_placeholder,mObjectOrdinal+1,numObjects));
-        }
-      });
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setupTextView(final ConfigActivity act, final float height, int numObjects)
-    {
-    int padding = (int)(height*PADDING);
-    int margin  = (int)(height*MARGIN);
-    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,2.0f);
-    params.topMargin    = margin;
-    params.bottomMargin = margin;
-    params.leftMargin   = margin;
-    params.rightMargin  = margin;
-
-    mMovesText = new TextView(act);
-    mMovesText.setTextSize(20);
-    mMovesText.setLayoutParams(params);
-    mMovesText.setPadding(padding,0,padding,0);
-    mMovesText.setGravity(Gravity.CENTER);
-    mMovesText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mButtonSize);
-    mMovesText.setText(act.getString(R.string.mo_placeholder,mObjectOrdinal+1,numObjects));
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void onAttachedToWindow(final ConfigActivity act, final int objectOrdinal)
-    {
-    int numObjects = RubikObjectList.getNumObjects();
-    int width = act.getScreenWidthInPixels();
-    int height= act.getScreenHeightInPixels();
-    int barHeight = act.getHeightBar();
-    mButtonSize = height*TEXT_SIZE;
-    mObjectOrdinal = objectOrdinal;
-
-    LinearLayout.LayoutParams paramsL = new LinearLayout.LayoutParams(width/4, LinearLayout.LayoutParams.MATCH_PARENT);
-    LinearLayout.LayoutParams paramsM = new LinearLayout.LayoutParams(width/2, LinearLayout.LayoutParams.MATCH_PARENT);
-    LinearLayout.LayoutParams paramsR = new LinearLayout.LayoutParams(width/4, LinearLayout.LayoutParams.MATCH_PARENT);
-
-    LinearLayout layoutLeft = new LinearLayout(act);
-    layoutLeft.setLayoutParams(paramsL);
-    LinearLayout layoutMid  = new LinearLayout(act);
-    layoutMid.setLayoutParams(paramsM);
-    LinearLayout layoutRight= new LinearLayout(act);
-    layoutRight.setLayoutParams(paramsR);
-
-    int popupW = (int)(POPUP_W*width);
-    int popupH = (int)(height-barHeight);
-
-    setupObjectButton(act,popupW,popupH);
-    setupPrevButton(act);
-    setupNextButton(act);
-    setupTextView(act,height,numObjects);
-    setupBackButton(act);
-
-    layoutLeft.addView(mObjectButton);
-    layoutMid.addView(mPrevButton);
-    layoutMid.addView(mMovesText);
-    layoutMid.addView(mNextButton);
-    layoutRight.addView(mBackButton);
-
-    LinearLayout layout = act.findViewById(R.id.lowerBar);
-    layout.removeAllViews();
-    layout.addView(layoutLeft);
-    layout.addView(layoutMid);
-    layout.addView(layoutRight);
-
-    mPane = new ConfigScreenPane(act,mObjectOrdinal);
-    }
-}
diff --git a/src/main/java/org/distorted/config/ConfigScreenPane.java b/src/main/java/org/distorted/config/ConfigScreenPane.java
deleted file mode 100644
index f7dbd44a..00000000
--- a/src/main/java/org/distorted/config/ConfigScreenPane.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.config;
-
-import static org.distorted.objects.RubikObjectCategories.DIFF_IDS;
-import static org.distorted.objects.RubikObjectCategories.DIFF_IMAGES;
-
-import java.io.InputStream;
-
-import android.graphics.PorterDuff;
-import android.util.TypedValue;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import org.distorted.main.R;
-import org.distorted.objectlib.json.JsonReader;
-import org.distorted.objects.RubikObject;
-import org.distorted.objects.RubikObjectList;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class ConfigScreenPane
-{
-  private static final int NUM_IDS         = DIFF_IDS.length;
-  private static final float PADDING_RATIO = 0.016f;
-  private static final float TEXT_RATIO    = 0.025f;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void updatePane(ConfigActivity act, int objectOrdinal)
-    {
-    RubikObject object = RubikObjectList.getObject(objectOrdinal);
-
-    if( object!=null )
-      {
-      InputStream stream = object.getObjectStream(act);
-
-      JsonReader reader = new JsonReader();
-      String name,author;
-      int year, difficulty;
-
-      try
-        {
-        reader.parseJsonFileMetadata(stream);
-        name       = reader.getObjectName();
-        author     = reader.getAuthor();
-        year       = reader.getYearOfInvention();
-        difficulty = (int)reader.getDifficulty();
-        }
-      catch(Exception ex)
-        {
-        name = "?";
-        author = "?";
-        year = 0;
-        difficulty = 0;
-        }
-
-      LinearLayout layout = act.findViewById(R.id.configLayout);
-      TextView view = layout.findViewById(R.id.configDetailsName2);
-      view.setText(name);
-      view = layout.findViewById(R.id.configDetailsAuthor2);
-      view.setText(author);
-      view = layout.findViewById(R.id.configDetailsYear2);
-      view.setText( year>0 ? String.valueOf(year) : "?" );
-
-      if( difficulty<0        ) difficulty=0;
-      if( difficulty>=NUM_IDS ) difficulty=NUM_IDS-1;
-
-      for(int i=0; i<NUM_IDS; i++)
-        {
-        ImageView image = layout.findViewById(DIFF_IDS[i]);
-        image.setImageResource(DIFF_IMAGES[i]);
-        image.setColorFilter( difficulty==i ? 0xffff0000 : 0xffffffff, PorterDuff.Mode.MULTIPLY );
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  ConfigScreenPane(final ConfigActivity act, int objectOrdinal)
-    {
-    int height = act.getScreenHeightInPixels();
-    float textSize = height*TEXT_RATIO;
-    int padding = (int)(height*PADDING_RATIO);
-
-    LinearLayout configLayout    = act.findViewById(R.id.configLayout);
-    LinearLayout nameLayout      = configLayout.findViewById(R.id.configLayoutName);
-    LinearLayout difficultyLayout= configLayout.findViewById(R.id.configLayoutDifficulty);
-    LinearLayout authorLayout    = configLayout.findViewById(R.id.configLayoutAuthor);
-    LinearLayout yearLayout      = configLayout.findViewById(R.id.configLayoutYear);
-
-    nameLayout.setPadding(padding,padding,padding,padding/2);
-    difficultyLayout.setPadding(padding,padding/2,padding,padding/2);
-    authorLayout.setPadding(padding,padding/2,padding,padding/2);
-    yearLayout.setPadding(padding,padding/2,padding,padding/2);
-
-    LinearLayout.LayoutParams params1 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 1.00f);
-    params1.bottomMargin = 0;
-    params1.topMargin    = padding;
-    params1.leftMargin   = padding;
-    params1.rightMargin  = padding;
-
-    LinearLayout.LayoutParams params2 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 1.00f);
-    params2.bottomMargin = padding;
-    params2.topMargin    = padding;
-    params2.leftMargin   = padding;
-    params2.rightMargin  = padding;
-
-    nameLayout.setLayoutParams(params1);
-    difficultyLayout.setLayoutParams(params1);
-    authorLayout.setLayoutParams(params1);
-    yearLayout.setLayoutParams(params2);
-
-    TextView text;
-    text = nameLayout.findViewById(R.id.configDetailsName1);
-    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-    text = nameLayout.findViewById(R.id.configDetailsName2);
-    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-    text = authorLayout.findViewById(R.id.configDetailsAuthor1);
-    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-    text = authorLayout.findViewById(R.id.configDetailsAuthor2);
-    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-    text = difficultyLayout.findViewById(R.id.configDifficultyTitle);
-    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-    text = yearLayout.findViewById(R.id.configDetailsYear1);
-    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-    text = yearLayout.findViewById(R.id.configDetailsYear2);
-    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-
-    LinearLayout layoutDiff = difficultyLayout.findViewById(R.id.configDifficultyLayout);
-    layoutDiff.setPadding(padding,padding,padding,padding);
-
-    updatePane(act,objectOrdinal);
-    }
-}
diff --git a/src/main/java/org/distorted/config/ConfigSurfaceView.java b/src/main/java/org/distorted/config/ConfigSurfaceView.java
deleted file mode 100644
index 0c7abece..00000000
--- a/src/main/java/org/distorted/config/ConfigSurfaceView.java
+++ /dev/null
@@ -1,125 +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.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;
-import org.distorted.os.OSInterface;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class ConfigSurfaceView extends GLSurfaceView
-{
-    private ObjectControl mObjectController;
-    private OSInterface mInterface;
-    private ConfigRenderer mRenderer;
-    private int mScreenWidth, mScreenHeight;
-    private boolean mCreated;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    void setScreenSize(int width, int height)
-      {
-      mScreenWidth = width;
-      mScreenHeight= height;
-      mObjectController.setScreenSizeAndScaling(width,height, Math.min(width,height));
-      mObjectController.setObjectScale(1.00f);
-
-      if( !mCreated )
-        {
-        mCreated = true;
-        mObjectController.createNode(width,height);
-        TwistyObjectNode objectNode = mObjectController.getNode();
-        mRenderer.getScreen().attach(objectNode);
-        }
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    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();
-        mInterface = new OSInterface(act,ref);
-        mObjectController = new ObjectControl(mInterface);
-        mObjectController.setRotateOnCreation(true);
-        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/info/InfoActivity.java b/src/main/java/org/distorted/info/InfoActivity.java
new file mode 100644
index 00000000..61c8c383
--- /dev/null
+++ b/src/main/java/org/distorted/info/InfoActivity.java
@@ -0,0 +1,239 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.info;
+
+import java.io.InputStream;
+
+import android.os.Build;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import androidx.appcompat.app.AppCompatActivity;
+
+import org.distorted.library.main.DistortedLibrary;
+import org.distorted.main.MainActivity;
+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.objectlib.main.TwistyObject;
+import org.distorted.objects.RubikObject;
+import org.distorted.objects.RubikObjectList;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class InfoActivity extends AppCompatActivity
+{
+    private static final int ACTIVITY_NUMBER = 1;
+    private static final float RATIO_BAR  = MainActivity.RATIO_BAR;
+    public static final int FLAGS = MainActivity.FLAGS;
+
+    private static int mScreenWidth, mScreenHeight;
+    private int mCurrentApiVersion;
+    private InfoScreen mScreen;
+    private int mObjectOrdinal;
+    private int mHeightBar;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    @Override
+    protected void onCreate(Bundle savedState)
+      {
+      super.onCreate(savedState);
+      DistortedLibrary.onCreate(ACTIVITY_NUMBER);
+      setTheme(R.style.MaterialThemeNoActionBar);
+      setContentView(R.layout.info);
+
+      Bundle b = getIntent().getExtras();
+
+      if(b != null) mObjectOrdinal = b.getInt("obj");
+
+      DisplayMetrics displaymetrics = new DisplayMetrics();
+      getWindowManager().getDefaultDisplay().getRealMetrics(displaymetrics);
+      mScreenWidth =displaymetrics.widthPixels;
+      mScreenHeight=displaymetrics.heightPixels;
+
+      hideNavigationBar();
+      cutoutHack();
+      computeBarHeights();
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// this does not include possible insets
+
+    private void computeBarHeights()
+      {
+      int barHeight = (int)(mScreenHeight*RATIO_BAR);
+      mHeightBar = barHeight;
+
+      LinearLayout layout = findViewById(R.id.lowerBar);
+      ViewGroup.LayoutParams params = layout.getLayoutParams();
+      params.height = barHeight;
+      layout.setLayoutParams(params);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    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);
+              }
+            }
+          });
+        }
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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();
+      InfoSurfaceView view = findViewById(R.id.infoSurfaceView);
+      view.onPause();
+      DistortedLibrary.onPause(ACTIVITY_NUMBER);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+    
+    @Override
+    protected void onResume() 
+      {
+      super.onResume();
+      DistortedLibrary.onResume(ACTIVITY_NUMBER);
+      InfoSurfaceView view = findViewById(R.id.infoSurfaceView);
+      view.onResume();
+
+      if( mScreen==null ) mScreen = new InfoScreen();
+      mScreen.onAttachedToWindow(this,mObjectOrdinal);
+
+      if( mObjectOrdinal>=0 && mObjectOrdinal< RubikObjectList.getNumObjects() )
+        {
+        RubikObject object = RubikObjectList.getObject(mObjectOrdinal);
+        changeIfDifferent(object,mObjectOrdinal,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(RubikObject object,int ordinal,ObjectControl control)
+      {
+      if( object!=null )
+        {
+        int iconMode           = TwistyObject.MODE_NORM;
+        InputStream jsonStream = object.getObjectStream(this);
+        InputStream meshStream = object.getMeshStream(this);
+        String name            = object.getUpperName();
+        InitAssets asset       = new InitAssets(jsonStream,meshStream,null);
+        control.changeIfDifferent(ordinal,name,iconMode,asset);
+        }
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void changeObject(int ordinal)
+      {
+      mObjectOrdinal = ordinal;
+      RubikObject object = RubikObjectList.getObject(ordinal);
+      InfoSurfaceView view = findViewById(R.id.infoSurfaceView);
+      ObjectControl control = view.getObjectControl();
+      changeIfDifferent(object,ordinal,control);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public int getHeightBar()
+      {
+      return mHeightBar;
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public int getScreenWidthInPixels()
+      {
+      return mScreenWidth;
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public int getScreenHeightInPixels()
+      {
+      return mScreenHeight;
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public ObjectControl getControl()
+      {
+      InfoSurfaceView view = findViewById(R.id.infoSurfaceView);
+      return view.getObjectControl();
+      }
+}
diff --git a/src/main/java/org/distorted/info/InfoObjectLibInterface.java b/src/main/java/org/distorted/info/InfoObjectLibInterface.java
new file mode 100644
index 00000000..66b23dbe
--- /dev/null
+++ b/src/main/java/org/distorted/info/InfoObjectLibInterface.java
@@ -0,0 +1,130 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.info;
+
+import com.google.firebase.crashlytics.FirebaseCrashlytics;
+
+import org.distorted.library.message.EffectMessageSender;
+import org.distorted.main.BuildConfig;
+import org.distorted.objectlib.helpers.BlockController;
+import org.distorted.objectlib.helpers.ObjectLibInterface;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class InfoObjectLibInterface implements ObjectLibInterface
+{
+  public void onWinEffectFinished(long startTime, long endTime, 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 onRemoveRotation(int axis, int row, int angle) { }
+  public void failedToDrag() { }
+  public void reportJSONError(String error, int ordinal) { }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  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 reportScramblingProblem(int place, long pause, long resume, long time)
+    {
+    String error = "SCRAMBLING 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 reportRotationProblem(int place, long pause, long resume, long time)
+    {
+    String error = "ROTATION 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_SCRAMBLING: reportScramblingProblem(place,pause,resume,time); break;
+      case BlockController.TYPE_ROTATION  : reportRotationProblem(place,pause,resume,time); break;
+      case BlockController.TYPE_THREAD    : reportThreadProblem(place,pause,resume,time); break;
+      }
+    }
+}
diff --git a/src/main/java/org/distorted/info/InfoRenderer.java b/src/main/java/org/distorted/info/InfoRenderer.java
new file mode 100644
index 00000000..03d59d29
--- /dev/null
+++ b/src/main/java/org/distorted/info/InfoRenderer.java
@@ -0,0 +1,108 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.info;
+
+import android.content.res.Resources;
+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 org.distorted.objectlib.main.ObjectControl;
+
+import java.io.InputStream;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class InfoRenderer implements GLSurfaceView.Renderer, DistortedLibrary.LibraryUser
+{
+   private final InfoSurfaceView mView;
+   private final Resources mResources;
+   private final DistortedScreen mScreen;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   InfoRenderer(InfoSurfaceView v)
+     {
+     final float BRIGHTNESS = 0.333f;
+
+     mView = v;
+     mResources = v.getResources();
+     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);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   DistortedScreen getScreen()
+     {
+     return mScreen;
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   @Override
+   public void onSurfaceCreated(GL10 glUnused, EGLConfig config)
+      {
+      DistortedLibrary.setMax(EffectType.VERTEX, ObjectControl.MAX_QUATS+1);
+      VertexEffectRotate.enable();
+      VertexEffectQuaternion.enable();
+      BaseEffect.Type.enableEffects();
+
+      DistortedLibrary.onSurfaceCreated(this,1);
+      DistortedLibrary.setCull(true);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   public void distortedException(Exception ex)
+     {
+     android.util.Log.e("Config", "unexpected exception: "+ex.getMessage() );
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   public InputStream localFile(int fileID)
+      {
+      return mResources.openRawResource(fileID);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   public void logMessage(String message)
+      {
+      android.util.Log.e("Config", message );
+      }
+}
diff --git a/src/main/java/org/distorted/info/InfoScreen.java b/src/main/java/org/distorted/info/InfoScreen.java
new file mode 100644
index 00000000..db3d85c8
--- /dev/null
+++ b/src/main/java/org/distorted/info/InfoScreen.java
@@ -0,0 +1,303 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.info;
+
+import android.os.Build;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.GridLayout;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.PopupWindow;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import org.distorted.helpers.ObjectGridCreator;
+
+import org.distorted.helpers.TransparentImageButton;
+import org.distorted.main.MainActivity;
+import org.distorted.main.R;
+import org.distorted.objects.RubikObject;
+import org.distorted.objects.RubikObjectList;
+
+import static android.view.View.inflate;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class InfoScreen
+{
+  private static final float TEXT_SIZE = 0.0310f;
+  private static final float PADDING   = 0.0060f;
+  private static final float MARGIN    = 0.0024f;
+  private static final int[] mLocation = new int[2];
+  private static final float POPUP_W  = 0.8f;
+
+  private TransparentImageButton mBackButton, mObjectButton, mPrevButton, mNextButton;
+  private TextView mMovesText;
+  private PopupWindow mObjectPopup;
+  private InfoScreenPane mPane;
+  private int mObjectOrdinal;
+  private float mButtonSize;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setupObjectWindow(final InfoActivity act, final float width, final float height)
+    {
+    int numObjects= RubikObjectList.getNumObjects();
+    ScrollView view = (ScrollView)inflate( act, R.layout.popup_object_simple, null);
+    GridLayout objectGrid = view.findViewById(R.id.objectGrid);
+
+    int[] objects = new int[numObjects];
+    for(int i=0; i<numObjects; i++) objects[i] = i;
+
+    ObjectGridCreator creator = new ObjectGridCreator(width);
+    creator.createObjectGridClassic(objectGrid,act,objects);
+
+    for(int child=0; child<numObjects; child++)
+      {
+      final RubikObject obj = RubikObjectList.getObject(child);
+      View v = objectGrid.getChildAt(child);
+      ImageButton button = creator.getButton(obj,v);
+      final int ordinal = child;
+
+      button.setOnClickListener( new View.OnClickListener()
+        {
+        @Override
+        public void onClick(View v)
+          {
+          if( mObjectOrdinal!=ordinal )
+            {
+            mObjectOrdinal = ordinal;
+            act.changeObject(mObjectOrdinal);
+            mMovesText.setText(act.getString(R.string.mo_placeholder,mObjectOrdinal+1,numObjects));
+            mPane.updatePane(act,mObjectOrdinal);
+            }
+
+          mObjectPopup.dismiss();
+          }
+        });
+      }
+
+    mObjectPopup = new PopupWindow(act);
+    mObjectPopup.setFocusable(true);
+    mObjectPopup.setContentView(view);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setupBackButton(final InfoActivity act)
+    {
+    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT);
+    mBackButton = new TransparentImageButton(act,R.drawable.ui_smallback,params);
+
+    mBackButton.setOnClickListener( new View.OnClickListener()
+      {
+      @Override
+      public void onClick(View v)
+        {
+        act.finish();
+        }
+      });
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setupObjectButton(final InfoActivity act, final int width, final int height)
+    {
+    final int margin= (int)(height*MARGIN);
+    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT);
+    mObjectButton = new TransparentImageButton(act,R.drawable.ui_cube_menu,params);
+
+    mObjectButton.setOnClickListener( new View.OnClickListener()
+      {
+      @Override
+      public void onClick(View view)
+        {
+        if( mObjectPopup==null ) setupObjectWindow(act,width,height);
+        View popupView = mObjectPopup.getContentView();
+        popupView.setSystemUiVisibility(MainActivity.FLAGS);
+        displayPopup(act,view,mObjectPopup,width,height,margin,margin);
+        }
+      });
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// work around lame bugs in Android's version <= 10 pop-up and split-screen modes
+
+  private void displayPopup(InfoActivity act, View view, PopupWindow window, int w, int h, int xoff, int yoff)
+    {
+    View topLayout = act.findViewById(R.id.mainLayout);
+    boolean isFullScreen;
+
+    if( topLayout!=null )
+      {
+      topLayout.getLocationOnScreen(mLocation);
+      isFullScreen = (mLocation[1]==0);
+      }
+    else
+      {
+      isFullScreen = true;
+      }
+
+    try
+      {
+      // if on Android 11 or we are fullscreen
+      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R || isFullScreen )
+        {
+        window.showAsDropDown(view, xoff, yoff, Gravity.CENTER);
+        window.update(view, w, h);
+        }
+      else  // Android 10 or below in pop-up mode or split-screen mode
+        {
+        view.getLocationOnScreen(mLocation);
+        int width  = view.getWidth();
+        int height = view.getHeight();
+        int x = mLocation[0]+(width-w)/2;
+        int y = mLocation[1]+height+yoff;
+
+        window.showAsDropDown(view);
+        window.update(x,y,w,h);
+        }
+      }
+    catch( IllegalArgumentException iae )
+      {
+      // ignore, this means window is 'not attached to window manager' -
+      // which most probably is because we are already exiting the app.
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void prevObject(InfoActivity act, int numObjects)
+    {
+    mObjectOrdinal--;
+    if( mObjectOrdinal<0 ) mObjectOrdinal=numObjects-1;
+
+    act.changeObject(mObjectOrdinal);
+    mPane.updatePane(act,mObjectOrdinal);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void nextObject(InfoActivity act, int numObjects)
+    {
+    mObjectOrdinal++;
+    if( mObjectOrdinal>=numObjects ) mObjectOrdinal=0;
+
+    act.changeObject(mObjectOrdinal);
+    mPane.updatePane(act,mObjectOrdinal);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setupPrevButton(final InfoActivity act)
+    {
+    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,1.0f);
+    mPrevButton = new TransparentImageButton(act,R.drawable.ui_left,params);
+
+    mPrevButton.setOnClickListener( new View.OnClickListener()
+      {
+      @Override
+      public void onClick(View v)
+        {
+        int numObjects = RubikObjectList.getNumObjects();
+        prevObject(act,numObjects);
+        mMovesText.setText(act.getString(R.string.mo_placeholder,mObjectOrdinal+1,numObjects));
+        }
+      });
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setupNextButton(final InfoActivity act)
+    {
+    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,1.0f);
+    mNextButton = new TransparentImageButton(act,R.drawable.ui_right,params);
+
+    mNextButton.setOnClickListener( new View.OnClickListener()
+      {
+      @Override
+      public void onClick(View v)
+        {
+        int numObjects = RubikObjectList.getNumObjects();
+        nextObject(act,numObjects);
+        mMovesText.setText(act.getString(R.string.mo_placeholder,mObjectOrdinal+1,numObjects));
+        }
+      });
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setupTextView(final InfoActivity act, final float height, int numObjects)
+    {
+    int padding = (int)(height*PADDING);
+    int margin  = (int)(height*MARGIN);
+    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,2.0f);
+    params.topMargin    = margin;
+    params.bottomMargin = margin;
+    params.leftMargin   = margin;
+    params.rightMargin  = margin;
+
+    mMovesText = new TextView(act);
+    mMovesText.setTextSize(20);
+    mMovesText.setLayoutParams(params);
+    mMovesText.setPadding(padding,0,padding,0);
+    mMovesText.setGravity(Gravity.CENTER);
+    mMovesText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mButtonSize);
+    mMovesText.setText(act.getString(R.string.mo_placeholder,mObjectOrdinal+1,numObjects));
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void onAttachedToWindow(final InfoActivity act, final int objectOrdinal)
+    {
+    int numObjects = RubikObjectList.getNumObjects();
+    int width = act.getScreenWidthInPixels();
+    int height= act.getScreenHeightInPixels();
+    int barHeight = act.getHeightBar();
+    mButtonSize = height*TEXT_SIZE;
+    mObjectOrdinal = objectOrdinal;
+
+    LinearLayout.LayoutParams paramsL = new LinearLayout.LayoutParams(width/4, LinearLayout.LayoutParams.MATCH_PARENT);
+    LinearLayout.LayoutParams paramsM = new LinearLayout.LayoutParams(width/2, LinearLayout.LayoutParams.MATCH_PARENT);
+    LinearLayout.LayoutParams paramsR = new LinearLayout.LayoutParams(width/4, LinearLayout.LayoutParams.MATCH_PARENT);
+
+    LinearLayout layoutLeft = new LinearLayout(act);
+    layoutLeft.setLayoutParams(paramsL);
+    LinearLayout layoutMid  = new LinearLayout(act);
+    layoutMid.setLayoutParams(paramsM);
+    LinearLayout layoutRight= new LinearLayout(act);
+    layoutRight.setLayoutParams(paramsR);
+
+    int popupW = (int)(POPUP_W*width);
+    int popupH = (int)(height-barHeight);
+
+    setupObjectButton(act,popupW,popupH);
+    setupPrevButton(act);
+    setupNextButton(act);
+    setupTextView(act,height,numObjects);
+    setupBackButton(act);
+
+    layoutLeft.addView(mObjectButton);
+    layoutMid.addView(mPrevButton);
+    layoutMid.addView(mMovesText);
+    layoutMid.addView(mNextButton);
+    layoutRight.addView(mBackButton);
+
+    LinearLayout layout = act.findViewById(R.id.lowerBar);
+    layout.removeAllViews();
+    layout.addView(layoutLeft);
+    layout.addView(layoutMid);
+    layout.addView(layoutRight);
+
+    mPane = new InfoScreenPane(act,mObjectOrdinal);
+    }
+}
diff --git a/src/main/java/org/distorted/info/InfoScreenPane.java b/src/main/java/org/distorted/info/InfoScreenPane.java
new file mode 100644
index 00000000..f11792ed
--- /dev/null
+++ b/src/main/java/org/distorted/info/InfoScreenPane.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.info;
+
+import static org.distorted.objects.RubikObjectCategories.DIFF_IDS;
+import static org.distorted.objects.RubikObjectCategories.DIFF_IMAGES;
+
+import java.io.InputStream;
+
+import android.graphics.PorterDuff;
+import android.util.TypedValue;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.distorted.main.R;
+import org.distorted.objectlib.json.JsonReader;
+import org.distorted.objects.RubikObject;
+import org.distorted.objects.RubikObjectList;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class InfoScreenPane
+{
+  private static final int NUM_IDS         = DIFF_IDS.length;
+  private static final float PADDING_RATIO = 0.016f;
+  private static final float TEXT_RATIO    = 0.025f;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void updatePane(InfoActivity act, int objectOrdinal)
+    {
+    RubikObject object = RubikObjectList.getObject(objectOrdinal);
+
+    if( object!=null )
+      {
+      InputStream stream = object.getObjectStream(act);
+
+      JsonReader reader = new JsonReader();
+      String name,author;
+      int year, difficulty;
+
+      try
+        {
+        reader.parseJsonFileMetadata(stream);
+        name       = reader.getObjectName();
+        author     = reader.getAuthor();
+        year       = reader.getYearOfInvention();
+        difficulty = (int)reader.getDifficulty();
+        }
+      catch(Exception ex)
+        {
+        name = "?";
+        author = "?";
+        year = 0;
+        difficulty = 0;
+        }
+
+      LinearLayout layout = act.findViewById(R.id.infoLayout);
+      TextView view = layout.findViewById(R.id.infoDetailsName2);
+      view.setText(name);
+      view = layout.findViewById(R.id.infoDetailsAuthor2);
+      view.setText(author);
+      view = layout.findViewById(R.id.infoDetailsYear2);
+      view.setText( year>0 ? String.valueOf(year) : "?" );
+
+      if( difficulty<0        ) difficulty=0;
+      if( difficulty>=NUM_IDS ) difficulty=NUM_IDS-1;
+
+      for(int i=0; i<NUM_IDS; i++)
+        {
+        ImageView image = layout.findViewById(DIFF_IDS[i]);
+        image.setImageResource(DIFF_IMAGES[i]);
+        image.setColorFilter( difficulty==i ? 0xffff0000 : 0xffffffff, PorterDuff.Mode.MULTIPLY );
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  InfoScreenPane(final InfoActivity act, int objectOrdinal)
+    {
+    int height = act.getScreenHeightInPixels();
+    float textSize = height*TEXT_RATIO;
+    int padding = (int)(height*PADDING_RATIO);
+
+    LinearLayout configLayout    = act.findViewById(R.id.infoLayout);
+    LinearLayout nameLayout      = configLayout.findViewById(R.id.infoLayoutName);
+    LinearLayout difficultyLayout= configLayout.findViewById(R.id.infoLayoutDifficulty);
+    LinearLayout authorLayout    = configLayout.findViewById(R.id.infoLayoutAuthor);
+    LinearLayout yearLayout      = configLayout.findViewById(R.id.infoLayoutYear);
+
+    nameLayout.setPadding(padding,padding,padding,padding/2);
+    difficultyLayout.setPadding(padding,padding/2,padding,padding/2);
+    authorLayout.setPadding(padding,padding/2,padding,padding/2);
+    yearLayout.setPadding(padding,padding/2,padding,padding/2);
+
+    LinearLayout.LayoutParams params1 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 1.00f);
+    params1.bottomMargin = 0;
+    params1.topMargin    = padding;
+    params1.leftMargin   = padding;
+    params1.rightMargin  = padding;
+
+    LinearLayout.LayoutParams params2 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 1.00f);
+    params2.bottomMargin = padding;
+    params2.topMargin    = padding;
+    params2.leftMargin   = padding;
+    params2.rightMargin  = padding;
+
+    nameLayout.setLayoutParams(params1);
+    difficultyLayout.setLayoutParams(params1);
+    authorLayout.setLayoutParams(params1);
+    yearLayout.setLayoutParams(params2);
+
+    TextView text;
+    text = nameLayout.findViewById(R.id.infoDetailsName1);
+    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+    text = nameLayout.findViewById(R.id.infoDetailsName2);
+    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+    text = authorLayout.findViewById(R.id.infoDetailsAuthor1);
+    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+    text = authorLayout.findViewById(R.id.infoDetailsAuthor2);
+    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+    text = difficultyLayout.findViewById(R.id.infoDifficultyTitle);
+    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+    text = yearLayout.findViewById(R.id.infoDetailsYear1);
+    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+    text = yearLayout.findViewById(R.id.infoDetailsYear2);
+    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+
+    LinearLayout layoutDiff = difficultyLayout.findViewById(R.id.infoDifficultyLayout);
+    layoutDiff.setPadding(padding,padding,padding,padding);
+
+    updatePane(act,objectOrdinal);
+    }
+}
diff --git a/src/main/java/org/distorted/info/InfoSurfaceView.java b/src/main/java/org/distorted/info/InfoSurfaceView.java
new file mode 100644
index 00000000..a07ba76c
--- /dev/null
+++ b/src/main/java/org/distorted/info/InfoSurfaceView.java
@@ -0,0 +1,121 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.info;
+
+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;
+import org.distorted.os.OSInterface;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class InfoSurfaceView extends GLSurfaceView
+{
+    private ObjectControl mObjectController;
+    private InfoRenderer mRenderer;
+    private boolean mCreated;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    void setScreenSize(int width, int height)
+      {
+      mObjectController.setScreenSizeAndScaling(width,height, Math.min(width,height));
+      mObjectController.setObjectScale(1.00f);
+
+      if( !mCreated )
+        {
+        mCreated = true;
+        mObjectController.createNode(width,height);
+        TwistyObjectNode objectNode = mObjectController.getNode();
+        mRenderer.getScreen().attach(objectNode);
+        }
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    ObjectControl getObjectControl()
+      {
+      return mObjectController;
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public InfoSurfaceView(Context context, AttributeSet attrs)
+      {
+      super(context,attrs);
+
+      mCreated = false;
+
+      if(!isInEditMode())
+        {
+        InfoActivity act = (InfoActivity)context;
+        InfoObjectLibInterface ref = new InfoObjectLibInterface();
+        OSInterface inter = new OSInterface(act,ref);
+        mObjectController = new ObjectControl(inter);
+        mObjectController.setRotateOnCreation(true);
+        mRenderer = new InfoRenderer(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/MainActivity.java b/src/main/java/org/distorted/main/MainActivity.java
index 28846f31..e2d2d538 100644
--- a/src/main/java/org/distorted/main/MainActivity.java
+++ b/src/main/java/org/distorted/main/MainActivity.java
@@ -34,7 +34,7 @@ import com.google.firebase.analytics.FirebaseAnalytics;
 import com.google.firebase.inappmessaging.FirebaseInAppMessaging;
 
 import org.distorted.bandaged.BandagedActivity;
-import org.distorted.config.ConfigActivity;
+import org.distorted.info.InfoActivity;
 import org.distorted.dialogs.RubikDialogAbout;
 import org.distorted.dialogs.RubikDialogCreators;
 import org.distorted.dialogs.RubikDialogExit;
@@ -395,9 +395,9 @@ public class MainActivity extends AppCompatActivity implements RubikNetwork.Upda
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    public void switchToConfig(int objectOrdinal)
+    public void switchToInfo(int objectOrdinal)
       {
-      Intent intent = new Intent(this, ConfigActivity.class);
+      Intent intent = new Intent(this, InfoActivity.class);
       intent.putExtra("obj", objectOrdinal);
       startActivity(intent);
       }
diff --git a/src/main/java/org/distorted/main/MainObjectPopup.java b/src/main/java/org/distorted/main/MainObjectPopup.java
index ca498f6a..d614b1f6 100644
--- a/src/main/java/org/distorted/main/MainObjectPopup.java
+++ b/src/main/java/org/distorted/main/MainObjectPopup.java
@@ -146,7 +146,7 @@ public class MainObjectPopup
       public void onClick(View v)
         {
         mPopup.dismiss();
-        act.switchToConfig(ordinal);
+        act.switchToInfo(ordinal);
         }
       });
 
diff --git a/src/main/java/org/distorted/objects/RubikObjectCategories.java b/src/main/java/org/distorted/objects/RubikObjectCategories.java
index e9919db1..c328484e 100644
--- a/src/main/java/org/distorted/objects/RubikObjectCategories.java
+++ b/src/main/java/org/distorted/objects/RubikObjectCategories.java
@@ -19,11 +19,11 @@ public abstract class RubikObjectCategories
 {
   public static final int[] DIFF_IDS =
     {
-    R.id.configDifficulty0,
-    R.id.configDifficulty1,
-    R.id.configDifficulty2,
-    R.id.configDifficulty3,
-    R.id.configDifficulty4
+    R.id.infoDifficulty0,
+    R.id.infoDifficulty1,
+    R.id.infoDifficulty2,
+    R.id.infoDifficulty3,
+    R.id.infoDifficulty4
     };
 
   public static final int[] DIFF_IMAGES =
diff --git a/src/main/res/layout-ldrtl/config.xml b/src/main/res/layout-ldrtl/config.xml
deleted file mode 100644
index fcbb429f..00000000
--- a/src/main/res/layout-ldrtl/config.xml
+++ /dev/null
@@ -1,204 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/mainLayout"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-
-    <org.distorted.config.ConfigSurfaceView
-        android:id="@+id/configSurfaceView"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1.3"/>
-
-    <LinearLayout
-        android:id="@+id/configLayout"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1.0"
-        android:background="@color/light_grey"
-        android:orientation="vertical" >
-
-        <LinearLayout
-            android:id="@+id/configLayoutName"
-            android:layout_width="fill_parent"
-            android:layout_height="0dp"
-            android:layout_weight="1"
-            android:paddingLeft="5dp"
-            android:paddingRight="5dp"
-            android:background="@color/grey"
-            android:orientation="horizontal">
-
-            <TextView
-                android:id="@+id/configDetailsName1"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="1.0"
-                android:gravity="center_vertical|right"
-                android:paddingStart="5dp"
-                android:textSize="26sp"
-                android:singleLine="true"
-                android:maxLines="1"
-                android:text="@string/config_name"/>
-
-            <TextView
-                android:id="@+id/configDetailsName2"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="2.1"
-                android:gravity="center_vertical|right"
-                android:paddingStart="5dp"
-                android:textSize="26sp"
-                android:singleLine="true"
-                android:maxLines="1"/>
-
-        </LinearLayout>
-
-        <LinearLayout
-            android:id="@+id/configLayoutDifficulty"
-            android:layout_width="fill_parent"
-            android:layout_height="0dp"
-            android:layout_weight="1"
-            android:paddingLeft="5dp"
-            android:paddingRight="5dp"
-            android:background="@color/grey"
-            android:orientation="horizontal">
-
-            <TextView
-                android:id="@+id/configDifficultyTitle"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="1.0"
-                android:gravity="center_vertical|right"
-                android:paddingStart="5dp"
-                android:textSize="26sp"
-                android:singleLine="true"
-                android:maxLines="1"
-                android:text="@string/config_difficulty"/>
-
-            <LinearLayout
-                android:id="@+id/configDifficultyLayout"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="2.5"
-                android:gravity="start"
-                android:paddingTop="20dp"
-                android:paddingBottom="20dp"
-                android:orientation="horizontal">
-
-                <ImageView
-                    android:id="@+id/configDifficulty0"
-                    android:layout_width="0dp"
-                    android:layout_height="match_parent"
-                    android:layout_weight="1"/>
-
-                <ImageView
-                    android:id="@+id/configDifficulty1"
-                    android:layout_width="0dp"
-                    android:layout_height="match_parent"
-                    android:layout_weight="1"/>
-
-                <ImageView
-                    android:id="@+id/configDifficulty2"
-                    android:layout_width="0dp"
-                    android:layout_height="match_parent"
-                    android:layout_weight="1"/>
-
-                <ImageView
-                    android:id="@+id/configDifficulty3"
-                    android:layout_width="0dp"
-                    android:layout_height="match_parent"
-                    android:layout_weight="1"/>
-
-                <ImageView
-                    android:id="@+id/configDifficulty4"
-                    android:layout_width="0dp"
-                    android:layout_height="match_parent"
-                    android:layout_weight="1"/>
-
-            </LinearLayout>
-
-        </LinearLayout>
-
-        <LinearLayout
-            android:id="@+id/configLayoutAuthor"
-            android:layout_width="fill_parent"
-            android:layout_height="0dp"
-            android:layout_weight="1"
-            android:paddingLeft="5dp"
-            android:paddingRight="5dp"
-            android:background="@color/grey"
-            android:orientation="horizontal">
-
-            <TextView
-                android:id="@+id/configDetailsAuthor1"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="1.0"
-                android:gravity="center_vertical|right"
-                android:paddingStart="5dp"
-                android:textSize="26sp"
-                android:singleLine="true"
-                android:maxLines="1"
-                android:text="@string/config_author"/>
-
-            <TextView
-                android:id="@+id/configDetailsAuthor2"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="2.1"
-                android:gravity="center_vertical|right"
-                android:paddingStart="5dp"
-                android:textSize="26sp"
-                android:singleLine="true"
-                android:maxLines="1"/>
-
-        </LinearLayout>
-
-        <LinearLayout
-            android:id="@+id/configLayoutYear"
-            android:layout_width="fill_parent"
-            android:layout_height="0dp"
-            android:layout_weight="1"
-            android:paddingLeft="5dp"
-            android:paddingRight="5dp"
-            android:background="@color/grey"
-            android:orientation="horizontal">
-
-            <TextView
-                android:id="@+id/configDetailsYear1"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="1.0"
-                android:gravity="center_vertical|right"
-                android:paddingStart="5dp"
-                android:textSize="26sp"
-                android:singleLine="true"
-                android:maxLines="1"
-                android:text="@string/config_author"/>
-
-            <TextView
-                android:id="@+id/configDetailsYear2"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="2.1"
-                android:gravity="center_vertical|right"
-                android:paddingStart="5dp"
-                android:textSize="26sp"
-                android:singleLine="true"
-                android:maxLines="1"/>
-
-        </LinearLayout>
-
-    </LinearLayout>
-
-    <LinearLayout
-        android:id="@+id/lowerBar"
-        android:layout_width="match_parent"
-        android:layout_height="100dp"
-        android:layout_gravity="end"
-        android:orientation="horizontal"
-        android:background="@color/light_grey">
-    </LinearLayout>
-
-</LinearLayout>
diff --git a/src/main/res/layout-ldrtl/info.xml b/src/main/res/layout-ldrtl/info.xml
new file mode 100644
index 00000000..d81f6010
--- /dev/null
+++ b/src/main/res/layout-ldrtl/info.xml
@@ -0,0 +1,204 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/mainLayout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <org.distorted.info.InfoSurfaceView
+        android:id="@+id/infoSurfaceView"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1.3"/>
+
+    <LinearLayout
+        android:id="@+id/infoLayout"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1.0"
+        android:background="@color/light_grey"
+        android:orientation="vertical" >
+
+        <LinearLayout
+            android:id="@+id/infoLayoutName"
+            android:layout_width="fill_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:paddingLeft="5dp"
+            android:paddingRight="5dp"
+            android:background="@color/grey"
+            android:orientation="horizontal">
+
+            <TextView
+                android:id="@+id/infoDetailsName1"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1.0"
+                android:gravity="center_vertical|right"
+                android:paddingStart="5dp"
+                android:textSize="26sp"
+                android:singleLine="true"
+                android:maxLines="1"
+                android:text="@string/config_name"/>
+
+            <TextView
+                android:id="@+id/infoDetailsName2"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="2.1"
+                android:gravity="center_vertical|right"
+                android:paddingStart="5dp"
+                android:textSize="26sp"
+                android:singleLine="true"
+                android:maxLines="1"/>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/infoLayoutDifficulty"
+            android:layout_width="fill_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:paddingLeft="5dp"
+            android:paddingRight="5dp"
+            android:background="@color/grey"
+            android:orientation="horizontal">
+
+            <TextView
+                android:id="@+id/infoDifficultyTitle"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1.0"
+                android:gravity="center_vertical|right"
+                android:paddingStart="5dp"
+                android:textSize="26sp"
+                android:singleLine="true"
+                android:maxLines="1"
+                android:text="@string/config_difficulty"/>
+
+            <LinearLayout
+                android:id="@+id/infoDifficultyLayout"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="2.5"
+                android:gravity="start"
+                android:paddingTop="20dp"
+                android:paddingBottom="20dp"
+                android:orientation="horizontal">
+
+                <ImageView
+                    android:id="@+id/infoDifficulty0"
+                    android:layout_width="0dp"
+                    android:layout_height="match_parent"
+                    android:layout_weight="1"/>
+
+                <ImageView
+                    android:id="@+id/infoDifficulty1"
+                    android:layout_width="0dp"
+                    android:layout_height="match_parent"
+                    android:layout_weight="1"/>
+
+                <ImageView
+                    android:id="@+id/infoDifficulty2"
+                    android:layout_width="0dp"
+                    android:layout_height="match_parent"
+                    android:layout_weight="1"/>
+
+                <ImageView
+                    android:id="@+id/infoDifficulty3"
+                    android:layout_width="0dp"
+                    android:layout_height="match_parent"
+                    android:layout_weight="1"/>
+
+                <ImageView
+                    android:id="@+id/infoDifficulty4"
+                    android:layout_width="0dp"
+                    android:layout_height="match_parent"
+                    android:layout_weight="1"/>
+
+            </LinearLayout>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/infoLayoutAuthor"
+            android:layout_width="fill_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:paddingLeft="5dp"
+            android:paddingRight="5dp"
+            android:background="@color/grey"
+            android:orientation="horizontal">
+
+            <TextView
+                android:id="@+id/infoDetailsAuthor1"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1.0"
+                android:gravity="center_vertical|right"
+                android:paddingStart="5dp"
+                android:textSize="26sp"
+                android:singleLine="true"
+                android:maxLines="1"
+                android:text="@string/config_author"/>
+
+            <TextView
+                android:id="@+id/infoDetailsAuthor2"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="2.1"
+                android:gravity="center_vertical|right"
+                android:paddingStart="5dp"
+                android:textSize="26sp"
+                android:singleLine="true"
+                android:maxLines="1"/>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/infoLayoutYear"
+            android:layout_width="fill_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:paddingLeft="5dp"
+            android:paddingRight="5dp"
+            android:background="@color/grey"
+            android:orientation="horizontal">
+
+            <TextView
+                android:id="@+id/infoDetailsYear1"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1.0"
+                android:gravity="center_vertical|right"
+                android:paddingStart="5dp"
+                android:textSize="26sp"
+                android:singleLine="true"
+                android:maxLines="1"
+                android:text="@string/config_author"/>
+
+            <TextView
+                android:id="@+id/infoDetailsYear2"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="2.1"
+                android:gravity="center_vertical|right"
+                android:paddingStart="5dp"
+                android:textSize="26sp"
+                android:singleLine="true"
+                android:maxLines="1"/>
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/lowerBar"
+        android:layout_width="match_parent"
+        android:layout_height="100dp"
+        android:layout_gravity="end"
+        android:orientation="horizontal"
+        android:background="@color/light_grey">
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/src/main/res/layout/config.xml b/src/main/res/layout/config.xml
deleted file mode 100644
index 571453c4..00000000
--- a/src/main/res/layout/config.xml
+++ /dev/null
@@ -1,204 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/mainLayout"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-
-    <org.distorted.config.ConfigSurfaceView
-        android:id="@+id/configSurfaceView"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1.3"/>
-
-    <LinearLayout
-        android:id="@+id/configLayout"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1.0"
-        android:background="@color/light_grey"
-        android:orientation="vertical" >
-
-        <LinearLayout
-            android:id="@+id/configLayoutName"
-            android:layout_width="fill_parent"
-            android:layout_height="0dp"
-            android:layout_weight="1"
-            android:paddingLeft="5dp"
-            android:paddingRight="5dp"
-            android:background="@color/grey"
-            android:orientation="horizontal">
-
-            <TextView
-                android:id="@+id/configDetailsName1"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="1.0"
-                android:gravity="center_vertical|start"
-                android:paddingStart="5dp"
-                android:textSize="26sp"
-                android:singleLine="true"
-                android:maxLines="1"
-                android:text="@string/config_name"/>
-
-            <TextView
-                android:id="@+id/configDetailsName2"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="2.1"
-                android:gravity="center_vertical|start"
-                android:paddingStart="5dp"
-                android:textSize="26sp"
-                android:singleLine="true"
-                android:maxLines="1"/>
-
-        </LinearLayout>
-
-        <LinearLayout
-            android:id="@+id/configLayoutDifficulty"
-            android:layout_width="fill_parent"
-            android:layout_height="0dp"
-            android:layout_weight="1"
-            android:paddingLeft="5dp"
-            android:paddingRight="5dp"
-            android:background="@color/grey"
-            android:orientation="horizontal">
-
-            <TextView
-                android:id="@+id/configDifficultyTitle"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="1.0"
-                android:gravity="center_vertical|start"
-                android:paddingStart="5dp"
-                android:textSize="26sp"
-                android:singleLine="true"
-                android:maxLines="1"
-                android:text="@string/config_difficulty"/>
-
-            <LinearLayout
-                android:id="@+id/configDifficultyLayout"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="2.5"
-                android:gravity="start"
-                android:paddingTop="20dp"
-                android:paddingBottom="20dp"
-                android:orientation="horizontal">
-
-                <ImageView
-                    android:id="@+id/configDifficulty0"
-                    android:layout_width="0dp"
-                    android:layout_height="match_parent"
-                    android:layout_weight="1"/>
-
-                <ImageView
-                    android:id="@+id/configDifficulty1"
-                    android:layout_width="0dp"
-                    android:layout_height="match_parent"
-                    android:layout_weight="1"/>
-
-                <ImageView
-                    android:id="@+id/configDifficulty2"
-                    android:layout_width="0dp"
-                    android:layout_height="match_parent"
-                    android:layout_weight="1"/>
-
-                <ImageView
-                    android:id="@+id/configDifficulty3"
-                    android:layout_width="0dp"
-                    android:layout_height="match_parent"
-                    android:layout_weight="1"/>
-
-                <ImageView
-                    android:id="@+id/configDifficulty4"
-                    android:layout_width="0dp"
-                    android:layout_height="match_parent"
-                    android:layout_weight="1"/>
-
-            </LinearLayout>
-
-        </LinearLayout>
-
-        <LinearLayout
-            android:id="@+id/configLayoutAuthor"
-            android:layout_width="fill_parent"
-            android:layout_height="0dp"
-            android:layout_weight="1"
-            android:paddingLeft="5dp"
-            android:paddingRight="5dp"
-            android:background="@color/grey"
-            android:orientation="horizontal">
-
-            <TextView
-                android:id="@+id/configDetailsAuthor1"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="1.0"
-                android:gravity="center_vertical|start"
-                android:paddingStart="5dp"
-                android:textSize="26sp"
-                android:singleLine="true"
-                android:maxLines="1"
-                android:text="@string/config_author"/>
-
-            <TextView
-                android:id="@+id/configDetailsAuthor2"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="2.1"
-                android:gravity="center_vertical|start"
-                android:paddingStart="5dp"
-                android:textSize="26sp"
-                android:singleLine="true"
-                android:maxLines="1"/>
-
-        </LinearLayout>
-
-        <LinearLayout
-            android:id="@+id/configLayoutYear"
-            android:layout_width="fill_parent"
-            android:layout_height="0dp"
-            android:layout_weight="1"
-            android:paddingLeft="5dp"
-            android:paddingRight="5dp"
-            android:background="@color/grey"
-            android:orientation="horizontal">
-
-            <TextView
-                android:id="@+id/configDetailsYear1"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="1.0"
-                android:gravity="center_vertical|start"
-                android:paddingStart="5dp"
-                android:textSize="26sp"
-                android:singleLine="true"
-                android:maxLines="1"
-                android:text="@string/config_year"/>
-
-            <TextView
-                android:id="@+id/configDetailsYear2"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="2.1"
-                android:gravity="center_vertical|start"
-                android:paddingStart="5dp"
-                android:textSize="26sp"
-                android:singleLine="true"
-                android:maxLines="1"/>
-
-        </LinearLayout>
-
-    </LinearLayout>
-
-    <LinearLayout
-        android:id="@+id/lowerBar"
-        android:layout_width="match_parent"
-        android:layout_height="100dp"
-        android:layout_gravity="end"
-        android:orientation="horizontal"
-        android:background="@color/light_grey">
-    </LinearLayout>
-
-</LinearLayout>
diff --git a/src/main/res/layout/info.xml b/src/main/res/layout/info.xml
new file mode 100644
index 00000000..d59b7ad8
--- /dev/null
+++ b/src/main/res/layout/info.xml
@@ -0,0 +1,204 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/mainLayout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <org.distorted.info.InfoSurfaceView
+        android:id="@+id/infoSurfaceView"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1.3"/>
+
+    <LinearLayout
+        android:id="@+id/infoLayout"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1.0"
+        android:background="@color/light_grey"
+        android:orientation="vertical" >
+
+        <LinearLayout
+            android:id="@+id/infoLayoutName"
+            android:layout_width="fill_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:paddingLeft="5dp"
+            android:paddingRight="5dp"
+            android:background="@color/grey"
+            android:orientation="horizontal">
+
+            <TextView
+                android:id="@+id/infoDetailsName1"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1.0"
+                android:gravity="center_vertical|start"
+                android:paddingStart="5dp"
+                android:textSize="26sp"
+                android:singleLine="true"
+                android:maxLines="1"
+                android:text="@string/config_name"/>
+
+            <TextView
+                android:id="@+id/infoDetailsName2"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="2.1"
+                android:gravity="center_vertical|start"
+                android:paddingStart="5dp"
+                android:textSize="26sp"
+                android:singleLine="true"
+                android:maxLines="1"/>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/infoLayoutDifficulty"
+            android:layout_width="fill_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:paddingLeft="5dp"
+            android:paddingRight="5dp"
+            android:background="@color/grey"
+            android:orientation="horizontal">
+
+            <TextView
+                android:id="@+id/infoDifficultyTitle"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1.0"
+                android:gravity="center_vertical|start"
+                android:paddingStart="5dp"
+                android:textSize="26sp"
+                android:singleLine="true"
+                android:maxLines="1"
+                android:text="@string/config_difficulty"/>
+
+            <LinearLayout
+                android:id="@+id/infoDifficultyLayout"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="2.5"
+                android:gravity="start"
+                android:paddingTop="20dp"
+                android:paddingBottom="20dp"
+                android:orientation="horizontal">
+
+                <ImageView
+                    android:id="@+id/infoDifficulty0"
+                    android:layout_width="0dp"
+                    android:layout_height="match_parent"
+                    android:layout_weight="1"/>
+
+                <ImageView
+                    android:id="@+id/infoDifficulty1"
+                    android:layout_width="0dp"
+                    android:layout_height="match_parent"
+                    android:layout_weight="1"/>
+
+                <ImageView
+                    android:id="@+id/infoDifficulty2"
+                    android:layout_width="0dp"
+                    android:layout_height="match_parent"
+                    android:layout_weight="1"/>
+
+                <ImageView
+                    android:id="@+id/infoDifficulty3"
+                    android:layout_width="0dp"
+                    android:layout_height="match_parent"
+                    android:layout_weight="1"/>
+
+                <ImageView
+                    android:id="@+id/infoDifficulty4"
+                    android:layout_width="0dp"
+                    android:layout_height="match_parent"
+                    android:layout_weight="1"/>
+
+            </LinearLayout>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/infoLayoutAuthor"
+            android:layout_width="fill_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:paddingLeft="5dp"
+            android:paddingRight="5dp"
+            android:background="@color/grey"
+            android:orientation="horizontal">
+
+            <TextView
+                android:id="@+id/infoDetailsAuthor1"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1.0"
+                android:gravity="center_vertical|start"
+                android:paddingStart="5dp"
+                android:textSize="26sp"
+                android:singleLine="true"
+                android:maxLines="1"
+                android:text="@string/config_author"/>
+
+            <TextView
+                android:id="@+id/infoDetailsAuthor2"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="2.1"
+                android:gravity="center_vertical|start"
+                android:paddingStart="5dp"
+                android:textSize="26sp"
+                android:singleLine="true"
+                android:maxLines="1"/>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/infoLayoutYear"
+            android:layout_width="fill_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:paddingLeft="5dp"
+            android:paddingRight="5dp"
+            android:background="@color/grey"
+            android:orientation="horizontal">
+
+            <TextView
+                android:id="@+id/infoDetailsYear1"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1.0"
+                android:gravity="center_vertical|start"
+                android:paddingStart="5dp"
+                android:textSize="26sp"
+                android:singleLine="true"
+                android:maxLines="1"
+                android:text="@string/config_year"/>
+
+            <TextView
+                android:id="@+id/infoDetailsYear2"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="2.1"
+                android:gravity="center_vertical|start"
+                android:paddingStart="5dp"
+                android:textSize="26sp"
+                android:singleLine="true"
+                android:maxLines="1"/>
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/lowerBar"
+        android:layout_width="match_parent"
+        android:layout_height="100dp"
+        android:layout_gravity="end"
+        android:orientation="horizontal"
+        android:background="@color/light_grey">
+    </LinearLayout>
+
+</LinearLayout>
