commit e41e7dc35b5087db97f440de5dcd5eb7c37246ba
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Fri Mar 20 22:43:53 2020 +0000

    Add the 'Solved' dialog.

diff --git a/src/main/java/org/distorted/dialog/RubikDialogSolved.java b/src/main/java/org/distorted/dialog/RubikDialogSolved.java
new file mode 100644
index 00000000..ed948f92
--- /dev/null
+++ b/src/main/java/org/distorted/dialog/RubikDialogSolved.java
@@ -0,0 +1,98 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is free software: you can redistribute it and/or modify                            //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Magic Cube is distributed in the hope that it will be useful,                                 //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.dialog;
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.FragmentActivity;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatDialogFragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import org.distorted.magic.R;
+import org.distorted.magic.RubikActivity;
+import org.distorted.uistate.RubikState;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class RubikDialogSolved extends AppCompatDialogFragment
+  {
+  @Override
+  public void onStart()
+    {
+    super.onStart();
+
+    Window window = getDialog().getWindow();
+    window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
+                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+    window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @NonNull
+  @Override
+  public Dialog onCreateDialog(Bundle savedInstanceState)
+    {
+    FragmentActivity act = getActivity();
+    LayoutInflater inflater = act.getLayoutInflater();
+    AlertDialog.Builder builder = new AlertDialog.Builder(act);
+
+    TextView tv = (TextView) inflater.inflate(R.layout.dialog_title, null);
+    tv.setText(R.string.solved);
+    builder.setCustomTitle(tv);
+
+    builder.setCancelable(true);
+    builder.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener()
+      {
+      @Override
+      public void onClick(DialogInterface dialog, int which)
+        {
+        RubikActivity act = (RubikActivity)getActivity();
+        RubikState.switchState(act,RubikState.PLAY);
+        }
+      });
+
+    Bundle args = getArguments();
+    long time;
+
+    try
+      {
+      time = args.getLong("time");
+      }
+    catch(Exception e)
+      {
+      time = 0;
+      }
+
+    final View view = inflater.inflate(R.layout.dialog_solved, null);
+    TextView text = view.findViewById(R.id.solved_time);
+    text.setText(getString(R.string.ti_placeholder, (time/100)/10.0f));
+    builder.setView(view);
+
+    return builder.create();
+    }
+  }
diff --git a/src/main/java/org/distorted/magic/RubikActivity.java b/src/main/java/org/distorted/magic/RubikActivity.java
index b8393018..c1f3cfe9 100644
--- a/src/main/java/org/distorted/magic/RubikActivity.java
+++ b/src/main/java/org/distorted/magic/RubikActivity.java
@@ -79,7 +79,6 @@ public class RubikActivity extends AppCompatActivity implements View.OnClickList
       view.onResume();
       restorePreferences();
       RubikState.setState(this);
-      RubikStatePlay play = (RubikStatePlay)RubikState.PLAY.getStateClass();
 
       if( mJustStarted )
         {
@@ -89,6 +88,7 @@ public class RubikActivity extends AppCompatActivity implements View.OnClickList
         scores.setCountry(this);
         }
 
+      RubikStatePlay play = (RubikStatePlay)RubikState.PLAY.getStateClass();
       int object = play.getObject();
       int size   = play.getSize();
       RubikObjectList obj = RubikObjectList.getObject(object);
diff --git a/src/main/java/org/distorted/magic/RubikRenderer.java b/src/main/java/org/distorted/magic/RubikRenderer.java
index 659eb141..47d98416 100644
--- a/src/main/java/org/distorted/magic/RubikRenderer.java
+++ b/src/main/java/org/distorted/magic/RubikRenderer.java
@@ -21,7 +21,9 @@ package org.distorted.magic;
 
 import android.content.SharedPreferences;
 import android.opengl.GLSurfaceView;
+import android.os.Bundle;
 
+import org.distorted.dialog.RubikDialogSolved;
 import org.distorted.effect.BaseEffect;
 import org.distorted.library.effect.VertexEffectSink;
 import org.distorted.library.main.DistortedLibrary;
@@ -53,6 +55,8 @@ public class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
     private boolean mChangeObject, mSolveObject, mScrambleObject;
     private boolean mCanRotate, mCanDrag, mCanUI;
     private boolean mIsSolved;
+    private boolean mIsNewRecord;
+    private long mNewRecord;
     private RubikObject mOldObject, mNewObject;
     private int mScreenWidth, mScreenHeight;
     private SharedPreferences mPreferences;
@@ -265,7 +269,17 @@ public class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
          if( RubikState.getCurrentState()==RubikState.SOLV )
            {
            RubikStateSolving solving = (RubikStateSolving)RubikState.SOLV.getStateClass();
-           solving.stopCounting();
+           mNewRecord = solving.stopCounting((RubikActivity)mView.getContext());
+
+           if( mNewRecord< 0 )
+             {
+             mNewRecord = -mNewRecord;
+             mIsNewRecord = false;
+             }
+           else
+             {
+             mIsNewRecord = true;
+             }
            }
 
          mCanDrag   = true;
@@ -383,6 +397,34 @@ public class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
                  }
                });
              }
+
+           if( i==BaseEffect.Type.WIN.ordinal() )
+             {
+             if( RubikState.getCurrentState()==RubikState.SOLV )
+               {
+               final RubikActivity act = (RubikActivity)mView.getContext();
+
+               if( mIsNewRecord )
+                 {
+                 RubikDialogSolved dialog = new RubikDialogSolved();
+
+                 Bundle bundle = new Bundle();
+                 bundle.putLong("time", mNewRecord );
+                 dialog.setArguments(bundle);
+                 dialog.show( act.getSupportFragmentManager(), null);
+                 }
+               else
+                 {
+                 RubikDialogSolved dialog = new RubikDialogSolved();
+
+                 Bundle bundle = new Bundle();
+                 bundle.putLong("time", mNewRecord );
+                 dialog.setArguments(bundle);
+                 dialog.show( act.getSupportFragmentManager(), null);
+                 }
+               }
+             }
+
            break;
            }
          }
