commit 1c89e2a72c08a9493d3b53bb4dd6d6d1678cd5da
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Tue Jan 24 15:44:59 2023 +0100

    What's new dialog
    Bump version to 1.12.0

diff --git a/build.gradle b/build.gradle
index 6716f3a2..6428027d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -15,8 +15,8 @@ android {
         applicationId "org.distorted.magic"
         minSdkVersion 21
         targetSdkVersion 32
-        versionCode 66
-        versionName "1.11.6"
+        versionCode 67
+        versionName "1.12.0"
     }
 
     buildTypes {
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogSolvers.java b/src/main/java/org/distorted/dialogs/RubikDialogSolvers.java
index ab7ac286..a94dfc78 100644
--- a/src/main/java/org/distorted/dialogs/RubikDialogSolvers.java
+++ b/src/main/java/org/distorted/dialogs/RubikDialogSolvers.java
@@ -67,7 +67,6 @@ public class RubikDialogSolvers extends RubikDialogAbstract
 
   public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
     {
-    int minH  = (int)(mHeight*0.250f);
     int margin= (int)(mHeight*0.010f);
     int textH = (int)(mHeight*0.035f);
     int buttH = (int)(mHeight*0.060f);
@@ -78,11 +77,10 @@ public class RubikDialogSolvers extends RubikDialogAbstract
     TextView text  = view.findViewById(R.id.dialog_scrollable_message);
     text.setVisibility(View.GONE);
 
-    layout.setMinimumHeight(minH);
-    view.setMinimumHeight(minH);
-
     LinearLayout.LayoutParams pV = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
     pV.setMargins(margin, margin, margin, 0);
+    LinearLayout.LayoutParams pL = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
+    pL.setMargins(margin, margin, margin, margin);
     LinearLayout.LayoutParams pT = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, textH );
     LinearLayout.LayoutParams pB = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, buttH );
     pB.setMargins(0,2*margin,0,0);
@@ -96,7 +94,7 @@ public class RubikDialogSolvers extends RubikDialogAbstract
       int object = solver.getObject();
       int title  = solver.getTitle();
       int description = solver.getDescription();
-      RubikDialogSolverView pane = new RubikDialogSolverView(ract,this,i,object,title,description, padd, font, pV,pT,pB);
+      RubikDialogSolverView pane = new RubikDialogSolverView(ract,this,i,object,title,description, padd, font, (i==num-1?pL:pV),pT,pB);
       layout.addView(pane.getView());
       }
     }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogStarsExplain.java b/src/main/java/org/distorted/dialogs/RubikDialogStarsExplain.java
deleted file mode 100644
index 2b272f8c..00000000
--- a/src/main/java/org/distorted/dialogs/RubikDialogStarsExplain.java
+++ /dev/null
@@ -1,52 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2022 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.dialogs;
-
-import android.app.Dialog;
-import android.util.TypedValue;
-import android.view.View;
-import android.widget.TextView;
-
-import androidx.fragment.app.FragmentActivity;
-
-import org.distorted.main.R;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikDialogStarsExplain extends RubikDialogAbstract
-  {
-  public int getResource()      { return R.layout.dialog_stars_explain; }
-  public int getTitleResource() { return -1; }
-  public boolean hasArgument()  { return false; }
-  public int getPositive()      { return R.string.ok; }
-  public int getNegative()      { return -1; }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void positiveAction()
-    {
-
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void negativeAction()
-    {
-
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
-    {
-    TextView text = view.findViewById(R.id.stars_dialog_explain);
-    text.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
-    }
-  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogUpdates.java b/src/main/java/org/distorted/dialogs/RubikDialogUpdates.java
index 5df79d59..780ab9bb 100644
--- a/src/main/java/org/distorted/dialogs/RubikDialogUpdates.java
+++ b/src/main/java/org/distorted/dialogs/RubikDialogUpdates.java
@@ -117,6 +117,8 @@ public class RubikDialogUpdates extends RubikDialogAbstract implements RubikNetw
 
       LinearLayout.LayoutParams pV = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, mSize );
       pV.setMargins(mMargin, mMargin, mMargin, 0);
+      LinearLayout.LayoutParams pL = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, mSize );
+      pL.setMargins(mMargin, mMargin, mMargin, mMargin);
       LinearLayout.LayoutParams pT = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, textH );
       LinearLayout.LayoutParams pB = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, buttH );
 
