commit def32b2cb75587b3116a7d2e6eb1b2901e075511
Author: leszek <leszek@koltunski.pl>
Date:   Thu Jan 11 22:42:19 2024 +0100

    Progress with sorting the objects by various criteria.

diff --git a/src/main/java/org/distorted/config/ConfigScreen.java b/src/main/java/org/distorted/config/ConfigScreen.java
index 19a21a78..c14e67cd 100644
--- a/src/main/java/org/distorted/config/ConfigScreen.java
+++ b/src/main/java/org/distorted/config/ConfigScreen.java
@@ -20,7 +20,7 @@ import android.widget.PopupWindow;
 import android.widget.ScrollView;
 import android.widget.TextView;
 
-import org.distorted.helpers.PopupCreator;
+import org.distorted.helpers.ObjectGridCreator;
 
 import org.distorted.helpers.TransparentImageButton;
 import org.distorted.main.MainActivity;
@@ -69,13 +69,13 @@ public class ConfigScreen
     ScrollView view = (ScrollView)inflate( act, R.layout.popup_object_simple, null);
     GridLayout objectGrid = view.findViewById(R.id.objectGrid);
 
-    PopupCreator.createObjectGrid(objectGrid,act,mRowCount,mColCount,numObjects,margin,cubeSize,padding);
+    ObjectGridCreator.createObjectGridClassic(objectGrid,act,mRowCount,mColCount,numObjects,margin,cubeSize,padding);
 
     for(int child=0; child<numObjects; child++)
       {
       final RubikObject obj = RubikObjectList.getObject(child);
       View v = objectGrid.getChildAt(child);
-      ImageButton button = PopupCreator.getButton(obj,v);
+      ImageButton button = ObjectGridCreator.getButton(obj,v);
       final int ordinal = child;
 
       button.setOnClickListener( new View.OnClickListener()
diff --git a/src/main/java/org/distorted/helpers/ObjectGridCreator.java b/src/main/java/org/distorted/helpers/ObjectGridCreator.java
new file mode 100644
index 00000000..a48f733b
--- /dev/null
+++ b/src/main/java/org/distorted/helpers/ObjectGridCreator.java
@@ -0,0 +1,155 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2022 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.helpers;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.GridLayout;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+
+import org.distorted.main.MainSettingsPopup;
+import org.distorted.main.R;
+import org.distorted.objects.RubikObject;
+import org.distorted.objects.RubikObjectList;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class ObjectGridCreator
+  {
+  private GridLayout mGrid;
+  private int mSortMode;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public View getChildAt(int index)
+    {
+    if( mSortMode==MainSettingsPopup.SORT_CLASSIC )
+      {
+      return mGrid.getChildAt(index);
+      }
+    else
+      {
+      return null;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void createObjectGrid(ScrollView scrollView, Activity act, int rowCount, int colCount, int numObjects, int margin, int size, int pad, int mode)
+    {
+    mSortMode = mode;
+
+    if( mode==MainSettingsPopup.SORT_CLASSIC )
+      {
+      mGrid = new GridLayout(act);
+      ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);
+      mGrid.setLayoutParams(params);
+      scrollView.addView(mGrid);
+
+      createObjectGridClassic(mGrid,act,rowCount,colCount,numObjects,margin,size,pad);
+      }
+    else
+      {
+      LinearLayout layout = new LinearLayout(act);
+      ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);
+      layout.setLayoutParams(params);
+      layout.setOrientation(LinearLayout.VERTICAL);
+      scrollView.addView(layout);
+
+
+
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static void createObjectGridClassic(GridLayout grid, Activity act, int rowCount, int colCount, int numObjects, int margin, int size, int pad)
+    {
+    GridLayout.Spec[] rowSpecs = new GridLayout.Spec[rowCount];
+    GridLayout.Spec[] colSpecs = new GridLayout.Spec[colCount];
+
+    grid.setColumnCount(colCount);
+    grid.setRowCount(rowCount);
+
+    int[] nextInRow = new int[rowCount];
+
+    for(int row=0; row<rowCount; row++)
+      {
+      rowSpecs[row] = GridLayout.spec(row);
+      nextInRow[row]= 0;
+      }
+    for(int col=0; col<colCount; col++)
+      {
+      colSpecs[col] = GridLayout.spec(col);
+      }
+
+    LayoutInflater layoutInflater = (LayoutInflater)act.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+    for(int object=0; object<numObjects; object++)
+      {
+      View v = createView(act,layoutInflater,object,pad);
+      int row = object/colCount;
+
+      GridLayout.LayoutParams params = new GridLayout.LayoutParams(rowSpecs[row],colSpecs[nextInRow[row]]);
+      params.bottomMargin = margin;
+      params.topMargin    = margin;
+      params.leftMargin   = margin;
+      params.rightMargin  = margin;
+
+      params.width = size;
+      params.height= size;
+
+      nextInRow[row]++;
+
+      grid.addView(v,params);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static ImageButton getButton(RubikObject object, View view)
+    {
+    return object!=null && object.isFree() ? (ImageButton)view : view.findViewById(R.id.non_free_button);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static View createView(Activity act, LayoutInflater inflater, int ordinal, int pad)
+    {
+    final RubikObject obj = RubikObjectList.getObject(ordinal);
+
+    if( obj!=null )
+      {
+      if( obj.isFree() )
+        {
+        ImageButton button = new ImageButton(act);
+        obj.setIconTo(act,button);
+        return button;
+        }
+      else
+        {
+        final View layout = inflater.inflate(R.layout.non_free_object, null);
+        ImageButton button= layout.findViewById(R.id.non_free_button);
+        obj.setIconTo(act,button);
+        ImageView view= layout.findViewById(R.id.non_free_lock);
+        view.setPadding(0,pad,0,0);
+        return layout;
+        }
+      }
+
+    return null;
+    }
+  }
diff --git a/src/main/java/org/distorted/helpers/PopupCreator.java b/src/main/java/org/distorted/helpers/PopupCreator.java
deleted file mode 100644
index 0de4bf53..00000000
--- a/src/main/java/org/distorted/helpers/PopupCreator.java
+++ /dev/null
@@ -1,104 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2022 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is proprietary software licensed under an EULA which you should have received      //
-// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.helpers;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.GridLayout;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-
-import org.distorted.main.R;
-import org.distorted.objects.RubikObject;
-import org.distorted.objects.RubikObjectList;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class PopupCreator
-  {
-  public static void createObjectGrid(GridLayout grid, Activity act, int rowCount, int colCount, int numObjects, int margin, int size, int pad)
-    {
-    GridLayout.Spec[] rowSpecs = new GridLayout.Spec[rowCount];
-    GridLayout.Spec[] colSpecs = new GridLayout.Spec[colCount];
-
-    grid.setColumnCount(colCount);
-    grid.setRowCount(rowCount);
-
-    int[] nextInRow = new int[rowCount];
-
-    for(int row=0; row<rowCount; row++)
-      {
-      rowSpecs[row] = GridLayout.spec(row);
-      nextInRow[row]= 0;
-      }
-    for(int col=0; col<colCount; col++)
-      {
-      colSpecs[col] = GridLayout.spec(col);
-      }
-
-    LayoutInflater layoutInflater = (LayoutInflater)act.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
-    for(int object=0; object<numObjects; object++)
-      {
-      View v = createView(act,layoutInflater,object,pad);
-      int row = object/colCount;
-
-      GridLayout.LayoutParams params = new GridLayout.LayoutParams(rowSpecs[row],colSpecs[nextInRow[row]]);
-      params.bottomMargin = margin;
-      params.topMargin    = margin;
-      params.leftMargin   = margin;
-      params.rightMargin  = margin;
-
-      params.width = size;
-      params.height= size;
-
-      nextInRow[row]++;
-
-      grid.addView(v,params);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static ImageButton getButton(RubikObject object, View view)
-    {
-    return object!=null && object.isFree() ? (ImageButton)view : view.findViewById(R.id.non_free_button);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static View createView(Activity act, LayoutInflater inflater, int ordinal, int pad)
-    {
-    final RubikObject obj = RubikObjectList.getObject(ordinal);
-
-    if( obj!=null )
-      {
-      if( obj.isFree() )
-        {
-        ImageButton button = new ImageButton(act);
-        obj.setIconTo(act,button);
-        return button;
-        }
-      else
-        {
-        final View layout = inflater.inflate(R.layout.non_free_object, null);
-        ImageButton button= layout.findViewById(R.id.non_free_button);
-        obj.setIconTo(act,button);
-        ImageView view= layout.findViewById(R.id.non_free_lock);
-        view.setPadding(0,pad,0,0);
-        return layout;
-        }
-      }
-
-    return null;
-    }
-  }
diff --git a/src/main/java/org/distorted/main/MainActivity.java b/src/main/java/org/distorted/main/MainActivity.java
index 5646f9d7..76a2d82c 100644
--- a/src/main/java/org/distorted/main/MainActivity.java
+++ b/src/main/java/org/distorted/main/MainActivity.java
@@ -71,6 +71,7 @@ public class MainActivity extends AppCompatActivity implements RubikNetwork.Upda
     private int mNumUpdates;
     private int mCurrentObject;
     private MainScrollGrid mGrid;
+    private int mSortMode;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -160,7 +161,7 @@ public class MainActivity extends AppCompatActivity implements RubikNetwork.Upda
 
       getWindowWidth(getResources().getConfiguration());
       mGrid = new MainScrollGrid();
-      mGrid.createGrid(this,mScreenWidth);
+      mGrid.createGrid(this,mScreenWidth,mSortMode);
 
       if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.P )
         {
@@ -304,6 +305,7 @@ public class MainActivity extends AppCompatActivity implements RubikNetwork.Upda
       SharedPreferences.Editor editor = preferences.edit();
 
       editor.putString("appVersion", mCurrVersion );
+      editor.putInt("sortMode", mSortMode);
 
       RubikObjectList.savePreferences(editor);
 
@@ -319,6 +321,7 @@ public class MainActivity extends AppCompatActivity implements RubikNetwork.Upda
     private void restorePreferences(SharedPreferences preferences, boolean justStarted)
       {
       mOldVersion = preferences.getString("appVersion","");
+      mSortMode = preferences.getInt("sortMode", MainSettingsPopup.SORT_CATEGORY);
 
       RubikObjectList.restorePreferences(this,preferences,justStarted);
       RubikScores scores = RubikScores.getInstance();
@@ -522,6 +525,14 @@ public class MainActivity extends AppCompatActivity implements RubikNetwork.Upda
       return 0;
       }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void sortObjectsBy(int sortMode)
+      {
+      mSortMode = sortMode;
+      mGrid.createGrid(this,mScreenWidth,mSortMode);
+      }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
     public void errorUpdate()
diff --git a/src/main/java/org/distorted/main/MainScrollGrid.java b/src/main/java/org/distorted/main/MainScrollGrid.java
index 90b71cf2..8a8685a2 100644
--- a/src/main/java/org/distorted/main/MainScrollGrid.java
+++ b/src/main/java/org/distorted/main/MainScrollGrid.java
@@ -11,10 +11,10 @@ package org.distorted.main;
 
 import android.util.DisplayMetrics;
 import android.view.View;
-import android.widget.GridLayout;
 import android.widget.ImageButton;
+import android.widget.ScrollView;
 
-import org.distorted.helpers.PopupCreator;
+import org.distorted.helpers.ObjectGridCreator;
 import org.distorted.objects.RubikObject;
 import org.distorted.objects.RubikObjectList;
 
@@ -26,10 +26,17 @@ public class MainScrollGrid
   public static final float POPUP_PADDING= 0.035f;
   public static final float POPUP_MARGIN = 0.024f;
 
+  private int mSortMode = -1;
+  private ObjectGridCreator mCreator;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  void createGrid(final MainActivity act, int windowWidth)
+  void createGrid(final MainActivity act, int windowWidth, int sortMode)
     {
+    if( mCreator==null ) mCreator = new ObjectGridCreator();
+
+    mSortMode = sortMode;
+
     int numObjects = RubikObjectList.getNumObjects();
     int rowCount   = (numObjects + NUM_COLUMNS-1) / NUM_COLUMNS;
     int colCount   = NUM_COLUMNS;
@@ -38,10 +45,10 @@ public class MainScrollGrid
     int padding    = (int)(windowWidth*POPUP_PADDING);
     int cubeSize   = objectSize - 2*margin;
 
-    GridLayout objectGrid = act.findViewById(R.id.objectGrid);
-    objectGrid.removeAllViews();
+    ScrollView scrollView = act.findViewById(R.id.objectScroll);
+    scrollView.removeAllViews();
 
-    PopupCreator.createObjectGrid(objectGrid,act,rowCount,colCount,numObjects,margin,cubeSize,padding);
+    mCreator.createObjectGrid(scrollView,act,rowCount,colCount,numObjects,margin,cubeSize,padding,sortMode);
 
     DisplayMetrics displaymetrics = new DisplayMetrics();
     act.getWindowManager().getDefaultDisplay().getRealMetrics(displaymetrics);
@@ -49,8 +56,8 @@ public class MainScrollGrid
     for(int child=0; child<numObjects; child++)
       {
       final RubikObject obj = RubikObjectList.getObject(child);
-      View v = objectGrid.getChildAt(child);
-      ImageButton button = PopupCreator.getButton(obj,v);
+      View v = mCreator.getChildAt(child);
+      ImageButton button = ObjectGridCreator.getButton(obj,v);
       final int ordinal = child;
 
       button.setOnClickListener( new View.OnClickListener()
@@ -77,7 +84,8 @@ public class MainScrollGrid
       @Override
       public void run()
         {
-        createGrid(act,scrW);
+        if( mSortMode<0 ) mSortMode = MainSettingsPopup.SORT_CATEGORY;
+        createGrid(act,scrW,mSortMode);
         }
       });
     }
diff --git a/src/main/java/org/distorted/main/MainSettingsPopup.java b/src/main/java/org/distorted/main/MainSettingsPopup.java
index df80f004..a866f801 100644
--- a/src/main/java/org/distorted/main/MainSettingsPopup.java
+++ b/src/main/java/org/distorted/main/MainSettingsPopup.java
@@ -22,17 +22,26 @@ import android.widget.PopupWindow;
 import android.widget.Spinner;
 import android.widget.TextView;
 
+import java.lang.ref.WeakReference;
 import java.lang.reflect.Field;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 public class MainSettingsPopup implements AdapterView.OnItemSelectedListener
   {
+  public static final int SORT_CLASSIC    = 0;
+  public static final int SORT_CATEGORY   = 1;
+  public static final int SORT_DIFFICULTY = 2;
+  public static final int SORT_AUTHOR     = 3;
+  public static final int SORT_YEAR       = 4;
+
+  private static final String[] mSortNames = { "Classic" , "Category" , "Difficulty", "Author" , "Year" };
   private static final float MENU_TITLE_SIZE= 0.070f;
   private static final float MENU_TEXT_SIZE = 0.060f;
+  private static final int[] mLocation = new int[2];
 
   private final PopupWindow mPopup;
-  private static final int[] mLocation = new int[2];
+  private final WeakReference<MainActivity> mAct;
   private int mCurrMethod;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -73,6 +82,8 @@ public class MainSettingsPopup implements AdapterView.OnItemSelectedListener
 
   MainSettingsPopup(MainActivity act, int width, int height)
     {
+    mAct = new WeakReference<>(act);
+
     // due to bugs in Android API <=25, a Spinner inside a PopupWindow will crash once you click on it.
     // solution: on those APIs, use a special Spinner in dialog mode (this does not crash)
     int id = android.os.Build.VERSION.SDK_INT <= 25 ? R.layout.settings_popup_android25 : R.layout.settings_popup;
@@ -97,8 +108,7 @@ public class MainSettingsPopup implements AdapterView.OnItemSelectedListener
 
     mCurrMethod = -1;
 
-    String[] actNames = { "Classic" , "Category" , "Difficulty", "Author" , "Year" };
-    ArrayAdapter<String> actAdapter = new ArrayAdapter<>(act, android.R.layout.simple_spinner_item, actNames);
+    ArrayAdapter<String> actAdapter = new ArrayAdapter<>(act, android.R.layout.simple_spinner_item, mSortNames);
     actAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
     actSpinner.setAdapter(actAdapter);
     }
@@ -158,6 +168,8 @@ public class MainSettingsPopup implements AdapterView.OnItemSelectedListener
     if( parent.getId()==R.id.sortMethod && mCurrMethod!=pos )
       {
       mCurrMethod = pos;
+      MainActivity act = mAct.get();
+      act.sortObjectsBy(pos);
       }
     }
 
diff --git a/src/main/res/layout/main.xml b/src/main/res/layout/main.xml
index bd15793a..8fd21246 100644
--- a/src/main/res/layout/main.xml
+++ b/src/main/res/layout/main.xml
@@ -94,13 +94,6 @@
         android:layout_height="0dp"
         android:layout_weight="0.84"
         android:background="@color/grey">
-
-        <GridLayout
-            android:id="@+id/objectGrid"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
-        </GridLayout>
-
     </ScrollView>
 
     <LinearLayout
