commit e9e744f72f7f45621db809bace282904fa15e30a
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Tue Dec 20 14:59:19 2022 +0100

    Changes to the initialization - initialize the 'bought objects'

diff --git a/src/main/java/org/distorted/dialogs/RubikDialogUpdateView.java b/src/main/java/org/distorted/dialogs/RubikDialogUpdateView.java
index 0100327f..33f4dab1 100644
--- a/src/main/java/org/distorted/dialogs/RubikDialogUpdateView.java
+++ b/src/main/java/org/distorted/dialogs/RubikDialogUpdateView.java
@@ -194,10 +194,10 @@ public class RubikDialogUpdateView implements RubikNetwork.Downloadee
         try
           {
           JsonReader reader = JsonReader.getInstance();
-          reader.readNumScramblesAndIsFree(act,objectName);
+          reader.readNumScramblesAndPrice(act,objectName);
           mInfo.mNumScrambles = reader.getNumScrambles();
-          mInfo.mIsFree       = reader.isFree();
-          if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "Read from JSON numScrambles="+mInfo.mNumScrambles+" isFree="+mInfo.mIsFree);
+          mInfo.mPrice        = reader.getPrice();
+          if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "Read from JSON numScrambles="+mInfo.mNumScrambles+" price="+mInfo.mPrice);
 
           if( mInfo.mExtrasStream!=null )
             {
@@ -213,7 +213,7 @@ public class RubikDialogUpdateView implements RubikNetwork.Downloadee
 
             if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "1");
 