diff --git a/src/main/java/org/distorted/scores/RubikScores.java b/src/main/java/org/distorted/scores/RubikScores.java
index 6d7d4edb..acb11638 100644
--- a/src/main/java/org/distorted/scores/RubikScores.java
+++ b/src/main/java/org/distorted/scores/RubikScores.java
@@ -217,7 +217,7 @@ public class RubikScores
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void setRecord(int object, int size, int scramble, long timeTaken)
+  public boolean setRecord(int object, int size, int scramble, long timeTaken)
     {
     int maxsize = RubikObjectList.getObject(object).getSizes().length;
 
@@ -226,9 +226,11 @@ public class RubikScores
       if( mRecords[object][size][scramble-1]> timeTaken )
         {
         mRecords[object][size][scramble-1] = timeTaken;
-        android.util.Log.e("RubikScores","new record: ("+object+","+size+","+scramble+") ="+timeTaken);
+        return true;
         }
       }
+
+    return false;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/uistate/RubikStateSolving.java b/src/main/java/org/distorted/uistate/RubikStateSolving.java
index 5fc42443..a13ecdce 100644
--- a/src/main/java/org/distorted/uistate/RubikStateSolving.java
+++ b/src/main/java/org/distorted/uistate/RubikStateSolving.java
@@ -42,6 +42,7 @@ public class RubikStateSolving extends RubikStateAbstract
   private long mStartTime;
   private boolean mRunning;
   private RubikScores mScores;
+  private Button mBack;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -54,7 +55,7 @@ public class RubikStateSolving extends RubikStateAbstract
 
   void leaveState(RubikActivity act)
     {
-    stopCounting();
+    stopCounting(act);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -88,13 +89,13 @@ public class RubikStateSolving extends RubikStateAbstract
     buttonL.setOnClickListener(act);
     layoutBot.addView(buttonL);
 
-    Button buttonR = new Button(act);
-    buttonR.setLayoutParams(params);
-    buttonR.setId(BUTTON_ID_BACK);
-    buttonR.setPadding(padding,0,padding,0);
-    buttonR.setText(R.string.back);
-    buttonR.setOnClickListener(act);
-    layoutBot.addView(buttonR);
+    mBack = new Button(act);
+    mBack.setLayoutParams(params);
+    mBack.setId(BUTTON_ID_BACK);
+    mBack.setPadding(padding,0,padding,0);
+    mBack.setText(R.string.back);
+    mBack.setOnClickListener(act);
+    layoutBot.addView(mBack);
 
     buttonL.setVisibility(android.view.View.INVISIBLE);
     }
@@ -144,10 +145,20 @@ public class RubikStateSolving extends RubikStateAbstract
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long stopCounting()
+  public long stopCounting(RubikActivity act)
     {
     if( mRunning )
       {
+      act.runOnUiThread(new Runnable()
+        {
+        @Override
+        public void run()
+          {
+          mTime.setText(R.string.solved);
+          mBack.setClickable(false);
+          }
+        });
+
       if( mTimer!=null )
         {
         mTimer.cancel();
@@ -162,8 +173,9 @@ public class RubikStateSolving extends RubikStateAbstract
       int size    = play.getSize();
       int scramble= play.getPicker();
 
-      mScores.setRecord(object, size, scramble, timeTaken);
-      return timeTaken;
+      boolean isNew = mScores.setRecord(object, size, scramble, timeTaken);
+
+      return isNew ? timeTaken : -timeTaken;
       }
 
     return 0;
diff --git a/src/main/res/layout/dialog_solved.xml b/src/main/res/layout/dialog_solved.xml
new file mode 100644
index 00000000..89ae86fa
--- /dev/null
+++ b/src/main/res/layout/dialog_solved.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center_horizontal"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:gravity="center|fill_horizontal"
+        android:layout_marginLeft="10dp"
+        android:layout_marginRight="10dp"
+        android:layout_marginTop="0dp"
+        android:background="@color/grey"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/solved_time"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:gravity="center"
+            android:textSize="24sp"
+            android:layout_marginTop="10dp"
+            android:layout_marginLeft="10dp"
+            android:layout_marginRight="10dp"
+            android:layout_marginBottom="10dp"/>
+
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 0e668dbf..3a1a9bf6 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -9,6 +9,7 @@
     <string name="settings">Settings</string>
     <string name="scores">High Scores</string>
     <string name="about">About</string>
+    <string name="solved">Solved</string>
     <string name="back">Back</string>
     <string name="save">SAVE</string>
     <string name="ok">OK</string>
@@ -19,7 +20,7 @@
     <string name="win_effect">Win Effect</string>
     <string name="duration">Duration:</string>
     <string name="type">Type:</string>
-    <string name="downloading">Downloading...</string>
+    <string name="downloading">Downloading…</string>
     <string name="credits1">Open Source app developed using the Distorted graphics library. </string>
     <string name="credits2">Code, tutorials, learn how to write your own graphics effect: <a href="http://www.distorted.org/cube">Distorted.org</a></string>
 
@@ -27,4 +28,5 @@
     <string name="sc_placeholder">Scramble %1$d</string>
     <string name="tm_placeholder">%1$02d:%2$02d</string>
     <string name="ap_placeholder">%1$s %2$s</string>
+    <string name="ti_placeholder">%1$.1f seconds</string>
 </resources>
