commit 8838ac87e98303cc44d5025b0d920f22723dcac4
Author: leszek <leszek@koltunski.pl>
Date:   Sat Nov 15 15:19:25 2025 +0100

    Progress remembering solves.

diff --git a/src/main/java/org/distorted/helpers/RubikRememberedSolves.java b/src/main/java/org/distorted/helpers/RubikRememberedSolves.java
index 66315d92..96a390fa 100644
--- a/src/main/java/org/distorted/helpers/RubikRememberedSolves.java
+++ b/src/main/java/org/distorted/helpers/RubikRememberedSolves.java
@@ -39,22 +39,28 @@ public class RubikRememberedSolves
 
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public static RubikRememberedSolves getInstance()
+  private String readContents(InputStream stream) throws IOException
     {
-    if( mThis==null ) mThis = new RubikRememberedSolves();
-    return mThis;
+    BufferedReader br = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8));
+    StringBuilder contents = new StringBuilder();
+    String tmp;
+
+    while( (tmp = br.readLine()) != null) contents.append(tmp);
+    br.close();
+    stream.close();
+
+    return contents.toString();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private JSONObject createData(long time, Static4D rot, int[] quats) throws JSONException
+  private JSONObject createData(int elapsed, Static4D rot, int[] quats) throws JSONException
     {
     JSONObject data = new JSONObject();
-    data.put("time",time);
+    data.put("time",System.currentTimeMillis());
+    data.put("elapsed", elapsed);
     data.put("rot0", rot.get0() );
     data.put("rot1", rot.get1() );
     data.put("rot2", rot.get2() );
@@ -66,6 +72,87 @@ public class RubikRememberedSolves
     return data;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private String deleteInfo(InputStream stream, int level, long time)
+    {
+    try
+      {
+      String contents = readContents(stream);
+      JSONArray levels = new JSONArray(contents);
+      JSONArray lvl = levels.getJSONArray(level);
+      int numSolves = lvl.length();
+
+      for(int s=0; s<numSolves; s++)
+        {
+        JSONObject obj = lvl.getJSONObject(s);
+        long tm = obj.getLong("time");
+        if( tm==time ) { lvl.remove(s); break; }
+        }
+
+      return levels.toString();
+      }
+    catch(IOException iex)    { android.util.Log.e("D", "addInfo: failed to read file: "+iex.getMessage() ); }
+    catch(JSONException jex)  { android.util.Log.e("D", "addInfo: failed to parse file: "+jex.getMessage()); }
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private String addInfo(InputStream stream, int level, int elapsed, Static4D rot, int[] quats)
+    {
+    if( stream!=null )
+      {
+      try
+        {
+        String contents = readContents(stream);
+        JSONArray levels = new JSONArray(contents);
+        JSONArray lvl = levels.getJSONArray(level);
+        JSONObject data = createData(elapsed,rot,quats);
+        if( lvl.length()>=MAXSOLVES ) lvl.remove(0);
+        lvl.put(data);
+        return levels.toString();
+        }
+      catch(IOException iex)    { android.util.Log.e("D", "addInfo: failed to read file: "+iex.getMessage() ); }
+      catch(JSONException jex)  { android.util.Log.e("D", "addInfo: failed to parse file: "+jex.getMessage()); }
+      }
+    else
+      {
+      JSONArray levels = new JSONArray();
+
+      try
+        {
+        for(int l=0; l< MainObjectPopup.LEVELS_SHOWN+1; l++)
+          {
+          JSONArray save = new JSONArray();
+
+          if( l==level )
+            {
+            JSONObject data = createData(elapsed,rot,quats);
+            save.put(data);
+            }
+          levels.put(save);
+          }
+        }
+      catch(JSONException jex)  { android.util.Log.e("D", "addInfo: failed to parse file: "+jex.getMessage()); }
+
+      return levels.toString();
+      }
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static RubikRememberedSolves getInstance()
+    {
+    if( mThis==null ) mThis = new RubikRememberedSolves();
+    return mThis;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public long getTime(JSONObject object) throws JSONException
@@ -73,6 +160,13 @@ public class RubikRememberedSolves
     return object.getLong("time");
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getElapsed(JSONObject object) throws JSONException
+    {
+    return object.getInt("elapsed");
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public Static4D getRotQuat(JSONObject object) throws JSONException
@@ -116,21 +210,6 @@ public class RubikRememberedSolves
     return null;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private String readContents(InputStream stream) throws IOException
-    {
-    BufferedReader br = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8));
-    StringBuilder contents = new StringBuilder();
-    String tmp;
-
-    while( (tmp = br.readLine()) != null) contents.append(tmp);
-    br.close();
-    stream.close();
-
-    return contents.toString();
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public void deleteSolve(Activity act, String objname, int level, long time)
@@ -161,12 +240,12 @@ public class RubikRememberedSolves
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void rememberSolve(Activity act, String objname, int level, long time, Static4D rot, int[] quats)
+  public void rememberSolve(Activity act, String objname, int level, int elapsed, Static4D rot, int[] quats)
     {
     String filename  = objname.toLowerCase()+"_solves.json";
     RubikFiles files = RubikFiles.getInstance();
     InputStream input= files.openFile(act,filename);
-    String contents  = addInfo(input,level,time,rot,quats);
+    String contents  = addInfo(input,level,elapsed,rot,quats);
     File file        = new File(act.getFilesDir(), filename);
 
     try( FileOutputStream fos = new FileOutputStream(file) )
@@ -178,93 +257,4 @@ public class RubikRememberedSolves
       android.util.Log.e("D", "rememberSolve: failed to save file "+filename+" : "+ex.getMessage());
       }
     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void debug(int level, long time, Static4D rot, int[] quats)
-    {
-    StringBuilder quatStr = new StringBuilder();
-
-    for(int c : quats)
-      {
-      quatStr.append(c);
-      quatStr.append(" ");
-      }
-
-    android.util.Log.e("D",   "level: "+level+"\n"+
-                                    "time: "+time+"\n"+
-                                    "rotQuat: " +rot.get0()+" "+rot.get1()+" "+rot.get2()+" "+rot.get3()+"\n"+
-                                    "quats: "+quatStr );
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public String deleteInfo(InputStream stream, int level, long time)
-    {
-    try
-      {
-      String contents = readContents(stream);
-      JSONArray levels = new JSONArray(contents);
-      JSONArray lvl = levels.getJSONArray(level);
-      int numSolves = lvl.length();
-
-      for(int s=0; s<numSolves; s++)
-        {
-        JSONObject obj = lvl.getJSONObject(s);
-        long tm = obj.getLong("time");
-        if( tm==time ) { lvl.remove(s); break; }
-        }
-
-      return levels.toString();
-      }
-    catch(IOException iex)    { android.util.Log.e("D", "addInfo: failed to read file: "+iex.getMessage() ); }
-    catch(JSONException jex)  { android.util.Log.e("D", "addInfo: failed to parse file: "+jex.getMessage()); }
-
-    return null;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public String addInfo(InputStream stream, int level, long time, Static4D rot, int[] quats)
-    {
-    if( stream!=null )
-      {
-      try
-        {
-        String contents = readContents(stream);
-        JSONArray levels = new JSONArray(contents);
-        JSONArray lvl = levels.getJSONArray(level);
-        JSONObject data = createData(time,rot,quats);
-        if( lvl.length()>=MAXSOLVES ) lvl.remove(0);
-        lvl.put(data);
-        return levels.toString();
-        }
-      catch(IOException iex)    { android.util.Log.e("D", "addInfo: failed to read file: "+iex.getMessage() ); }
-      catch(JSONException jex)  { android.util.Log.e("D", "addInfo: failed to parse file: "+jex.getMessage()); }
-      }
-    else
-      {
-      JSONArray levels = new JSONArray();
-
-      try
-        {
-        for(int l=0; l< MainObjectPopup.LEVELS_SHOWN+1; l++)
-          {
-          JSONArray save = new JSONArray();
-
-          if( l==level )
-            {
-            JSONObject data = createData(time,rot,quats);
-            save.put(data);
-            }
-          levels.put(save);
-          }
-        }
-      catch(JSONException jex)  { android.util.Log.e("D", "addInfo: failed to parse file: "+jex.getMessage()); }
-
-      return levels.toString();
-      }
-
-    return null;
-    }
 }
diff --git a/src/main/java/org/distorted/main/MainActivity.java b/src/main/java/org/distorted/main/MainActivity.java
index d2f50294..28db6e8d 100644
--- a/src/main/java/org/distorted/main/MainActivity.java
+++ b/src/main/java/org/distorted/main/MainActivity.java
@@ -338,7 +338,7 @@ public class MainActivity extends BaseActivity implements RubikNetwork.Updatee,
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    public void switchToPlay(RubikObject object, int ordinal, int scrambles, int level, long time, Static4D rot, int[] quats)
+    public void switchToPlay(RubikObject object, int ordinal, int scrambles, int level, int elapsed, Static4D rot, int[] quats)
       {
       boolean local = object.isLocal();
       String name = local ? object.getLowerName() : object.getUpperName();
@@ -349,7 +349,7 @@ public class MainActivity extends BaseActivity implements RubikNetwork.Updatee,
       intent.putExtra("scrambles", scrambles);
       intent.putExtra("local", local );
       intent.putExtra("ordinal", ordinal );
-      intent.putExtra("time", time);
+      intent.putExtra("elapsed", elapsed);
 
       if( rot!=null )
         {
diff --git a/src/main/java/org/distorted/main/MainSolvesPopup.java b/src/main/java/org/distorted/main/MainSolvesPopup.java
index ea2e4376..f4441230 100644
--- a/src/main/java/org/distorted/main/MainSolvesPopup.java
+++ b/src/main/java/org/distorted/main/MainSolvesPopup.java
@@ -88,9 +88,10 @@ public class MainSolvesPopup
         {
         JSONObject object = array.getJSONObject(s);
         long time = solves.getTime(object);
+        int elapsed = solves.getElapsed(object);
         Static4D rotQuat = solves.getRotQuat(object);
         int[] quats = solves.getQuats(object);
-        View pane = createOldPane(act, s, time, rotQuat, quats, pL, pT);
+        View pane = createOldPane(act, s, time, elapsed, rotQuat, quats, pL, pT);
         mLayout.addView(pane);
         }
       }
@@ -121,7 +122,8 @@ public class MainSolvesPopup
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private View createOldPane(MainActivity act, int index, long time, Static4D rot, int[] quats, LinearLayout.LayoutParams pView, LinearLayout.LayoutParams pText)
+  private View createOldPane(MainActivity act, int index, long time, int elapsed, Static4D rot,
+                             int[] quats, LinearLayout.LayoutParams pView, LinearLayout.LayoutParams pText)
     {
     View view = act.getLayoutInflater().inflate(R.layout.dialog_solve_old_pane, null);
     TextView title = view.findViewById(R.id.solves_pane_title);
@@ -166,7 +168,7 @@ public class MainSolvesPopup
         public void onClick(View v)
           {
           mPopup.dismiss();
-          act.switchToPlay(mObject,mObjectOrdinal,mNumScrambles,mLevel, time, rot, quats);
+          act.switchToPlay(mObject,mObjectOrdinal,mNumScrambles,mLevel, elapsed, rot, quats);
           }
         });
 
diff --git a/src/main/java/org/distorted/play/PlayActivity.java b/src/main/java/org/distorted/play/PlayActivity.java
index 21aa6869..048da1ef 100644
--- a/src/main/java/org/distorted/play/PlayActivity.java
+++ b/src/main/java/org/distorted/play/PlayActivity.java
@@ -53,10 +53,10 @@ public class PlayActivity extends BaseActivity implements DialogScores.ScoresInv
 
     private String mObjectName;
     private int mNumScrambles;
-    private long mTime;
+    private int mElapsed;
     private Static4D mRotQuat;
     private int[] mQuats;
-    private boolean mObjectLocal;
+    private boolean mObjectLocal, mModeResume;
     private int mObjectOrdinal;
     private int mLevel;
     private boolean mModeFree;
@@ -84,16 +84,16 @@ public class PlayActivity extends BaseActivity implements DialogScores.ScoresInv
         mNumScrambles  = b.getInt("scrambles");
         mObjectLocal   = b.getBoolean("local");
         mObjectOrdinal = b.getInt("ordinal");
-        mTime          = b.getLong("time");
+        mElapsed       = b.getInt("elapsed");
 
-        if( mTime>0 )
+        if( mElapsed>0 )
           {
           float r0 = b.getFloat("rot0");
           float r1 = b.getFloat("rot1");
           float r2 = b.getFloat("rot2");
           float r3 = b.getFloat("rot3");
           mRotQuat = new Static4D(r0,r1,r2,r3);
-          mQuats = b.getIntArray("quqts");
+          mQuats = b.getIntArray("quats");
           }
         }
       else
@@ -103,12 +103,13 @@ public class PlayActivity extends BaseActivity implements DialogScores.ScoresInv
         mNumScrambles = 0;
         mObjectLocal = true;
         mObjectOrdinal = 0;
-        mTime = 0;
+        mElapsed = 0;
         mRotQuat = null;
         mQuats = null;
         }
 
       mModeFree = (mLevel<0);
+      mModeResume = (mElapsed>0);
 
       computeScreenDimensions();
       hideNavigationBar();
@@ -163,22 +164,28 @@ public class PlayActivity extends BaseActivity implements DialogScores.ScoresInv
       SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
       restorePreferences(preferences);
 
-      ScreenList sl =  mJustStarted ?
-                      (mModeFree ? ScreenList.FREE : ScreenList.SCRA) :
-                      ScreenList.getCurrentScreen();
-
-      ScreenList.switchScreen(this,sl);
+      ScreenList sl = mJustStarted ? (mModeFree ? ScreenList.FREE : ScreenList.SCRA) : ScreenList.getCurrentScreen();
+      ScreenList.switchScreen(this, sl);
 
       if( !mJustStarted ) restoreMoves(preferences);
 
       if( !mObjectName.isEmpty() )
         {
-        changeIfDifferent(mObjectName,mObjectLocal,mObjectOrdinal,control);
+        changeIfDifferent(mObjectName, mObjectLocal, mObjectOrdinal, control);
         }
 
-      if( mJustStarted && !mModeFree )
+      if( !mModeResume )
+        {
+        if( mJustStarted && !mModeFree )
+          {
+          control.scrambleObject(mNumScrambles);
+          }
+        }
+      else
         {
-        control.scrambleObject(mNumScrambles);
+        control.rotateNow(mRotQuat);
+        TwistyObject object = control.getObject();
+        object.setCubitQuats(mQuats);
         }
 
       mJustStarted = false;
@@ -327,7 +334,8 @@ public class PlayActivity extends BaseActivity implements DialogScores.ScoresInv
       TwistyObject object = control.getObject();
 
       int level = mLevel==LEVELS_SHOWN ? mLevel : mLevel+1;
-      long time = System.currentTimeMillis();
+      ScreenSolving solving = (ScreenSolving)ScreenList.SOLV.getScreenClass();
+      int time = solving.stopTimerAndGetRecord();
       String name = object.getShortName();
       int numCubits = object.getNumCubits();
       int[] quats = new int[numCubits];
diff --git a/src/main/java/org/distorted/play/ScreenScrambling.java b/src/main/java/org/distorted/play/ScreenScrambling.java
index cfbb58ca..03a470b0 100644
--- a/src/main/java/org/distorted/play/ScreenScrambling.java
+++ b/src/main/java/org/distorted/play/ScreenScrambling.java
@@ -24,10 +24,9 @@ public class ScreenScrambling extends ScreenAbstract
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  void leaveScreen(PlayActivity act)
-    {
-
-    }
+  void leaveScreen(PlayActivity act) { }
+  public void savePreferences(SharedPreferences.Editor editor) { }
+  public void restorePreferences(SharedPreferences preferences) { }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -72,18 +71,4 @@ public class ScreenScrambling extends ScreenAbstract
         }
       });
     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void savePreferences(SharedPreferences.Editor editor)
-    {
-
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void restorePreferences(SharedPreferences preferences)
-    {
-
-    }
   }