-            boolean success = RubikObjectList.addDownloadedObject(act, mInfo.mObjectShortName, mInfo.mNumScrambles, mInfo.mIsFree,
+            boolean success = RubikObjectList.addDownloadedObject(act, mInfo.mObjectShortName, mInfo.mNumScrambles, mInfo.mPrice,
                                                                   mInfo.mObjectMinorVersion, mInfo.mExtrasMinorVersion, mIconSaved, oSuccess, eSuccess);
             if( success )
               {
diff --git a/src/main/java/org/distorted/external/RubikScores.java b/src/main/java/org/distorted/external/RubikScores.java
index f854423a..29a803ef 100644
--- a/src/main/java/org/distorted/external/RubikScores.java
+++ b/src/main/java/org/distorted/external/RubikScores.java
@@ -241,6 +241,7 @@ public class RubikScores
   public void changeNumStars(int stars)
     {
     mNumStars += stars;
+    if( mNumStars<0 ) mNumStars = 0;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/external/RubikUpdates.java b/src/main/java/org/distorted/external/RubikUpdates.java
index f18a29cb..dc03f673 100644
--- a/src/main/java/org/distorted/external/RubikUpdates.java
+++ b/src/main/java/org/distorted/external/RubikUpdates.java
@@ -36,7 +36,7 @@ public class RubikUpdates
     public final boolean mUpdateExtras;
 
     public int mNumScrambles;
-    public boolean mIsFree;
+    public int mPrice;
     public Bitmap mIcon;
     public InputStream mObjectStream;
     public InputStream mExtrasStream;
diff --git a/src/main/java/org/distorted/main/RubikActivity.java b/src/main/java/org/distorted/main/RubikActivity.java
index 71971cdc..fba7fd86 100644
--- a/src/main/java/org/distorted/main/RubikActivity.java
+++ b/src/main/java/org/distorted/main/RubikActivity.java
@@ -63,6 +63,7 @@ public class RubikActivity extends AppCompatActivity
 {
     public static final boolean SHOW_DOWNLOADED_DEBUG = false;
     public static final boolean SHOW_SOLVED_DEBUG     = true;
+    public static final boolean USE_IAP               = false;
 
     public static final float PADDING             = 0.01f;
     public static final float SMALL_MARGIN        = 0.004f;
@@ -106,6 +107,8 @@ public class RubikActivity extends AppCompatActivity
     private boolean mPolicyAccepted, mIsChinese;
     private int mCurrentApiVersion;
     private int mHeightUpperBar, mHeightLowerBar;
+    private int mOldVersion1, mOldVersion2, mOldVersion3;
+    private String mCurrVersion;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -131,6 +134,8 @@ public class RubikActivity extends AppCompatActivity
       cutoutHack();
       computeBarHeights();
 
+      mCurrVersion = getAppVers();
+
       Thread thread = new Thread()
         {
         public void run()
@@ -309,7 +314,7 @@ public class RubikActivity extends AppCompatActivity
       SharedPreferences.Editor editor = preferences.edit();
 
       editor.putBoolean("policyAccepted", mPolicyAccepted);
-      editor.putString("appVersion", getAppVers() );
+      editor.putString("appVersion", mCurrVersion );
 
       for (int i=0; i<BaseEffect.Type.LENGTH; i++)
         {
@@ -350,6 +355,7 @@ public class RubikActivity extends AppCompatActivity
       {
       mPolicyAccepted = preferences.getBoolean("policyAccepted", false);
       String oldVersion = preferences.getString("appVersion","");
+      parseVersion(oldVersion);
 
       RubikObjectList.restorePreferences(this,preferences,justStarted);
 
@@ -363,11 +369,34 @@ public class RubikActivity extends AppCompatActivity
         ScreenList.getScreen(i).getScreenClass().restorePreferences(preferences);
         }
 
-      // this needs to be after the above ScreenList.restore as that restores the Records which we need here
-      if( justStarted ) RubikObjectList.setObjectFreeState();
+      RubikScores scores = RubikScores.getInstance();
+
+      if( scores.isVerified() )
+        {
+        FirebaseAnalytics analytics = getAnalytics();
+        analytics.setUserId(scores.getName());
+        }
+
+      // all old users upgrading from version <1.11.4, where there was no concept of 'stars',
+      // get all the stars they have earned
+      int numStars = scores.getNumStars();
+
+      if( justStarted && numStars==0 && oldVersionLessThan(1,11,4) )
+        {
+        scores.correctNumStars();
+        }
+
+      // in 1.11.5 we have introduced IAP. When upgrading from something less than 1.11.5 to
+      // something at least 1.11.5, mark all solved objects as free.
+      // this needs to be after the above ScreenList.getScreen(i).getScreenClass().restorePreferences()
+      // as that restores the Records which we need here
+      // also needs to be after RubikObjectList.restorePreferences()
+      if( USE_IAP && justStarted && oldVersionLessThan(1,11,5) && !mCurrVersion.equals("1.11.4") )
+        {
+        RubikObjectList.firstUpgradeMarkAllSolvedAsFree();
+        }
 
       ScreenList.restorePreferences(preferences);
-      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
 
       // Versions <= 1.8.6 did not save their 'appVersion' to preferences, therefore in their
       // case the 'mOldVersion' - version of the app which saved the preferences on the last
@@ -380,55 +409,45 @@ public class RubikActivity extends AppCompatActivity
 
       if( !oldVersion.equals("") )
         {
+        RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
         view.getObjectControl().restorePreferences(preferences);
         }
+      }
 
-      RubikScores scores = RubikScores.getInstance();
+///////////////////////////////////////////////////////////////////////////////////////////////////
 
-      if( scores.isVerified() )
-        {
-        FirebaseAnalytics analytics = getAnalytics();
-        analytics.setUserId(scores.getName());
-        }
+    private void parseVersion(String version)
+      {
+      if( version==null ) return;
 
-      // all old users upgrading from version <1.11.4, where there was no concept of 'stars',
-      // get all the stars they have earned
-      int numStars = scores.getNumStars();
+      try
+        {
+        String[] parts = version.split("\\.");
 
-      if( numStars==0 && upgradingFromVersionWithNoStars(oldVersion) )
+        if( parts.length==3 )
+          {
+          mOldVersion1 = Integer.parseInt(parts[0]);
+          mOldVersion2 = Integer.parseInt(parts[1]);
+          mOldVersion3 = Integer.parseInt(parts[2]);
+          }
+        }
+      catch(Exception ignored)
         {
-        scores.correctNumStars();
+        mOldVersion1 = 0;
+        mOldVersion2 = 0;
+        mOldVersion3 = 0;
         }
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    private boolean upgradingFromVersionWithNoStars(String version)
+    private boolean oldVersionLessThan(int v1, int v2, int v3)
       {
-      if( version!=null )
-        {
-        try
-          {
-          int dot = version.indexOf('.',2);
-
-          if( dot>2 )
-            {
-            String middle = version.substring(2,dot);
-            int middleInt = Integer.parseInt(middle);
-
-            if( middleInt<11 ) return true;
-            if( middleInt==11 )
-              {
-              String end = version.substring(dot+1);
-              int endInt = Integer.parseInt(end);
-              return endInt<4;
-              }
-            }
-          }
-        catch(Exception ignored) {}
-        }
-
-      return false;
+      if( mOldVersion1<v1 ) return true;
+      if( mOldVersion1>v1 ) return false;
+      if( mOldVersion2<v2 ) return true;
+      if( mOldVersion2>v2 ) return false;
+      return mOldVersion3<v3;
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/main/RubikObjectLibInterface.java b/src/main/java/org/distorted/main/RubikObjectLibInterface.java
index d277bc1a..5a1966bb 100644
--- a/src/main/java/org/distorted/main/RubikObjectLibInterface.java
+++ b/src/main/java/org/distorted/main/RubikObjectLibInterface.java
@@ -450,16 +450,25 @@ public class RubikObjectLibInterface implements ObjectLibInterface, ListenerOver
 
       switch(mIsNewRecord)
         {
-        case RECORD_FIRST  : RubikScreenPlay play = (RubikScreenPlay) ScreenList.PLAY.getScreenClass();
-                             int level = play.getLevel();
-                             RubikScores scores = RubikScores.getInstance();
-                             int newStars = scores.computeNumStars(level);
-                             int totStars = scores.getNumStars();
-                             scores.changeNumStars(newStars);
-                             DistortedScreen screen = act.getScreen();
-                             OverlayStars stars = new OverlayStars();
-                             DataStars data  = new DataStars(totStars,newStars,act.getResources());
-                             stars.startOverlay(screen,this,data);
+        case RECORD_FIRST  : if( RubikActivity.USE_IAP )
+                                {
+                                RubikScreenPlay play = (RubikScreenPlay) ScreenList.PLAY.getScreenClass();
+                                int level = play.getLevel();
+                                RubikScores scores = RubikScores.getInstance();
+                                int newStars = scores.computeNumStars(level);
+                                int totStars = scores.getNumStars();
+                                scores.changeNumStars(newStars);
+                                DistortedScreen screen = act.getScreen();
+                                OverlayStars stars = new OverlayStars();
+                                DataStars data  = new DataStars(totStars,newStars,act.getResources());
+                                stars.startOverlay(screen,this,data);
+                                }
+                             else
+                                {
+                                RubikDialogNewRecord d2 = new RubikDialogNewRecord();
+                                d2.setArguments(bundle);
+                                d2.show( act.getSupportFragmentManager(), RubikDialogNewRecord.getDialogTag() );
+                                }
                              break;
         case RECORD_NEW    : RubikDialogNewRecord d2 = new RubikDialogNewRecord();
                              d2.setArguments(bundle);
diff --git a/src/main/java/org/distorted/objects/RubikObject.java b/src/main/java/org/distorted/objects/RubikObject.java
index 255af796..15788b58 100644
--- a/src/main/java/org/distorted/objects/RubikObject.java
+++ b/src/main/java/org/distorted/objects/RubikObject.java
@@ -24,6 +24,7 @@ import org.distorted.dmesh.ObjectMesh;
 import org.distorted.external.RubikFiles;
 import org.distorted.jsons.ObjectJson;
 import org.distorted.main.R;
+import org.distorted.main.RubikActivity;
 import org.distorted.objectlib.json.JsonWriter;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.patterns.RubikPatternList;
@@ -42,6 +43,7 @@ public class RubikObject
   private final String[][] mPatterns;
 
   private boolean mIsFree;
+  private int mPrice;
   private int mJsonID, mMeshID, mExtrasID;
   private int mObjectMinor, mExtrasMinor;
   private int mNumScramble;
@@ -58,7 +60,8 @@ public class RubikObject
     mUpperName   = type.name();
     mLowerName   = type.name().toLowerCase(Locale.ENGLISH);
     mNumScramble = type.getNumScramble();
-    mIsFree      = type.isFree();
+    mPrice       = type.getPrice();
+    mIsFree      = mPrice==0;
 
     mIconID      = type.getIconID();
     mJsonID      = ObjectJson.getObjectJsonID(ordinal);
@@ -87,7 +90,8 @@ public class RubikObject
     mLowerName     = object.shortName;
     mUpperName     = object.shortName.toUpperCase(Locale.ENGLISH);
     mNumScramble   = object.numScrambles;
-    mIsFree        = object.free;
+    mPrice         = object.price;
+    mIsFree        = mPrice==0;
     mObjectMinor   = object.objectMinor;
     mExtrasMinor   = object.extrasMinor;
 
@@ -215,18 +219,24 @@ public class RubikObject
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// temporarily switch off IAP (charging for objects) - all objects are free!
+
+  public int getPrice()
+    {
+    return mPrice;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public boolean isFree()
     {
-    return mIsFree;
+    return (!RubikActivity.USE_IAP || mIsFree);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public void markFree()
     {
-    mIsFree = true;
+    mIsFree=true;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objects/RubikObjectList.java b/src/main/java/org/distorted/objects/RubikObjectList.java
index c909bb8d..e06ffed9 100644
--- a/src/main/java/org/distorted/objects/RubikObjectList.java
+++ b/src/main/java/org/distorted/objects/RubikObjectList.java
@@ -21,6 +21,7 @@ import org.distorted.main.RubikActivity;
 import org.distorted.objectlib.main.ObjectSignatures;
 import org.distorted.objectlib.main.ObjectType;
 
+import static org.distorted.main.RubikActivity.SHOW_SOLVED_DEBUG;
 import static org.distorted.objectlib.main.TwistyObject.MESH_NICE;
 import static org.distorted.objectlib.main.ObjectType.NUM_OBJECTS;
 import static org.distorted.main.RubikActivity.SHOW_DOWNLOADED_DEBUG;
@@ -40,15 +41,14 @@ public class RubikObjectList
   public static class DownloadedObject
     {
     String shortName;
-    boolean icon,object,extras,free;
-    int numScrambles, objectMinor, extrasMinor;
+    boolean icon,object,extras;
+    int numScrambles, objectMinor, extrasMinor, price;
 
-    DownloadedObject(String sName, int scrambles, boolean isFree, int oMinor, int eMinor, boolean i, boolean o, boolean e)
+    DownloadedObject(String sName, int scrambles, int pr, int oMinor, int eMinor, boolean i, boolean o, boolean e)
       {
-      shortName = sName;
-
+      shortName   = sName;
       numScrambles= scrambles;
-      free        = isFree;
+      price       = pr;
       objectMinor = oMinor;
       extrasMinor = eMinor;
 
@@ -59,19 +59,15 @@ public class RubikObjectList
     }
 
   private static ArrayList<DownloadedObject> mDownloadedObjects;
-  /*
-  private static String mFreeSolvedObjects;
-  private static String mFreeBoughtObjects;
-  private static int mNumFreeSolved;
-  */
+
+  private static String mBoughtObjects;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private RubikObjectList()
     {
-    mNumObjects    = 0;
-    mNumExtras     = 0;
-    //mNumFreeSolved = 0;
-
+    mNumObjects        = 0;
+    mNumExtras         = 0;
     mObjects           = new ArrayList<>();
     mDownloadedObjects = new ArrayList<>();
 
@@ -120,16 +116,7 @@ public class RubikObjectList
     RubikObject obj = new RubikObject(object);
     mObjects.add(obj);
     mNumObjects++;
-    /*
-    int numUnclaimed = getNumUnclaimedSolves();
 
-    if( numUnclaimed>0 && !obj.isFree() )
-      {
-      String objname = obj.getUpperName();
-      if( SHOW_SOLVED_DEBUG ) android.util.Log.e("D", "downloadedObject: "+objname+" making it solved. Unclaimed: "+numUnclaimed);
-      solveObject(obj,objname);
-      }
-    */
     if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "creating downloaded object "+obj.getUpperName() );
 
     if( obj.hasExtras() )
@@ -151,29 +138,26 @@ public class RubikObjectList
 
   private static void restoreFreedObjects(SharedPreferences preferences)
     {
-    /*
-    mFreeSolvedObjects = preferences.getString("rol_freeSolved", "");
-    mFreeBoughtObjects = preferences.getString("rol_freeBought", "");
+    mBoughtObjects = preferences.getString("rol_bought", "");
 
     if( SHOW_SOLVED_DEBUG )
       {
-      android.util.Log.e("D", "freeSolved: "+mFreeSolvedObjects);
-      android.util.Log.e("D", "freeBought: "+mFreeBoughtObjects);
+      android.util.Log.e("D", "bought objects: "+mBoughtObjects);
       }
 
-    if( mFreeBoughtObjects.length()>0 )
+    if( mBoughtObjects.length()>0 )
       {
-      if( mFreeBoughtObjects.charAt(0)=='*' )
+      if( mBoughtObjects.charAt(0)=='*' )
         {
         for(int i=0; i<mNumObjects; i++)
           {
           RubikObject o = mObjects.get(i);
-          o.markFree();
+          if( o!=null ) o.markFree();
           }
         }
       else
         {
-        String[] objs = mFreeBoughtObjects.split(",");
+        String[] objs = mBoughtObjects.split(",");
 
         for( String obj : objs )
           {
@@ -182,96 +166,12 @@ public class RubikObjectList
           }
         }
       }
-
-    if( mFreeSolvedObjects.length()>0 )
-      {
-      String[] objs = mFreeSolvedObjects.split(",");
-      mNumFreeSolved = objs.length;
-
-      for( String obj : objs )
-        {
-        RubikObject o = getObject(obj);
-        if( o!=null ) o.markFree();
-        }
-      }
-     */
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static int getNumUnclaimedSolves()
-    {
-    /*
-    RubikScores scores = RubikScores.getInstance();
-    int numMAXes = scores.numberOfSolvedMAXes();
-    return numMAXes-mNumFreeSolved;
-     */
-
-    return 0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static int markAllSolvedAsFree()
-    {
-    RubikScores scores = RubikScores.getInstance();
-    int numObjects = RubikObjectList.getNumObjects();
-    int ret = 0;
-
-    for(int obj=0; obj<numObjects; obj++)
-      {
-      RubikObject object = getObject(obj);
-
-      if( object!=null && !object.isFree() && scores.isSolved(obj,LEVELS_SHOWN) )
-        {
-        solveObject(object.getUpperName());
-        ret++;
-        }
-      }
-
-    return ret;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static void markAsFree(int numToBeMarked)
-    {
-    int numObjects = RubikObjectList.getNumObjects();
-
-    for(int obj=0; obj<numObjects && numToBeMarked>0; obj++)
-      {
-      RubikObject object = getObject(obj);
-
-      if( object!=null && !object.isFree() )
-        {
-        solveObject(object.getUpperName());
-        numToBeMarked--;
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static boolean solveObject(RubikObject object, String shortName)
-    {
-    /*
-    if( object!=null && !object.isFree() )
-      {
-      if( SHOW_SOLVED_DEBUG ) android.util.Log.e("D", "object "+shortName+" marked as solved");
-      object.markFree();
-      String add = mFreeSolvedObjects.length()==0 ? shortName : (","+shortName);
-      mFreeSolvedObjects += add;
-      mNumFreeSolved++;
-      return true;
-      }
-    */
-    return false;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
 
-  public static boolean addDownloadedObject(Context context, String shortName, int numScrambles, boolean isFree, int objectMinor,
+  public static boolean addDownloadedObject(Context context, String shortName, int numScrambles, int price, int objectMinor,
                                          int extrasMinor, boolean icon, boolean object, boolean extras)
     {
     if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "New downloaded object "+shortName+" icon="+icon+" object="+object+" extras="+extras);
@@ -309,7 +209,7 @@ public class RubikObjectList
     if( !object ) objectMinor=-1;
     if( !extras ) extrasMinor=-1;
 
-    DownloadedObject obj = new DownloadedObject(shortName,numScrambles,isFree,objectMinor,extrasMinor,icon,object,extras);
+    DownloadedObject obj = new DownloadedObject(shortName,numScrambles,price,objectMinor,extrasMinor,icon,object,extras);
     if ( internalAddDownloadedObject(obj) )
       {
       if( SHOW_DOWNLOADED_DEBUG ) android.util.Log.e("D", "Adding new downloaded object "+shortName+" icon="+obj.icon+" object="+obj.object+" extras="+obj.extras);
@@ -361,8 +261,7 @@ public class RubikObjectList
 
   public static boolean allAlreadyBought()
     {
-    //return mFreeBoughtObjects.equals("*");
-    return false;
+    return mBoughtObjects.equals("*");
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -380,18 +279,17 @@ public class RubikObjectList
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public static boolean objectAlreadyBought(String shortName)
+  public static boolean objectAlreadyBought(RubikObject object)
     {
-    RubikObject o = getObject(shortName);
-    return ( o!=null && o.isFree() );
+    return ( object!=null && object.isFree() );
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+// calling PurchaseScreenPane charges the user.
 
   public static void buyAll()
     {
-    /*
-    mFreeBoughtObjects = "*";
+    mBoughtObjects = "*";
     if( SHOW_SOLVED_DEBUG ) android.util.Log.e("D", "all objects marked as bought");
 
     for(int i=0; i<mNumObjects; i++)
@@ -399,34 +297,21 @@ public class RubikObjectList
       RubikObject o = mObjects.get(i);
       o.markFree();
       }
-    */
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+// calling PurchaseScreenPane charges the user.
 
-  public static boolean buyObject(String shortName)
+  public static void buyObject(RubikObject object)
     {
-    /*
-    RubikObject o = getObject(shortName);
-
-    if( o!=null && !o.isFree() )
+    if( object!=null && !object.isFree() )
       {
+      String shortName = object.getUpperName();
       if( SHOW_SOLVED_DEBUG ) android.util.Log.e("D", "object "+shortName+" marked as bought");
-      o.markFree();
-      String add = mFreeBoughtObjects.length()==0 ? shortName : (","+shortName);
-      mFreeBoughtObjects += add;
-      return true;
+      object.markFree();
+      String add = mBoughtObjects.length()==0 ? shortName : (","+shortName);
+      mBoughtObjects += add;
       }
-    */
-    return false;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static boolean solveObject(String shortName)
-    {
-    RubikObject object = getObject(shortName);
-    return solveObject(object,shortName);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -461,7 +346,7 @@ public class RubikObjectList
         downloadedObjects.append(' ');
         downloadedObjects.append(object.extras ? "1":"0");
         downloadedObjects.append(' ');
-        downloadedObjects.append(object.free ? "true":"false");
+        downloadedObjects.append(object.price);
         }
 
       String objects = downloadedObjects.toString();
@@ -472,16 +357,16 @@ public class RubikObjectList
       {
       editor.putString("rol_downloaded", "" );
       }
-/*
-    editor.putString("rol_freeSolved", mFreeSolvedObjects);
-    editor.putString("rol_freeBought", mFreeBoughtObjects);
 
-    if( SHOW_SOLVED_DEBUG )
+    if( RubikActivity.USE_IAP )
       {
-      android.util.Log.e("D", "saving solved objects: "+mFreeSolvedObjects);
-      android.util.Log.e("D", "saving bought objects: "+mFreeBoughtObjects);
+      editor.putString("rol_bought", mBoughtObjects);
+
+      if( SHOW_SOLVED_DEBUG )
+        {
+        android.util.Log.e("D", "saving bought objects: "+mBoughtObjects);
+        }
       }
- */
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -512,7 +397,17 @@ public class RubikObjectList
           String icon = parts[4];
           String obje = parts[5];
           String extr = parts[6];
-          boolean isFree = (length==7 || Boolean.parseBoolean(parts[7]));
+
+          int price;
+
+          if( length==7 ) price=0;
+          else
+            {
+            char c = parts[7].charAt(0);
+            if( c=='t' )      price = 0;
+            else if( c=='f' ) price = ObjectType.DEFAULT_PRICE_OF_OLD_OBJECTS;
+            else              price = Integer.parseInt(parts[7]);
+            }
 
           int scrambles = Integer.parseInt(scra);
           int oMinor    = Integer.parseInt(objM);
@@ -522,7 +417,7 @@ public class RubikObjectList
           boolean bObje = obje.equals("1");
           boolean bExtr = extr.equals("1");
 
-          addDownloadedObject(context,name,scrambles,isFree,oMinor,eMinor,bIcon,bObje,bExtr);
+          addDownloadedObject(context,name,scrambles,price,oMinor,eMinor,bIcon,bObje,bExtr);
           }
         }
       }
@@ -533,7 +428,7 @@ public class RubikObjectList
     mObject = getOrdinal(objName);
     if( mObject<0 || mObject>=mNumObjects ) mObject = DEF_OBJECT;
 
-    if( justStarted) restoreFreedObjects(preferences);
+    if( RubikActivity.USE_IAP && justStarted ) restoreFreedObjects(preferences);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -555,15 +450,61 @@ public class RubikObjectList
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public static void setObjectFreeState()
+  public static void firstUpgradeMarkAllSolvedAsFree()
     {
-    int numUnclaimed = getNumUnclaimedSolves();
+    RubikScores scores = RubikScores.getInstance();
+    int numUnclaimed = scores.numberOfSolvedMAXes();
 
     if( numUnclaimed>0 )
       {
-      int marked = markAllSolvedAsFree();
+      int marked = markAllSolvedAsFree(scores);
       int stillUnclaimed = numUnclaimed-marked;
-      if( stillUnclaimed>0) markAsFree(stillUnclaimed);
+      if( stillUnclaimed>0) markObjectsAsFree(scores,stillUnclaimed);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static int markAllSolvedAsFree(RubikScores scores)
+    {
+    int numObjects = RubikObjectList.getNumObjects();
+    int ret = 0;
+
+    for(int obj=0; obj<numObjects; obj++)
+      {
+      RubikObject object = getObject(obj);
+
+      if( object!=null && !object.isFree() && scores.isSolved(obj,LEVELS_SHOWN) )
+        {
+        if( SHOW_SOLVED_DEBUG ) android.util.Log.e("D", "object "+object.getUpperName()+" marked as free");
+        object.markFree();
+        int price = object.getPrice();
+        scores.changeNumStars(-price);
+        ret++;
+        }
+      }
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static void markObjectsAsFree(RubikScores scores, int numToBeMarked)
+    {
+    int numObjects = RubikObjectList.getNumObjects();
+
+    for(int obj=0; obj<numObjects && numToBeMarked>0; obj++)
+      {
+      RubikObject object = getObject(obj);
+
+      if( object!=null && !object.isFree() )
+        {
+        if( SHOW_SOLVED_DEBUG ) android.util.Log.e("D", "object "+object.getUpperName()+" marked as free");
+        object.markFree();
+        int price = object.getPrice();
+        scores.changeNumStars(-price);
+        numToBeMarked--;
+        }
       }
     }
 
diff --git a/src/main/java/org/distorted/purchase/PurchaseScreenPane.java b/src/main/java/org/distorted/purchase/PurchaseScreenPane.java
index 15b721aa..37262c5b 100644
--- a/src/main/java/org/distorted/purchase/PurchaseScreenPane.java
+++ b/src/main/java/org/distorted/purchase/PurchaseScreenPane.java
@@ -27,6 +27,8 @@ import java.io.InputStream;
 
 public class PurchaseScreenPane
 {
+  private static final int UNLOCK_ALL_PRICE = 1000;
+
   private static final int[] IMAGES =
     {
     R.drawable.difficulty1,
@@ -43,17 +45,17 @@ public class PurchaseScreenPane
   private RubikObject mObject;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// TODO
+// TODO: charge the user an amount of stars
 
-  private boolean buyOne(String shortName)
+  private boolean chargeUser(RubikObject object)
     {
     return true;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// TODO
+// TODO: charge the user an amount of stars
 
-  private boolean buyAll()
+  private boolean chargeUser()
     {
     return true;
     }
@@ -64,13 +66,10 @@ public class PurchaseScreenPane
     {
     if( mObject!=null )
       {
-      String shortName = mObject.getUpperName();
-
-      if( !RubikObjectList.objectAlreadyBought(shortName) && buyOne(shortName) )
+      if( !RubikObjectList.objectAlreadyBought(mObject) && chargeUser(mObject) )
         {
-        android.util.Log.e("D", "buying "+shortName);
-
-        RubikObjectList.buyObject(shortName);
+        android.util.Log.e("D", "buying "+mObject.getUpperName());
+        RubikObjectList.buyObject(mObject);
         }
       }
     }
@@ -79,7 +78,7 @@ public class PurchaseScreenPane
 
   private void allButtonClicked()
     {
-    if( !RubikObjectList.allAlreadyBought() && buyAll() )
+    if( !RubikObjectList.allAlreadyBought() && chargeUser() )
       {
       RubikObjectList.buyAll();
       }