@@ -137,7 +139,7 @@ public class RubikDialogUpdates extends RubikDialogAbstract implements RubikNetw
           {
           RubikUpdates.UpdateInfo info = updates.getStartedUpdate(i);
           RubikDialogUpdateView rubikView = new RubikDialogUpdateView();
-          View pane = rubikView.createView(act,info,mFontSize,mPadding,pV,pT,pB);
+          View pane = rubikView.createView(act,info,mFontSize,mPadding,(i==numS-1?pL:pV),pT,pB);
           mLayout.addView(pane);
           mPanes.add(rubikView);
           }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogWhatsNew.java b/src/main/java/org/distorted/dialogs/RubikDialogWhatsNew.java
new file mode 100644
index 00000000..49320401
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/RubikDialogWhatsNew.java
@@ -0,0 +1,149 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2022 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.app.Dialog;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.fragment.app.FragmentActivity;
+
+import org.distorted.main.R;
+import org.distorted.main.RubikActivity;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class RubikDialogWhatsNew extends RubikDialogAbstract
+  {
+  private static final String[] MESSAGES =
+      {
+      "1.11.6",
+
+      "1. This dialog :)\n" +
+      "2. UI ready for NEW SOLVERS\n" +
+      "3. Important fix for a bug which used to let players solve any scramble with just one move\n" +
+      "4. UI fixes for squarish screens\n" +
+      "5. Preparation for Penrose Cubes.\n",
+
+      "current",
+      };
+
+  private int mLowerVersion, mUpperVersion;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public void onResume()
+    {
+    super.onResume();
+
+    Window window = getDialog().getWindow();
+
+    if( window!=null )
+      {
+      WindowManager.LayoutParams params = window.getAttributes();
+      params.width  = (int)Math.min( mHeight*0.65f,mWidth*0.98f );
+      window.setAttributes(params);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getResource()      { return R.layout.dialog_scrollable_panes; }
+  public int getTitleResource() { return R.string.whatsnew; }
+  public boolean hasArgument()  { return true; }
+  public int getPositive()      { return R.string.ok; }
+  public int getNegative()      { return -1; }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void positiveAction()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void negativeAction()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
+    {
+    int margin= (int)(mHeight*0.010f);
+    int titleH= (int)(mHeight*0.035f);
+    int padd  = (int)(mHeight*0.010f);
+
+    LinearLayout layout= view.findViewById(R.id.dialog_scrollable_main_layout);
+    TextView text  = view.findViewById(R.id.dialog_scrollable_message);
+    text.setVisibility(View.GONE);
+
+    LinearLayout.LayoutParams pV = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
+    pV.setMargins(margin, margin, margin, 0);
+    LinearLayout.LayoutParams pL = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
+    pL.setMargins(margin, margin, margin, margin);
+    LinearLayout.LayoutParams pT = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, titleH );
+    LinearLayout.LayoutParams pM = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
+    pV.setMargins(margin, margin, margin, margin);
+
+    parseArgument(mArgument);
+    RubikActivity ract = (RubikActivity) getContext();
+
+    for(int i=mLowerVersion; i<mUpperVersion; i+=2)
+      {
+      String lower   = MESSAGES[i];
+      String message = MESSAGES[i+1];
+      String upper   = MESSAGES[i+2];
+
+      RubikDialogWhatsNewView pane = new RubikDialogWhatsNewView(ract,lower,upper,message,padd,(i==mUpperVersion-1 ? pL:pV),pT,pM);
+      layout.addView(pane.getView());
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void parseArgument(String argument)
+    {
+    String[] parts = argument.split(" ");
+
+    if( parts.length==2 )
+      {
+      mLowerVersion = findVersion(parts[0]);
+      mUpperVersion = findVersion(parts[1]);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int findVersion(String version)
+    {
+    int len = MESSAGES.length;
+
+    for(int i=0; i<len; i++)
+      {
+      if( MESSAGES[i].equals(version) ) return i;
+      }
+
+    return len-1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static String getDialogTag()
+    {
+    return "DialogSolvers";
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogWhatsNewView.java b/src/main/java/org/distorted/dialogs/RubikDialogWhatsNewView.java
new file mode 100644
index 00000000..ae79fd7d
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/RubikDialogWhatsNewView.java
@@ -0,0 +1,49 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2023 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialogs;
+
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.distorted.main.R;
+import org.distorted.main.RubikActivity;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class RubikDialogWhatsNewView
+  {
+  private final View mView;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public RubikDialogWhatsNewView(final RubikActivity act, String lower, String upper, String message, int padding,
+                                 LinearLayout.LayoutParams pView, LinearLayout.LayoutParams pTitle, LinearLayout.LayoutParams pMessage )
+    {
+    mView = act.getLayoutInflater().inflate(R.layout.dialog_whatsnew_pane, null);
+    mView.setLayoutParams(pView);
+    mView.setPadding(padding,padding,padding,padding);
+
+    TextView titleView = mView.findViewById(R.id.whatsnew_pane_title);
+    titleView.setText(lower+" -> "+upper);
+    titleView.setLayoutParams(pTitle);
+
+    TextView messView = mView.findViewById(R.id.whatsnew_pane_message);
+    messView.setText(message);
+    messView.setLayoutParams(pMessage);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public View getView()
+    {
+    return mView;
+    }
+  }
diff --git a/src/main/java/org/distorted/main/RubikActivity.java b/src/main/java/org/distorted/main/RubikActivity.java
index c4c0591d..9a9e8e26 100644
--- a/src/main/java/org/distorted/main/RubikActivity.java
+++ b/src/main/java/org/distorted/main/RubikActivity.java
@@ -35,7 +35,7 @@ import com.google.firebase.inappmessaging.FirebaseInAppMessaging;
 
 import org.distorted.config.ConfigActivity;
 import org.distorted.bandaged.BandagedCreatorActivity;
-import org.distorted.dialogs.RubikDialogStarsExplain;
+import org.distorted.dialogs.RubikDialogWhatsNew;
 import org.distorted.library.main.DistortedLibrary;
 
 import org.distorted.library.main.DistortedScreen;
@@ -272,16 +272,15 @@ public class RubikActivity extends AppCompatActivity
       changeIfDifferent(object,view.getObjectControl());
 
       if( mIsChinese && !mPolicyAccepted ) privacyPolicy();
-
-      if( USE_IAP )
+      else
         {
-        if( mOldVersion!=null && !mOldVersion.equals("") && oldVersionLessThan(1,11,5) && !mCurrVersion.equals("1.11.4") )
+        if( !mOldVersion.equals(mCurrVersion) )
           {
-          explainStars();
+          displayNovelties();
           }
         else
           {
-          view.setShowStars();
+          if( USE_IAP ) view.setShowStars();
           }
         }
       }
@@ -296,10 +295,13 @@ public class RubikActivity extends AppCompatActivity
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    private void explainStars()
+    private void displayNovelties()
       {
-      RubikDialogStarsExplain staDiag = new RubikDialogStarsExplain();
-      staDiag.show(getSupportFragmentManager(), null);
+      Bundle bundle = new Bundle();
+      bundle.putString("argument",mOldVersion+" "+mCurrVersion);
+      RubikDialogWhatsNew newDialog = new RubikDialogWhatsNew();
+      newDialog.setArguments(bundle);
+      newDialog.show(getSupportFragmentManager(), RubikDialogWhatsNew.getDialogTag() );
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/res/layout/dialog_scrollable_panes.xml b/src/main/res/layout/dialog_scrollable_panes.xml
index 9b636708..da84709f 100644
--- a/src/main/res/layout/dialog_scrollable_panes.xml
+++ b/src/main/res/layout/dialog_scrollable_panes.xml
@@ -11,7 +11,6 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:background="@color/black"
-        android:paddingBottom="8dp"
         android:orientation="vertical">
 
         <TextView
diff --git a/src/main/res/layout/dialog_whatsnew_pane.xml b/src/main/res/layout/dialog_whatsnew_pane.xml
new file mode 100644
index 00000000..ccc2f0f5
--- /dev/null
+++ b/src/main/res/layout/dialog_whatsnew_pane.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	android:layout_width="match_parent"
+	android:layout_height="wrap_content"
+	android:background="@color/medium_grey"
+	android:padding="8dp"
+	android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/whatsnew_pane_title"
+    	android:gravity="top|start"
+    	android:layout_width="match_parent"
+    	android:layout_height="wrap_content"
+    	android:singleLine="true"
+    	android:textStyle="bold"/>
+
+    <TextView
+    	android:id="@+id/whatsnew_pane_message"
+    	android:gravity="top|start"
+    	android:layout_width="match_parent"
+    	android:layout_height="wrap_content"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml
index 96381527..25d31182 100755
--- a/src/main/res/values-de/strings.xml
+++ b/src/main/res/values-de/strings.xml
@@ -48,6 +48,7 @@
     <string name="install">Installieren</string>
     <string name="abandon_solve">Give up</string>
     <string name="use_solver">Benutze</string>
+    <string name="whatsnew">Was gibt\'s Neues</string>
 
     <string name="stars">Sterne</string>
     <string name="scores">Highscores</string>
diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml
index 3322f4a0..09b5155d 100755
--- a/src/main/res/values-es/strings.xml
+++ b/src/main/res/values-es/strings.xml
@@ -48,6 +48,7 @@
     <string name="install">Instalar</string>
     <string name="abandon_solve">Abandonar</string>
     <string name="use_solver">Utilizar</string>
+    <string name="whatsnew">Qué hay de nuevo</string>
 
     <string name="stars">Estrellas</string>
     <string name="scores">Leaderboard</string>
diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml
index 9de066c5..62e02c4b 100755
--- a/src/main/res/values-fr/strings.xml
+++ b/src/main/res/values-fr/strings.xml
@@ -48,6 +48,7 @@
     <string name="install">Installer</string>
     <string name="abandon_solve">Abandonner</string>
     <string name="use_solver">Utiliser</string>
+    <string name="whatsnew">Quoi de neuf</string>
 
     <string name="stars">Étoiles</string>
     <string name="scores">Meilleurs scores</string>
diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml
index 3848304b..3df1ae9c 100755
--- a/src/main/res/values-ja/strings.xml
+++ b/src/main/res/values-ja/strings.xml
@@ -48,6 +48,7 @@
     <string name="install">インストール</string>
     <string name="abandon_solve">あきらめる</string>
     <string name="use_solver">使用する</string>
+    <string name="whatsnew">新着情報</string>
 
     <string name="stars">星</string>
     <string name="scores">ハイスコア</string>
diff --git a/src/main/res/values-ko/strings.xml b/src/main/res/values-ko/strings.xml
index 46b2ede1..772258b6 100755
--- a/src/main/res/values-ko/strings.xml
+++ b/src/main/res/values-ko/strings.xml
@@ -48,6 +48,7 @@
     <string name="install">설치</string>
     <string name="abandon_solve">포기</string>
     <string name="use_solver">사용</string>
+    <string name="whatsnew">새로운 기능</string>
 
     <string name="stars">별</string>
     <string name="scores">고득점</string>
diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml
index 9897020d..362fae15 100644
--- a/src/main/res/values-pl/strings.xml
+++ b/src/main/res/values-pl/strings.xml
@@ -48,6 +48,7 @@
     <string name="install">Zainstaluj</string>
     <string name="abandon_solve">Zrezygnuj</string>
     <string name="use_solver">Użyj</string>
+    <string name="whatsnew">Nowości</string>
 
     <string name="stars">Gwiazdki</string>
     <string name="scores">Lista najlepszych</string>
diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml
index b865818b..33939a9a 100755
--- a/src/main/res/values-ru/strings.xml
+++ b/src/main/res/values-ru/strings.xml
@@ -48,6 +48,7 @@
     <string name="install">Установите</string>
     <string name="abandon_solve">Сдаться</string>
     <string name="use_solver">Попробуй</string>
+    <string name="whatsnew">Новинки</string>
 
     <string name="stars">Звезды</string>
     <string name="scores">Высокие баллы</string>
diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml
index d87eef4f..4cf5e769 100644
--- a/src/main/res/values-zh-rCN/strings.xml
+++ b/src/main/res/values-zh-rCN/strings.xml
@@ -48,6 +48,7 @@
     <string name="install">安装</string>
     <string name="abandon_solve">放弃</string>
     <string name="use_solver">尝试</string>
+    <string name="whatsnew">新奇事物</string>
 
     <string name="stars">星星</string>
     <string name="scores">高分</string>
diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml
index 9ae43b3a..14c668bc 100644
--- a/src/main/res/values-zh-rTW/strings.xml
+++ b/src/main/res/values-zh-rTW/strings.xml
@@ -48,6 +48,7 @@
     <string name="install">安裝</string>
     <string name="abandon_solve">放棄</string>
     <string name="use_solver">嘗試</string>
+    <string name="whatsnew">新奇事物</string>
 
     <string name="stars">星星</string>
     <string name="scores">高分</string>
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 5fc3c408..548c8287 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -50,6 +50,7 @@
     <string name="install">Install</string>
     <string name="abandon_solve">Give up</string>
     <string name="use_solver">Try</string>
+    <string name="whatsnew">What\'s new</string>
 
     <string name="stars">Stars</string>
     <string name="scores">High Scores</string>
@@ -98,7 +99,7 @@
     <string name="opengl_error" translatable="false">Error</string>
     <string name="opengl_error_text" translatable="false">This device does not support OpenGL 3.0</string>
 
-    <string name="solver_generic_error1">Solver for this object and size not implemented yet!</string>
+    <string name="solver_generic_error1">Solver for this object not implemented yet!</string>
     <string name="solver_cube3_error1">There are only %1$d %2$s facelets.</string>
     <string name="solver_cube3_error2">Not all 12 edges exist exactly once!</string>
     <string name="solver_cube3_error3">One edge has to be flipped!</string>
