commit ebaa194f60b79f50d11ae63fe83caadd444e9aee
Author: LeszekKoltunski <leszek@koltunski.pl>
Date:   Wed May 21 00:16:15 2025 +0200

    big change to SharedPreferences: make all the values in RubikScores save their preferences just-in-time, and not on the exit of the app.

diff --git a/src/main/java/org/distorted/dialogs/DialogNewRecord.java b/src/main/java/org/distorted/dialogs/DialogNewRecord.java
index 5a10989c..a84cf397 100644
--- a/src/main/java/org/distorted/dialogs/DialogNewRecord.java
+++ b/src/main/java/org/distorted/dialogs/DialogNewRecord.java
@@ -35,14 +35,14 @@ public class DialogNewRecord extends DialogAbstract
 
   public void positiveAction()
     {
-    RubikScores scores = RubikScores.getInstance();
+    RubikScores scores = RubikScores.getInstance(getActivity());
     String name = scores.getName();
     Bundle bundle = new Bundle();
     PlayActivity act = (PlayActivity)getActivity();
 
     if( act!=null )
       {
-      if(name.length()>0)
+      if( !name.isEmpty() )
         {
         bundle.putString("argument", "true");
         DialogScores scoresDiag = new DialogScores();
diff --git a/src/main/java/org/distorted/dialogs/DialogScoresPagerAdapter.java b/src/main/java/org/distorted/dialogs/DialogScoresPagerAdapter.java
index 606c2384..af597174 100644
--- a/src/main/java/org/distorted/dialogs/DialogScoresPagerAdapter.java
+++ b/src/main/java/org/distorted/dialogs/DialogScoresPagerAdapter.java
@@ -110,7 +110,7 @@ class DialogScoresPagerAdapter extends PagerAdapter implements RubikNetwork.Scor
       {
       case '1': message(mAct.getString(R.string.networkError));
                 break;
-      case '2': RubikScores scores = RubikScores.getInstance();
+      case '2': RubikScores scores = RubikScores.getInstance(mAct);
                 Bundle bundle = new Bundle();
                 bundle.putString("argument", scores.getName() );
 
diff --git a/src/main/java/org/distorted/dialogs/DialogScoresView.java b/src/main/java/org/distorted/dialogs/DialogScoresView.java
index 28768f34..85422024 100644
--- a/src/main/java/org/distorted/dialogs/DialogScoresView.java
+++ b/src/main/java/org/distorted/dialogs/DialogScoresView.java
@@ -80,13 +80,13 @@ public class DialogScoresView extends FrameLayout
 
     Resources res = act.getResources();
     String packageName = act.getPackageName();
-    RubikScores scores = RubikScores.getInstance();
+    RubikScores scores = RubikScores.getInstance(act);
 
     boolean inserted = false;
     int myRecordInMillis = scores.getRecord(tab, level);
     String myRecord = ( myRecordInMillis<RubikScores.NO_RECORD ) ? formatRecord(myRecordInMillis) : "??";
     String myName = scores.getName();
-    if( myName.length()==0 ) myName = act.getString(R.string.you);
+    if( myName.isEmpty() ) myName = act.getString(R.string.you);
     int myCountryID = res.getIdentifier( scores.getCountry(), "drawable", packageName);
     String theirTime;
     int theirCountryID;
diff --git a/src/main/java/org/distorted/dialogs/DialogSetName.java b/src/main/java/org/distorted/dialogs/DialogSetName.java
index 4553feef..cddf73e0 100644
--- a/src/main/java/org/distorted/dialogs/DialogSetName.java
+++ b/src/main/java/org/distorted/dialogs/DialogSetName.java
@@ -49,7 +49,7 @@ public class DialogSetName extends DialogAbstract
       if( positiveButton!=null )
         {
         String editName = mEdit.getText().toString();
-        positiveButton.setEnabled(editName.length()>0);
+        positiveButton.setEnabled(!editName.isEmpty());
         }
       }
     }
@@ -57,7 +57,7 @@ public class DialogSetName extends DialogAbstract
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getResource()      { return R.layout.dialog_set_name; }
-  public int getTitleResource() { return mArgument.length()==0 ? R.string.choose_name : R.string.name_taken; }
+  public int getTitleResource() { return mArgument.isEmpty() ? R.string.choose_name : R.string.name_taken; }
   public boolean hasArgument()  { return true; }
   public int getPositive()      { return R.string.ok; }
   public int getNegative()      { return -1; }
@@ -81,7 +81,7 @@ public class DialogSetName extends DialogAbstract
       try
         {
         PlayActivity act = (PlayActivity)getActivity();
-        RubikScores.getInstance().setName(name);
+        RubikScores.getInstance(act).setName(name);
         Bundle bundle = new Bundle();
         bundle.putString("argument", "true");
         DialogScores scores = new DialogScores();
@@ -112,7 +112,7 @@ public class DialogSetName extends DialogAbstract
     mEdit.setHeight( (int)(2*mTitleSize) );
     mEdit.setTextSize(TypedValue.COMPLEX_UNIT_PX, 1.5f*mTitleSize);
 
-    if( mArgument.length()==0 )
+    if( mArgument.isEmpty() )
       {
       text.setText(R.string.new_name);
       }
diff --git a/src/main/java/org/distorted/helpers/RubikNetwork.java b/src/main/java/org/distorted/helpers/RubikNetwork.java
index 8296499b..15bf6870 100644
--- a/src/main/java/org/distorted/helpers/RubikNetwork.java
+++ b/src/main/java/org/distorted/helpers/RubikNetwork.java
@@ -21,7 +21,6 @@ import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 
 import android.app.Activity;
-import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
@@ -166,7 +165,7 @@ public class RubikNetwork
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private boolean fillValuesNormal(ScoresReceiver receiver)
+  private boolean fillValuesNormal(Activity act, ScoresReceiver receiver)
     {
     int begin=-1 ,end, len = mScores.length();
     String row;
@@ -196,7 +195,7 @@ public class RubikNetwork
       try
         {
         row = mScores.substring(begin+1,end);
-        fillRow(row);
+        fillRow(act,row);
         }
       catch(Exception ex)
         {
@@ -211,7 +210,7 @@ public class RubikNetwork
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void fillRow(String row)
+  private void fillRow(Activity act, String row)
     {
     int s1 = row.indexOf(' ');
     int s2 = row.indexOf(' ',s1+1);
@@ -247,13 +246,13 @@ public class RubikNetwork
       }
     else
       {
-      tryDoCommand(row);
+      tryDoCommand(act,row);
       }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void tryDoCommand(String row)
+  private void tryDoCommand(Activity act, String row)
     {
     if( row.startsWith("comm") )
       {
@@ -276,7 +275,7 @@ public class RubikNetwork
         if(number==1)
           {
           String country = row.substring(colon+1);
-          RubikScores scores = RubikScores.getInstance();
+          RubikScores scores = RubikScores.getInstance(act);
           scores.setCountry(country);
           }
         }
@@ -375,9 +374,9 @@ public class RubikNetwork
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private String constructSuspiciousURL(String suspURL)
+  private String constructSuspiciousURL(Activity act, String suspURL)
     {
-    RubikScores scores = RubikScores.getInstance();
+    RubikScores scores = RubikScores.getInstance(act);
     int deviceID       = scores.getDeviceID();
     String suspicious  = URLencode(suspURL);
 
@@ -388,21 +387,27 @@ public class RubikNetwork
 
   private String constructTokenURL(String token)
     {
-    RubikScores scores = RubikScores.getInstance();
-    String name = URLencode(scores.getName());
-    int deviceID= scores.getDeviceID();
-    String country = scores.getCountry();
     String version = mVersion==null ? "null" : mVersion;
     String tkn = URLencode(token);
+    RubikScores scores = RubikScores.getInstance();
 
-    return SERVER+"token.cgi?n="+name+"&i="+deviceID+"&e="+version+"&c="+country+"&t="+tkn;
+    if( scores!=null )
+      {
+      String name = URLencode(scores.getName());
+      int deviceID= scores.getDeviceID();
+      String country = scores.getCountry();
+
+      return SERVER+"token.cgi?n="+name+"&i="+deviceID+"&e="+version+"&c="+country+"&t="+tkn;
+      }
+
+    return SERVER+"token.cgi?e="+version+"&t="+tkn;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private String constructUpdatesURL()
+  private String constructUpdatesURL(Activity act)
     {
-    RubikScores sco = RubikScores.getInstance();
+    RubikScores sco = RubikScores.getInstance(act);
     String name     = URLencode(sco.getName());
     int numRuns     = sco.getNumRuns();
     int numPlay     = sco.getNumPlays();
@@ -418,9 +423,9 @@ public class RubikNetwork
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private String constructDownloadURL()
+  private String constructDownloadURL(Activity act)
     {
-    RubikScores scores = RubikScores.getInstance();
+    RubikScores scores = RubikScores.getInstance(act);
     String name = URLencode(scores.getName());
     int numRuns = scores.getNumRuns();
     int numPlay = scores.getNumPlays();
@@ -431,9 +436,9 @@ public class RubikNetwork
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private String constructSubmitURL()
+  private String constructSubmitURL(Activity act)
     {
-    RubikScores scores = RubikScores.getInstance();
+    RubikScores scores = RubikScores.getInstance(act);
     String name = URLencode(scores.getName());
     String veri = scores.isVerified() ? "1" : "";
     int numRuns = scores.getNumRuns();
@@ -472,7 +477,7 @@ public class RubikNetwork
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void downloadThread(ScoresReceiver receiver)
+  private void downloadThread(Activity act, ScoresReceiver receiver)
     {
     boolean receiveValues = true;
 
@@ -481,11 +486,11 @@ public class RubikNetwork
       if( mScores.isEmpty() && !mDowRunning )
         {
         mDowRunning = true;
-        receiveValues = network(constructDownloadURL(),receiver);
+        receiveValues = network(constructDownloadURL(act),receiver);
 
         if( mDowRunning )
           {
-          receiveValues = fillValuesNormal(receiver);
+          receiveValues = fillValuesNormal(act,receiver);
           mDowRunning = false;
           }
         }
@@ -499,26 +504,26 @@ public class RubikNetwork
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void submitThread(ScoresReceiver receiver)
+  private void submitThread(Activity act, ScoresReceiver receiver)
     {
     try
       {
-      RubikScores scores = RubikScores.getInstance();
+      RubikScores scores = RubikScores.getInstance(act);
 
       if( scores.thereAreUnsubmittedRecords() && !mSubRunning )
         {
         mSubRunning = true;
-        boolean receiveValues = network(constructSubmitURL(),receiver);
+        boolean receiveValues = network(constructSubmitURL(act),receiver);
 
         if( mSubRunning )
           {
-          receiveValues = fillValuesNormal(receiver);
+          receiveValues = fillValuesNormal(act,receiver);
           mSubRunning = false;
           }
 
         if( receiveValues )
           {
-          RubikScores.getInstance().successfulSubmit();
+          RubikScores.getInstance(act).successfulSubmit();
           receiver.receive(mCountry, mName, mTime);
           }
         }
@@ -531,9 +536,9 @@ public class RubikNetwork
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void updatesThread()
+  private void updatesThread(Activity act)
     {
-    String url = constructUpdatesURL();
+    String url = constructUpdatesURL(act);
 
     try
       {
@@ -592,9 +597,9 @@ public class RubikNetwork
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void suspiciousThread(String suspURL)
+  private void suspiciousThread(Activity act, String suspURL)
     {
-    String url = constructSuspiciousURL(suspURL);
+    String url = constructSuspiciousURL(act,suspURL);
 
     try
       {
@@ -667,7 +672,7 @@ public class RubikNetwork
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void iconThread(Context context, IconReceiver receiver)
+  private void iconThread(Activity act, IconReceiver receiver)
     {
     int numC = mUpdates.getCompletedNumber();
     int numS = mUpdates.getStartedNumber();
@@ -679,7 +684,7 @@ public class RubikNetwork
       if( iconPresent!=0 )
         {
         boolean downloaded = false;
-        Bitmap icon = mUpdates.getCompletedIcon(context,c);
+        Bitmap icon = mUpdates.getCompletedIcon(act,c);
 
         if( icon==null )
           {
@@ -704,7 +709,7 @@ public class RubikNetwork
       if( iconPresent!=0 )
         {
         boolean downloaded = false;
-        Bitmap icon = mUpdates.getStartedIcon(context,s);
+        Bitmap icon = mUpdates.getStartedIcon(act,s);
 
         if( icon==null )
           {
@@ -813,7 +818,7 @@ public class RubikNetwork
       {
       public void run()
         {
-        downloadThread(receiver);
+        downloadThread(act,receiver);
         }
       };
 
@@ -831,7 +836,7 @@ public class RubikNetwork
       {
       public void run()
         {
-        submitThread(receiver);
+        submitThread(act,receiver);
         }
       };
 
@@ -850,7 +855,7 @@ public class RubikNetwork
       {
       public void run()
         {
-        updatesThread();
+        updatesThread(act);
         }
       };
 
@@ -868,7 +873,7 @@ public class RubikNetwork
       {
       public void run()
         {
-        suspiciousThread(suspicious);
+        suspiciousThread(act,suspicious);
         }
       };
 
@@ -926,7 +931,7 @@ public class RubikNetwork
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void downloadIcons(final Context context, final IconReceiver receiver)
+  public void downloadIcons(final Activity act, final IconReceiver receiver)
     {
     initializeStatics();
 
@@ -934,7 +939,7 @@ public class RubikNetwork
       {
       public void run()
         {
-        iconThread(context,receiver);
+        iconThread(act,receiver);
         }
       };
 
diff --git a/src/main/java/org/distorted/helpers/RubikScores.java b/src/main/java/org/distorted/helpers/RubikScores.java
index a138a9c8..05897cc1 100644
--- a/src/main/java/org/distorted/helpers/RubikScores.java
+++ b/src/main/java/org/distorted/helpers/RubikScores.java
@@ -12,10 +12,13 @@ package org.distorted.helpers;
 import java.util.HashMap;
 import java.util.UUID;
 
+import android.app.Activity;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.telephony.TelephonyManager;
 
+import androidx.preference.PreferenceManager;
+
 import com.google.firebase.crashlytics.FirebaseCrashlytics;
 
 import org.distorted.main.BuildConfig;
@@ -34,6 +37,14 @@ public class RubikScores
   public static final int RECORD_NEW     = 1;
   public static final int RECORD_NOT_NEW = 2;
 
+  public static final String SCORES_NAME = "scores_name";
+  public static final String SCORES_ISVE = "scores_isVerified";
+  public static final String SCORES_NUMP = "scores_numPlays";
+  public static final String SCORES_NUMR = "scores_numRuns";
+  public static final String SCORES_DEID = "scores_deviceid";
+  public static final String SCORES_NUMW = "scores_review";   // legacy
+  public static final String SCORES_NUMS = "scores_numStars";
+
   public static final int MAX_RECORD = 10;
   private static final int MULT = 1000000;
   public static final int NO_RECORD = Integer.MAX_VALUE;
@@ -45,7 +56,6 @@ public class RubikScores
   private int mNumPlays;
   private int mNumWins;
   private int mDeviceID;
-  private int mNumStars;
 
   private static class MapValue
     {
@@ -60,10 +70,11 @@ public class RubikScores
     }
 
   private final HashMap<Integer,MapValue> mMap;
+  private final SharedPreferences mPreferences;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private RubikScores()
+  private RubikScores(Activity act)
     {
     mMap = new HashMap<>();
 
@@ -76,7 +87,8 @@ public class RubikScores
     mNumRuns = -1;
     mDeviceID= -1;
     mNumWins =  0;
-    mNumStars=  0;
+
+    mPreferences = PreferenceManager.getDefaultSharedPreferences(act);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -117,6 +129,12 @@ public class RubikScores
       MapValue value = mMap.get(key);
       if( value!=null ) value.submitted = true;
       }
+
+    SharedPreferences.Editor editor = mPreferences.edit();
+    editor.putBoolean( SCORES_ISVE, mNameIsVerified);
+
+    for(int level=0; level<=MAX_RECORD; level++) saveRecordLevel(editor,level);
+    editor.apply();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -186,56 +204,37 @@ public class RubikScores
   public int getNumRuns()         { return mNumRuns; }
   public String getName()         { return mName; }
   public String getCountry()      { return mCountry; }
-  public void incrementNumPlays() { mNumPlays++; }
-  public void incrementNumRuns()  { mNumRuns++; }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int incrementNumWins()
-    {
-    mNumWins++;
-    return mNumWins;
-    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void changeNumStars(int stars)
+  public void incrementNumPlays()
     {
-    mNumStars += stars;
-    if( mNumStars<0 ) mNumStars = 0;
+    mNumPlays++;
+    SharedPreferences.Editor editor = mPreferences.edit();
+    editor.putInt( SCORES_NUMP, mNumPlays);
+    editor.apply();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public int getNumStars()
+  public void incrementNumRuns()
     {
-    return mNumStars;
+    mNumRuns++;
+    SharedPreferences.Editor editor = mPreferences.edit();
+    editor.putInt( SCORES_NUMR, mNumRuns);
+    editor.apply();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void correctNumStars()
+  public int incrementNumWins()
     {
-    int numObjects = RubikObjectList.getNumObjects();
-
-    for(int obj=0; obj<numObjects; obj++)
-      {
-      for(int level=0; level<=LEVELS_SHOWN; level++)
-        {
-        if( isSolved(obj,level) )
-          {
-          int numStars = computeNumStars(level+1);
-          mNumStars += numStars;
-          }
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    mNumWins++;
+    SharedPreferences.Editor editor = mPreferences.edit();
+    editor.putInt( SCORES_NUMW, mNumWins);
+    editor.apply();
 
-  public int computeNumStars(int level)
-    {
-    return level>LEVELS_SHOWN ? 50 : level;
+    return mNumWins;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -243,6 +242,10 @@ public class RubikScores
   public void setName(String newName)
     {
     mName = newName;
+
+    SharedPreferences.Editor editor = mPreferences.edit();
+    editor.putString( SCORES_NAME, mName);
+    editor.apply();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -256,6 +259,9 @@ public class RubikScores
       {
       MapValue value = new MapValue(record,0);
       mMap.put(key,value);
+      SharedPreferences.Editor editor = mPreferences.edit();
+      saveRecordLevel(editor,level);
+      editor.apply();
       return RECORD_FIRST;
       }
 
@@ -265,6 +271,9 @@ public class RubikScores
       {
       MapValue value = new MapValue(record,0);
       mMap.put(key,value);
+      SharedPreferences.Editor editor = mPreferences.edit();
+      saveRecordLevel(editor,level);
+      editor.apply();
       return RECORD_NEW;
       }
 
@@ -305,11 +314,7 @@ public class RubikScores
         }
       }
 
-    // Special case: Dominicana. Its ISO-3166-alpha-2 country code is 'do' which we can't have here
-    // because we later on map this to a resource name (the flag) and 'do' is a reserved Java keyword
-    // and can't be a resource name.
-
-    if( mCountry.equals("do") ) mCountry = "dm";
+    dominicana();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -317,63 +322,59 @@ public class RubikScores
   public void setCountry(String country)
     {
     mCountry = country;
+    dominicana();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Special case: Dominicana. Its ISO-3166-alpha-2 country code is 'do' which we can't have here
+// because we later on map this to a resource name (the flag) and 'do' is a reserved Java keyword
+// and can't be a resource name.
 
-    if( mCountry.equals("do") ) mCountry = "dm";  // see above
+  private void dominicana()
+    {
+    if( mCountry.equals("do") ) mCountry = "dm";
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static RubikScores getInstance(Activity act)
+    {
+    if( mThis==null ) mThis = new RubikScores(act);
+    return mThis;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public static RubikScores getInstance()
     {
-    if( mThis==null ) mThis = new RubikScores();
     return mThis;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public synchronized void savePreferences(SharedPreferences.Editor editor)
+  private void saveRecordLevel(SharedPreferences.Editor editor, int level)
     {
     int numObjects = RubikObjectList.getNumObjects();
     StringBuilder builder = new StringBuilder();
 
-    for(int level=0; level<=MAX_RECORD; level++)
+    for(int object=0; object<numObjects; object++)
       {
-      builder.setLength(0);
+      int key = mapKey(object,level);
+      RubikObject obj = RubikObjectList.getObject(object);
+      MapValue value = mMap.get(key);
 
-      for(int object=0; object<numObjects; object++)
+      if( obj!=null && value!=null && value.record<NO_RECORD )
         {
-        int key = mapKey(object,level);
-        RubikObject obj = RubikObjectList.getObject(object);
-        MapValue value = mMap.get(key);
-
-        if( obj!=null && value!=null && value.record<NO_RECORD )
-          {
-          builder.append(obj.getUpperName());
-          builder.append("=");
-          builder.append(value.record);
-          builder.append(",");
-          builder.append(value.submitted ? 1:0 );
-          builder.append(" ");
-          }
+        builder.append(obj.getUpperName());
+        builder.append("=");
+        builder.append(value.record);
+        builder.append(",");
+        builder.append(value.submitted ? 1:0 );
+        builder.append(" ");
         }
-
-      editor.putString("scores_record"+level, builder.toString());
       }
 
-    editor.putString("scores_name"  , mName  );
-    editor.putBoolean("scores_isVerified", mNameIsVerified);
-    editor.putInt("scores_numPlays", mNumPlays);
-    editor.putInt("scores_numRuns" , mNumRuns );
-    editor.putInt("scores_deviceid", mDeviceID);
-    editor.putInt("scores_review"  , mNumWins );   // legacy name
-    editor.putInt("scores_numStars", mNumStars );
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public synchronized void savePreferencesMinimal(SharedPreferences.Editor editor)
-    {
-    editor.putInt("scores_numStars", mNumStars );
+    editor.putString("scores_record"+level, builder.toString());
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -447,15 +448,20 @@ public class RubikScores
         }
       }
 
-    mName           = preferences.getString("scores_name"  , "" );
-    mNameIsVerified = preferences.getBoolean("scores_isVerified", false);
-    mNumPlays       = preferences.getInt("scores_numPlays", 0);
-    mNumRuns        = preferences.getInt("scores_numRuns" , 0);
-    mDeviceID       = preferences.getInt("scores_deviceid",-1);
-    mNumWins        = preferences.getInt("scores_review"  , 0);
-    mNumStars       = preferences.getInt("scores_numStars", 0);
+    mName           = preferences.getString( SCORES_NAME  , "" );
+    mNameIsVerified = preferences.getBoolean( SCORES_ISVE, false);
+    mNumPlays       = preferences.getInt( SCORES_NUMP, 0);
+    mNumRuns        = preferences.getInt( SCORES_NUMR, 0);
+    mDeviceID       = preferences.getInt( SCORES_DEID,-1);
+    mNumWins        = preferences.getInt( SCORES_NUMW, 0);
 
-    if( mDeviceID==-1 ) mDeviceID = privateGetDeviceID();
+    if( mDeviceID==-1 )
+      {
+      mDeviceID = privateGetDeviceID();
+      SharedPreferences.Editor editor = mPreferences.edit();
+      editor.putInt( SCORES_DEID, mDeviceID);
+      editor.apply();
+      }
 
     if( thereWasError ) recordDBError(errorStr);
     }
diff --git a/src/main/java/org/distorted/main/MainActivity.java b/src/main/java/org/distorted/main/MainActivity.java
index 050c4eb5..a2f6444d 100644
--- a/src/main/java/org/distorted/main/MainActivity.java
+++ b/src/main/java/org/distorted/main/MainActivity.java
@@ -155,7 +155,7 @@ public class MainActivity extends BaseActivity implements RubikNetwork.Updatee,
         mJustStarted = false;
 
         network.downloadUpdates(this);
-        RubikScores scores = RubikScores.getInstance();
+        RubikScores scores = RubikScores.getInstance(this);
         scores.incrementNumRuns();
         scores.setCountry(this);
         }
@@ -220,7 +220,7 @@ public class MainActivity extends BaseActivity implements RubikNetwork.Updatee,
       mSortMode = preferences.getInt("sortMode", MainSettingsPopup.SORT_DEFAULT);
 
       RubikObjectList.restorePreferences(this,preferences,justStarted);
-      RubikScores scores = RubikScores.getInstance();
+      RubikScores scores = RubikScores.getInstance(this);
       scores.restorePreferences(preferences);
 
       if( scores.isVerified() )
diff --git a/src/main/java/org/distorted/main/MainObjectPopup.java b/src/main/java/org/distorted/main/MainObjectPopup.java
index 06d30e00..b94937d3 100644
--- a/src/main/java/org/distorted/main/MainObjectPopup.java
+++ b/src/main/java/org/distorted/main/MainObjectPopup.java
@@ -196,7 +196,7 @@ public class MainObjectPopup
 
   private void setupLevelButtons(MainActivity act, RubikObject object, View layout, int width,int padding, int margin, int darkC, int passedC)
     {
-    RubikScores scores = RubikScores.getInstance();
+    RubikScores scores = RubikScores.getInstance(act);
     Resources res = act.getResources();
     ColorStateList colorG = ColorStateList.valueOf(res.getColor(passedC));
     ColorStateList colorD = ColorStateList.valueOf(res.getColor(darkC));
diff --git a/src/main/java/org/distorted/messaging/RubikMessagingService.java b/src/main/java/org/distorted/messaging/RubikMessagingService.java
index 34fae985..ee3057bd 100644
--- a/src/main/java/org/distorted/messaging/RubikMessagingService.java
+++ b/src/main/java/org/distorted/messaging/RubikMessagingService.java
@@ -52,7 +52,7 @@ public class RubikMessagingService extends FirebaseMessagingService
     {
     Log.e(TAG, "From: " + remoteMessage.getFrom());
 
-    if (remoteMessage.getData().size() > 0)
+    if( !remoteMessage.getData().isEmpty() )
       {
       Log.e(TAG, "Message data payload: " + remoteMessage.getData());
 
diff --git a/src/main/java/org/distorted/objects/RubikObjectList.java b/src/main/java/org/distorted/objects/RubikObjectList.java
index 1333f805..ef682b64 100644
--- a/src/main/java/org/distorted/objects/RubikObjectList.java
+++ b/src/main/java/org/distorted/objects/RubikObjectList.java
@@ -12,6 +12,7 @@ package org.distorted.objects;
 import java.util.ArrayList;
 import java.util.Locale;
 
+import android.app.Activity;
 import android.content.Context;
 import android.content.SharedPreferences;
 
@@ -82,9 +83,7 @@ public class RubikObjectList
 
   private static boolean allObjectsUnlocked()
     {
-    return mBoughtObjects!=null       &&
-           mBoughtObjects.length()>0  &&
-           mBoughtObjects.charAt(0)=='*';
+    return mBoughtObjects!=null && !mBoughtObjects.isEmpty() && mBoughtObjects.charAt(0)=='*';
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -168,7 +167,7 @@ public class RubikObjectList
       android.util.Log.e("D", "bought objects: "+mBoughtObjects);
       }
 
-    if( mBoughtObjects.length()>0 )
+    if( !mBoughtObjects.isEmpty() )
       {
       if( mBoughtObjects.charAt(0)=='*' )
         {
@@ -286,7 +285,7 @@ public class RubikObjectList
       String shortName = object.getUpperName();
       if( SHOW_IAP_DEBUG ) android.util.Log.e("D", "object "+shortName+" marked as bought");
       object.markFree();
-      String add = mBoughtObjects.length()==0 ? shortName : (","+shortName);
+      String add = mBoughtObjects.isEmpty() ? shortName : (","+shortName);
       mBoughtObjects += add;
       }
     }
@@ -363,7 +362,7 @@ public class RubikObjectList
 
     if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", downloaded);
 
-    if( !downloaded.equals(""))
+    if( !downloaded.isEmpty() )
       {
       String[] dObjects = downloaded.split(",");
 
@@ -425,9 +424,9 @@ public class RubikObjectList
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public static void firstUpgradeMarkAllSolvedAsFree()
+  public static void firstUpgradeMarkAllSolvedAsFree(Activity act)
     {
-    RubikScores scores = RubikScores.getInstance();
+    RubikScores scores = RubikScores.getInstance(act);
     int numUnclaimed = scores.numberOfSolvedMAXes();
 
     if( numUnclaimed>0 )
@@ -484,9 +483,7 @@ public class RubikObjectList
     String shortName = object.getUpperName();
     if( SHOW_IAP_DEBUG ) android.util.Log.e("D", "object "+object.getUpperName()+" marked as free");
     object.markFree();
-    int price = object.getPrice();
-    scores.changeNumStars(-price);
-    String add = mBoughtObjects.length()==0 ? shortName : (","+shortName);
+    String add = mBoughtObjects.isEmpty() ? shortName : (","+shortName);
     mBoughtObjects += add;
     }
 
diff --git a/src/main/java/org/distorted/play/PlayActivity.java b/src/main/java/org/distorted/play/PlayActivity.java
index 11f5b0d2..c70a7517 100644
--- a/src/main/java/org/distorted/play/PlayActivity.java
+++ b/src/main/java/org/distorted/play/PlayActivity.java
@@ -23,7 +23,6 @@ import androidx.preference.PreferenceManager;
 import com.google.firebase.analytics.FirebaseAnalytics;
 
 import org.distorted.dialogs.DialogScores;
-import org.distorted.helpers.RubikScores;
 import org.distorted.helpers.BaseActivity;
 import org.distorted.library.main.DistortedLibrary;
 import org.distorted.objectlib.main.InitAssets;
@@ -198,9 +197,6 @@ public class PlayActivity extends BaseActivity implements DialogScores.ScoresInv
       solv.saveMovePreferences(KEY_SOLV,editor);
       }
 
-    RubikScores scores = RubikScores.getInstance();
-    scores.savePreferences(editor);
-
     editor.apply();
     }
 
diff --git a/src/main/java/org/distorted/play/PlayLibInterface.java b/src/main/java/org/distorted/play/PlayLibInterface.java
index 4b4b596d..ce16a6e4 100644
--- a/src/main/java/org/distorted/play/PlayLibInterface.java
+++ b/src/main/java/org/distorted/play/PlayLibInterface.java
@@ -96,7 +96,7 @@ public class PlayLibInterface implements ObjectLibInterface
 
   private void reportRecord(PlayActivity act, long startTime, long endTime, int scrambleNum)
     {
-    RubikScores scores  = RubikScores.getInstance();
+    RubikScores scores  = RubikScores.getInstance(act);
     int objectOrdinal = act.getObjectOrdinal();
     String name = scores.getName();
     RubikObject obj = RubikObjectList.getObject(objectOrdinal);
@@ -149,7 +149,7 @@ public class PlayLibInterface implements ObjectLibInterface
     android.util.Log.e("D", "ASKING FOR REVIEW");
 
     mReviewAsked = true;
-    final String name = RubikScores.getInstance().getName();
+    final String name = RubikScores.getInstance(act).getName();
     final long timeBegin = System.currentTimeMillis();
     final ReviewManager manager = ReviewManagerFactory.create(act);
     Task<ReviewInfo> request = manager.requestReviewFlow();
@@ -194,7 +194,7 @@ public class PlayLibInterface implements ObjectLibInterface
     if( ScreenList.getCurrentScreen()==ScreenList.SCRA )
       {
       PlayActivity act = mAct.get();
-      RubikScores.getInstance().incrementNumPlays();
+      RubikScores.getInstance(act).incrementNumPlays();
 
       act.runOnUiThread(new Runnable()
         {
@@ -280,7 +280,7 @@ public class PlayLibInterface implements ObjectLibInterface
       int objectOrdinal = act.getObjectOrdinal();
       ScreenSolving solving = (ScreenSolving)ScreenList.SOLV.getScreenClass();
       mNewRecord = solving.stopTimerAndGetRecord();
-      mIsNewRecord = submittable ? solving.setRecord(objectOrdinal) : RECORD_NOT_NEW;
+      mIsNewRecord = submittable ? solving.setRecord(act,objectOrdinal) : RECORD_NOT_NEW;
       }
     }
 
@@ -297,7 +297,7 @@ public class PlayLibInterface implements ObjectLibInterface
 
       if( submittable ) reportRecord(act,startTime,endTime,scrambleNum);
 
-      RubikScores scores = RubikScores.getInstance();
+      RubikScores scores = RubikScores.getInstance(act);
       int numWins = scores.incrementNumWins();
       int numRuns = scores.getNumRuns();
 
diff --git a/src/main/java/org/distorted/play/ScreenSolving.java b/src/main/java/org/distorted/play/ScreenSolving.java
index bf072980..ceb7dc9d 100644
--- a/src/main/java/org/distorted/play/ScreenSolving.java
+++ b/src/main/java/org/distorted/play/ScreenSolving.java
@@ -9,6 +9,7 @@
 
 package org.distorted.play;
 
+import android.app.Activity;
 import android.content.SharedPreferences;
 import android.util.TypedValue;
 import android.view.LayoutInflater;
@@ -34,18 +35,10 @@ public class ScreenSolving extends ScreenBase
   private Timer mTimer;
   private long mStartTime;
   private boolean mRunning;
-  private final RubikScores mScores;
   private long mElapsed;
   private int mLevel;
   private TransparentImageButton mBackButton;
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  ScreenSolving()
-    {
-    mScores = RubikScores.getInstance();
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   void leaveScreen(PlayActivity act)
@@ -177,9 +170,10 @@ public class ScreenSolving extends ScreenBase
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public int setRecord(int object)
+  public int setRecord(Activity act, int object)
     {
-    return mScores.setRecord(object, mLevel, (int)mElapsed);
+    RubikScores scores = RubikScores.getInstance(act);
+    return scores.setRecord(object, mLevel, (int)mElapsed);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
