commit 50bb18a1c55e7d88b1a15131de96fde565290dfb
Author: leszek <leszek@koltunski.pl>
Date:   Mon Dec 30 23:09:17 2024 +0100

    a dialog to interrupt too long solves

diff --git a/src/main/java/org/distorted/dialogs/DialogInterrupt.java b/src/main/java/org/distorted/dialogs/DialogInterrupt.java
new file mode 100644
index 00000000..f73e5939
--- /dev/null
+++ b/src/main/java/org/distorted/dialogs/DialogInterrupt.java
@@ -0,0 +1,48 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 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.dialogs;
+
+import android.app.Dialog;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.fragment.app.FragmentActivity;
+
+import org.distorted.main.R;
+import org.distorted.solvers.SolverActivity;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DialogInterrupt extends DialogAbstract
+  {
+  public int getResource()            { return R.layout.dialog_message; }
+  public int getTitleResource()       { return -1; }
+  public boolean hasArgument()        { return false; }
+  public int getPositive()            { return R.string.interrupt; }
+  public int getNegative()            { return -1; }
+  public void negativeAction()        { }
+  public static String getDialogTag() { return "DialogInterrupt"; }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void positiveAction()
+    {
+    final SolverActivity act = (SolverActivity)getContext();
+    if( act!=null ) act.interruptSolve();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void prepareBody(Dialog dialog, View view, FragmentActivity act, float size)
+    {
+    TextView mess = view.findViewById(R.id.dialog_message);
+    mess.setText(R.string.solving);
+    }
+  }
diff --git a/src/main/java/org/distorted/dialogs/DialogMessage.java b/src/main/java/org/distorted/dialogs/DialogMessage.java
index 78e1c54e..eaa0cdec 100644
--- a/src/main/java/org/distorted/dialogs/DialogMessage.java
+++ b/src/main/java/org/distorted/dialogs/DialogMessage.java
@@ -21,13 +21,14 @@ import org.distorted.main.R;
 
 public class DialogMessage extends DialogAbstract
   {
-  public int getResource()      { return R.layout.dialog_message; }
-  public int getTitleResource() { return -1; }
-  public boolean hasArgument()  { return true; }
-  public int getPositive()      { return R.string.ok; }
-  public int getNegative()      { return -1; }
-  public void negativeAction()  { }
-  public void positiveAction()  { }
+  public int getResource()            { return R.layout.dialog_message; }
+  public int getTitleResource()       { return -1; }
+  public boolean hasArgument()        { return true; }
+  public int getPositive()            { return R.string.ok; }
+  public int getNegative()            { return -1; }
+  public void negativeAction()        { }
+  public void positiveAction()        { }
+  public static String getDialogTag() { return "DialogBandageMessage"; }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -37,10 +38,4 @@ public class DialogMessage extends DialogAbstract
     mess.setText(mArgument);
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static String getDialogTag()
-    {
-    return "DialogBandageMessage";
-    }
   }
diff --git a/src/main/java/org/distorted/dialogs/DialogSolverView.java b/src/main/java/org/distorted/dialogs/DialogSolverView.java
index c1d58e28..15acb34c 100644
--- a/src/main/java/org/distorted/dialogs/DialogSolverView.java
+++ b/src/main/java/org/distorted/dialogs/DialogSolverView.java
@@ -63,6 +63,7 @@ public class DialogSolverView
 
         if( solver!=null )
           {
+          act.setSolver(solver);
           int[] result = solver.validatePosition(object);
           if( result[0]>=0 ) solver.solve(screen,result);
           else screen.displayImpossibleDialog(result,solver.getFaceColors());
diff --git a/src/main/java/org/distorted/solvers/ScreenSetupPosition.java b/src/main/java/org/distorted/solvers/ScreenSetupPosition.java
index 2430835f..7e0981cb 100644
--- a/src/main/java/org/distorted/solvers/ScreenSetupPosition.java
+++ b/src/main/java/org/distorted/solvers/ScreenSetupPosition.java
@@ -10,10 +10,8 @@
 package org.distorted.solvers;
 
 import static org.distorted.objectlib.metadata.ListObjects.*;
-import static org.distorted.objectlib.solvers.verifiers.SolverTablebase.*;
 
 import android.content.SharedPreferences;
-import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
@@ -25,7 +23,9 @@ import android.widget.ImageButton;
 import android.widget.LinearLayout;
 
 import androidx.core.content.ContextCompat;
+import androidx.fragment.app.FragmentManager;
 
+import org.distorted.dialogs.DialogInterrupt;
 import org.distorted.dialogs.DialogSolverError;
 import org.distorted.dialogs.DialogSolverImpossible;
 import org.distorted.dialogs.DialogSolvers;
@@ -42,6 +42,8 @@ import org.distorted.objectlib.solvers.verifiers.SolverAbstract;
 import org.distorted.objectlib.solvers.verifiers.SolvingList;
 
 import java.lang.ref.WeakReference;
+import java.util.Timer;
+import java.util.TimerTask;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -51,41 +53,6 @@ public class ScreenSetupPosition extends ScreenAbstract implements ResultScreen
   private static final int MODE_NORMAL = 0;
   private static final int MODE_DINO_4 = 1;
 
-  private static final int[][] colorsHex =
-    {
-      {R.string.color_yellow1,R.string.color_yellow2,R.string.color_yellow3,R.string.color_yellow4,R.string.color_yellow5,R.string.color_yellow6,R.string.color_yellow7 },
-      {R.string.color_white1 ,R.string.color_white2 ,R.string.color_white3 ,R.string.color_white4 ,R.string.color_white5 ,R.string.color_white6 ,R.string.color_white7  },
-      {R.string.color_blue1  ,R.string.color_blue2  ,R.string.color_blue3  ,R.string.color_blue4  ,R.string.color_blue5  ,R.string.color_blue6  ,R.string.color_blue7   },
-      {R.string.color_green1 ,R.string.color_green2 ,R.string.color_green3 ,R.string.color_green4 ,R.string.color_green5 ,R.string.color_green6 ,R.string.color_green7  },
-      {R.string.color_red1   ,R.string.color_red2   ,R.string.color_red3   ,R.string.color_red4   ,R.string.color_red5   ,R.string.color_red6   ,R.string.color_red7    },
-      {R.string.color_orange1,R.string.color_orange2,R.string.color_orange3,R.string.color_orange4,R.string.color_orange5,R.string.color_orange6,R.string.color_orange7 },
-    };
-  private static final int[][] colorsTet =
-    {
-      {R.string.color_green1 ,R.string.color_green2 ,R.string.color_green3 ,R.string.color_green4 ,R.string.color_green5 ,R.string.color_green6 ,R.string.color_green7  },
-      {R.string.color_yellow1,R.string.color_yellow2,R.string.color_yellow3,R.string.color_yellow4,R.string.color_yellow5,R.string.color_yellow6,R.string.color_yellow7 },
-      {R.string.color_blue1  ,R.string.color_blue2  ,R.string.color_blue3  ,R.string.color_blue4  ,R.string.color_blue5  ,R.string.color_blue6  ,R.string.color_blue7   },
-      {R.string.color_red1   ,R.string.color_red2   ,R.string.color_red3   ,R.string.color_red4   ,R.string.color_red5   ,R.string.color_red6   ,R.string.color_red7    },
-    };
-  private static final int[][] colorsOct =
-    {
-      {R.string.color_violet1,R.string.color_violet2,R.string.color_violet3,R.string.color_violet4,R.string.color_violet5,R.string.color_violet6,R.string.color_violet7 },
-      {R.string.color_grey1  ,R.string.color_grey2  ,R.string.color_grey3  ,R.string.color_grey4  ,R.string.color_grey5  ,R.string.color_grey6  ,R.string.color_grey7   },
-      {R.string.color_blue1  ,R.string.color_blue2  ,R.string.color_blue3  ,R.string.color_blue4  ,R.string.color_blue5  ,R.string.color_blue6  ,R.string.color_blue7   },
-      {R.string.color_red1   ,R.string.color_red2   ,R.string.color_red3   ,R.string.color_red4   ,R.string.color_red5   ,R.string.color_red6   ,R.string.color_red7    },
-      {R.string.color_orange1,R.string.color_orange2,R.string.color_orange3,R.string.color_orange4,R.string.color_orange5,R.string.color_orange6,R.string.color_orange7 },
-      {R.string.color_green1 ,R.string.color_green2 ,R.string.color_green3 ,R.string.color_green4 ,R.string.color_green5 ,R.string.color_green6 ,R.string.color_green7  },
-      {R.string.color_white1 ,R.string.color_white2 ,R.string.color_white3 ,R.string.color_white4 ,R.string.color_white5 ,R.string.color_white6 ,R.string.color_white7  },
-      {R.string.color_yellow1,R.string.color_yellow2,R.string.color_yellow3,R.string.color_yellow4,R.string.color_yellow5,R.string.color_yellow6,R.string.color_yellow7 },
-    };
-  private static final int[][] colorsDi4 =
-    {
-      {R.string.color_yellow1,R.string.color_yellow2,R.string.color_yellow3,R.string.color_yellow4,R.string.color_yellow5,R.string.color_yellow6,R.string.color_yellow7 },
-      {R.string.color_white1 ,R.string.color_white2 ,R.string.color_white3 ,R.string.color_white4 ,R.string.color_white5 ,R.string.color_white6 ,R.string.color_white7  },
-      {R.string.color_blue1  ,R.string.color_blue2  ,R.string.color_blue3  ,R.string.color_blue4  ,R.string.color_blue5  ,R.string.color_blue6  ,R.string.color_blue7   },
-      {R.string.color_red1   ,R.string.color_red2   ,R.string.color_red3   ,R.string.color_red4   ,R.string.color_red5   ,R.string.color_red6   ,R.string.color_red7    },
-    };
-
   private static Bitmap[] mBitmap;
   private ImageButton[] mColorButton;
   private TransparentImageButton mResetButton,mBackButton, mSolveButton;
@@ -99,6 +66,8 @@ public class ScreenSetupPosition extends ScreenAbstract implements ResultScreen
   private WeakReference<SolverActivity> mWeakAct;
   private int mObjectOrdinal;
   private String[] mPhaseNames;
+  private Timer mTimer;
+  private SolverAbstract mSolver;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -320,7 +289,7 @@ public class ScreenSetupPosition extends ScreenAbstract implements ResultScreen
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private boolean pressSolve(SolverActivity act)
+  private void pressSolve(SolverActivity act)
     {
     int[] solverOrdinals = SolvingList.getSolverOrdinals(mObjectOrdinal);
 
@@ -339,8 +308,8 @@ public class ScreenSetupPosition extends ScreenAbstract implements ResultScreen
           {
           if( solverOrdinals.length==1 ) // just one solver - simply launch it
             {
+            mSolver = solver;
             solver.solve(this,result);
-            return true;
             }
           else // more than one solver - pop up a choosing dialog
             {
@@ -348,29 +317,16 @@ public class ScreenSetupPosition extends ScreenAbstract implements ResultScreen
             String upperName = objList.name();
             Bundle bundle = new Bundle();
             bundle.putString("argument", upperName );
-            DialogSolvers solv = new DialogSolvers();
-            solv.setArguments(bundle);
-            solv.show( act.getSupportFragmentManager(), DialogSolvers.getDialogTag());
-            return false;
+            DialogSolvers dialog = new DialogSolvers();
+            dialog.setArguments(bundle);
+            dialog.show( act.getSupportFragmentManager(), DialogSolvers.getDialogTag());
             }
           }
-        else
-          {
-          displayImpossibleDialog(result,solver.getFaceColors());
-          return false;
-          }
-        }
-      else
-        {
-        displayErrorDialog(act.getString(R.string.solver_generic_not_implemented));
-        return false;
+        else displayImpossibleDialog(result,solver.getFaceColors());
         }
+      else displayErrorDialog(act.getString(R.string.solver_generic_not_implemented));
       }
-    else  // no solvers? Impossible!
-      {
-      displayErrorDialog("No solvers found for object "+mObjectOrdinal);
-      return false;
-      }
+    else displayErrorDialog("No solvers found for object "+mObjectOrdinal);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -378,20 +334,82 @@ public class ScreenSetupPosition extends ScreenAbstract implements ResultScreen
   public void fail(String result) {}
   public void setPhaseNames(String[] names) { mPhaseNames = names; }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void setSolver(SolverAbstract solver )
+    {
+    mSolver = solver;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public void startedSolving()
     {
+    if( !mSolving )
+      {
+      mSolving = true;
+      mTimer = new Timer();
 
+      mTimer.schedule(new TimerTask()
+        {
+        @Override
+        public void run()
+          {
+          if( mSolving ) showInterruptDialog();
+          }
+        }, 2000);
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public void stoppedSolving()
     {
+    mSolving = false;
+
+    if( mTimer!=null )
+      {
+      mTimer.cancel();
+      mTimer = null;
+      }
 
+    final SolverActivity act = mWeakAct.get();
+    FragmentManager mana = act.getSupportFragmentManager();
+    DialogInterrupt diag = (DialogInterrupt) mana.findFragmentByTag(DialogInterrupt.getDialogTag());
+
+    if( diag!=null ) diag.dismiss();
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void interrupt()
+    {
+    final SolverActivity act = mWeakAct.get();
+    FragmentManager mana = act.getSupportFragmentManager();
+    DialogInterrupt diag = (DialogInterrupt) mana.findFragmentByTag(DialogInterrupt.getDialogTag());
+
+    if( diag!=null ) diag.dismiss();
+
+    if( mSolver!=null ) mSolver.interrupt();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private void showInterruptDialog()
+      {
+      final SolverActivity act = mWeakAct.get();
+
+      act.runOnUiThread(new Runnable()
+        {
+        @Override
+        public void run()
+          {
+          DialogInterrupt dialog = new DialogInterrupt();
+          dialog.show(act.getSupportFragmentManager(), null );
+          }
+        });
+      }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private void setupColorButtons(final SolverActivity act)
@@ -450,7 +468,7 @@ public class ScreenSetupPosition extends ScreenAbstract implements ResultScreen
       @Override
       public void onClick(View v)
         {
-        if( !mSolving && pressSolve(act) ) mSolving = true;
+        if( !mSolving ) pressSolve(act);
         }
       });
     }
@@ -515,34 +533,10 @@ public class ScreenSetupPosition extends ScreenAbstract implements ResultScreen
     return mCurrentColor;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void setSolved(final String moves)
-    {
-    mSolving = false;
-    final SolverActivity act = mWeakAct.get();
-
-    if( act!=null )
-      {
-      act.runOnUiThread(new Runnable()
-        {
-        @Override
-        public void run()
-          {
-          ScreenList.switchScreen(act, ScreenList.SOLU);
-          ScreenSolutionSinglephased solution = (ScreenSolutionSinglephased) ScreenList.SOLU.getScreenClass();
-          solution.setSolution(moves);
-          if( !moves.isEmpty() ) act.doNotShowDialogAnymore();
-          }
-        });
-      }
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public void setSolved(final int[][] moves, final int phaseNumber, final int[][] subphases)
     {
-    mSolving = false;
     final SolverActivity act = mWeakAct.get();
 
     if( act!=null )
@@ -579,7 +573,6 @@ public class ScreenSetupPosition extends ScreenAbstract implements ResultScreen
 
   public void displayErrorDialog(String message)
     {
-    mSolving = false;
     SolverActivity act = mWeakAct.get();
 
     if( act!=null )
@@ -596,7 +589,6 @@ public class ScreenSetupPosition extends ScreenAbstract implements ResultScreen
 
   public void displayImpossibleDialog(String message)
     {
-    mSolving = false;
     SolverActivity act = mWeakAct.get();
 
     if( act!=null )
@@ -613,233 +605,12 @@ public class ScreenSetupPosition extends ScreenAbstract implements ResultScreen
 
   public void displayImpossibleDialog(int[] errorCode, int[] faceColors)
     {
-    mSolving = false;
     SolverActivity act = mWeakAct.get();
 
     if( act!=null )
       {
-      String message = error(act.getResources(),errorCode,faceColors);
+      String message = SolverErrors.error(act.getResources(),errorCode,faceColors);
       displayImpossibleDialog(message);
       }
     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  int getHexColor(int color,int variant) { return colorsHex[color][variant]; }
-  int getTetColor(int color,int variant) { return colorsTet[color][variant]; }
-  int getOctColor(int color,int variant) { return colorsOct[color][variant]; }
-  int getDi4Color(int color,int variant) { return colorsDi4[color][variant]; }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  String hexCornerMissingError(Resources res, int face0, int face1, int face2)
-    {
-    int j0 = getHexColor(face0,3);
-    int j1 = getHexColor(face1,3);
-    int j2 = getHexColor(face2,4);
-
-    String c0 = res.getString(j0);
-    String c1 = res.getString(j1);
-    String c2 = res.getString(j2);
-
-    return res.getString(R.string.solver_generic_missing_corner,c0,c1,c2);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  String hexCenterMissingError(Resources res, int face)
-    {
-    int color = getHexColor(face,2);
-    String clr= res.getString(color);
-    return res.getString(R.string.solver_generic_missing_center,clr);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  String hexEdgeMissingError(Resources res, int face0, int face1)
-    {
-    int j0 = getHexColor(face0,3);
-    int j1 = getHexColor(face1,6);
-
-    String c0 = res.getString(j0);
-    String c1 = res.getString(j1);
-
-    return res.getString(R.string.solver_generic_missing_edge,c0,c1);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  String hexEdgeTwistedError(Resources res, int color0, int color1)
-    {
-    int j0 = getHexColor(color0,3);
-    int j1 = getHexColor(color1,6);
-
-    String c0 = res.getString(j0);
-    String c1 = res.getString(j1);
-
-    return res.getString(R.string.solver_generic_twisted_edge,c0,c1);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  String hexCornerTwistedError(Resources res, int color0, int color1, int color2)
-    {
-    int j0 = getHexColor(color0,3);
-    int j1 = getHexColor(color1,3);
-    int j2 = getHexColor(color2,5);
-
-    String c0 = res.getString(j0);
-    String c1 = res.getString(j1);
-    String c2 = res.getString(j2);
-
-    return res.getString(R.string.solver_generic_twisted_corner,c0,c1,c2);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  String hexEdgeMonoError(Resources res, int color)
-    {
-    int j0 = getHexColor(color,3);
-    int j1 = getHexColor(color,6);
-    String c0 = res.getString(j0);
-    String c1 = res.getString(j1);
-
-    return res.getString(R.string.solver_generic_edge_mono,c0,c1);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  String hexEdgeTwiceError(Resources res, int color0, int color1)
-    {
-    int j0 = getHexColor(color0,3);
-    int j1 = getHexColor(color1,6);
-    String c0 = res.getString(j0);
-    String c1 = res.getString(j1);
-
-    return res.getString(R.string.solver_generic_edge_twice,c0,c1);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  String octCenterMissingError(Resources res, int face)
-    {
-    int index = getOctColor(face,2);
-    String color = res.getString(index);
-    return res.getString(R.string.solver_generic_missing_center,color);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  String octCornerMissingError(Resources res, int f1, int f2)
-    {
-    int i1 = getOctColor(f1,3);
-    int i2 = getOctColor(f2,4);
-    String c1 = res.getString(i1);
-    String c2 = res.getString(i2);
-    return res.getString(R.string.solver_generic_missing_corner2,c1,c2);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  String tetCornerMissingError(Resources res, int color0, int color1, int color2)
-    {
-    int j0 = getTetColor(color0,3);
-    int j1 = getTetColor(color1,3);
-    int j2 = getTetColor(color2,4);
-
-    String c0 = res.getString(j0);
-    String c1 = res.getString(j1);
-    String c2 = res.getString(j2);
-
-    return res.getString(R.string.solver_generic_missing_corner,c0,c1,c2);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  String tetEdgeMissingError(Resources res, int face0, int face1)
-    {
-    int j0 = getTetColor(face0,3);
-    int j1 = getTetColor(face1,6);
-
-    String c0 = res.getString(j0);
-    String c1 = res.getString(j1);
-
-    return res.getString(R.string.solver_generic_missing_edge,c0,c1);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  String tetCenterMissingError(Resources res, int face)
-    {
-    int j = getTetColor(face,2);
-    String c = res.getString(j);
-    return res.getString(R.string.solver_generic_missing_center,c);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  String tetVertexMissingError(Resources res, int color0, int color1, int color2)
-    {
-    int j0 = getTetColor(color0,3);
-    int j1 = getTetColor(color1,3);
-    int j2 = getTetColor(color2,4);
-
-    String c0 = res.getString(j0);
-    String c1 = res.getString(j1);
-    String c2 = res.getString(j2);
-
-    return res.getString(R.string.solver_generic_missing_vertex,c0,c1,c2);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  String di4EdgeThreeError(Resources res, int color)
-    {
-    int j0 = getDi4Color(color,7);
-    String c0 = res.getString(j0);
-    return res.getString(R.string.solver_generic_edge_three,c0);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public String error(Resources res, int[] err, int[] faceColors)
-    {
-    switch(err[0])
-      {
-      case ERROR_HEX_CORNER_MISSING    : return hexCornerMissingError(res,err[1],err[2],err[3]);
-      case ERROR_HEX_CENTER_MISSING    : return hexCenterMissingError(res,err[1]);
-      case ERROR_HEX_EDGE_MISSING      : return hexEdgeMissingError(res,err[1],err[2]);
-      case ERROR_HEX_EDGE_TWISTED      : return hexEdgeTwistedError(res,err[1],err[2]);
-      case ERROR_HEX_EDGE_MONOCHROMATIC: return hexEdgeMonoError(res,err[1]);
-      case ERROR_HEX_EDGE_TWICE        : return hexEdgeTwiceError(res,err[1],err[2]);
-      case ERROR_HEX_CORNER_TWISTED    : return hexCornerTwistedError(res,err[1],err[2],err[3]);
-
-      case ERROR_TET_CORNER_MISSING    : return tetCornerMissingError(res,err[1],err[2],err[3]);
-      case ERROR_TET_VERTEX_MISSING    : return tetVertexMissingError(res,err[1],err[2],err[3]);
-      case ERROR_TET_EDGE_MISSING      : return tetEdgeMissingError(res,faceColors[err[1]],faceColors[err[2]]);
-      case ERROR_TET_CENTER_MISSING    : return tetCenterMissingError(res,err[1]);
-
-      case ERROR_OCT_CENTER_MISSING    : return octCenterMissingError(res,err[1]);
-      case ERROR_OCT_CORNER_MISSING    : return octCornerMissingError(res,err[1],err[2]);
-
-      case ERROR_DI4_EDGE_THREE        : return di4EdgeThreeError(res,err[1]);
-
-      case ERROR_CORNERS_CANNOT        : return res.getString(R.string.solver_generic_corners_cannot);
-      case ERROR_EDGE_CANNOT           : return res.getString(R.string.solver_generic_edges_cannot);
-      case ERROR_CORNER_TWISTED        : return res.getString(R.string.solver_generic_corner_twist);
-      case ERROR_CORNER_TWIST_90       : return res.getString(R.string.solver_generic_corner_twist) + " (90)";
-      case ERROR_CORNER_TWIST_180      : return res.getString(R.string.solver_generic_corner_twist) + " (180)";
-      case ERROR_EDGE_TWISTED          : return res.getString(R.string.solver_generic_edge_twist);
-      case ERROR_TWO_CENTERS           : return res.getString(R.string.solver_generic_two_centers);
-      case ERROR_TWO_CORNERS           : return res.getString(R.string.solver_generic_two_corners);
-      case ERROR_TWO_EDGES             : return res.getString(R.string.solver_generic_two_edges);
-      case ERROR_FREE_CORNERS_NOT_EVEN : return res.getString(R.string.solver_generic_free_corners_odd);
-      case ERROR_FREE_CORNERS_ROTATED  : return res.getString(R.string.solver_generic_free_corners_rotated);
-      case ERROR_VERTICES_CANNOT       : return res.getString(R.string.solver_generic_vertices_cannot);
-      case ERROR_C_V_DONT_MATCH        : return res.getString(R.string.solver_generic_c_v_dont_match);
-      case ERROR_TWO_CORNERS_TWO_EDGES : return res.getString(R.string.solver_two_corners_two_edges);
-      }
-
-    return null;
-    }
   }
diff --git a/src/main/java/org/distorted/solvers/ScreenSolutionSinglephased.java b/src/main/java/org/distorted/solvers/ScreenSolutionSinglephased.java
index a2f700fd..16fd1a03 100644
--- a/src/main/java/org/distorted/solvers/ScreenSolutionSinglephased.java
+++ b/src/main/java/org/distorted/solvers/ScreenSolutionSinglephased.java
@@ -21,7 +21,6 @@ import org.distorted.helpers.TransparentImageButton;
 import org.distorted.main.R;
 import org.distorted.objectlib.helpers.MovesFinished;
 import org.distorted.objectlib.main.ObjectControl;
-import org.distorted.objectlib.patterns.RubikPattern;
 
 import java.lang.ref.WeakReference;
 
@@ -248,21 +247,6 @@ public class ScreenSolutionSinglephased extends ScreenAbstract implements MovesF
       }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void setSolution(String moves)
-    {
-    mCanRotate= true;
-    mCurrMove = 0;
-    mNumMoves = moves.length()/4;
-    mMoves    = new int[mNumMoves][3];
-
-    RubikPattern.parseMoves(mMoves,mNumMoves,moves);
-
-    SolverActivity act = mAct.get();
-    mMovesText.setText(act.getString(R.string.mo_placeholder,mCurrMove,mNumMoves));
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   void setSolution(int[][] moves)
diff --git a/src/main/java/org/distorted/solvers/SolverActivity.java b/src/main/java/org/distorted/solvers/SolverActivity.java
index ec869d00..568d12b5 100644
--- a/src/main/java/org/distorted/solvers/SolverActivity.java
+++ b/src/main/java/org/distorted/solvers/SolverActivity.java
@@ -26,6 +26,7 @@ import org.distorted.objectlib.helpers.OperatingSystemInterface;
 import org.distorted.objectlib.main.InitAssets;
 import org.distorted.objectlib.main.ObjectControl;
 import org.distorted.objectlib.main.TwistyObject;
+import org.distorted.objectlib.solvers.verifiers.SolverAbstract;
 import org.distorted.objects.RubikObject;
 import org.distorted.objects.RubikObjectList;
 import org.distorted.os.OSInterface;
@@ -207,6 +208,22 @@ public class SolverActivity extends BaseActivity
       return view.getObjectControl();
       }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void interruptSolve()
+      {
+      ScreenSetupPosition screen = (ScreenSetupPosition) ScreenList.SVER.getScreenClass();
+      screen.interrupt();
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void setSolver(SolverAbstract solver)
+      {
+      ScreenSetupPosition screen = (ScreenSetupPosition) ScreenList.SVER.getScreenClass();
+      screen.setSolver(solver);
+      }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
     public int getObjectOrdinal()
diff --git a/src/main/java/org/distorted/solvers/SolverErrors.java b/src/main/java/org/distorted/solvers/SolverErrors.java
new file mode 100644
index 00000000..8c87c68a
--- /dev/null
+++ b/src/main/java/org/distorted/solvers/SolverErrors.java
@@ -0,0 +1,276 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2024 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.solvers;
+
+import static org.distorted.objectlib.solvers.verifiers.SolverAbstract.*;
+
+import android.content.res.Resources;
+
+import org.distorted.main.R;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class SolverErrors
+{
+  private static final int[][] colorsHex =
+          {
+                  {R.string.color_yellow1,R.string.color_yellow2,R.string.color_yellow3,R.string.color_yellow4,R.string.color_yellow5,R.string.color_yellow6,R.string.color_yellow7 },
+                  {R.string.color_white1 ,R.string.color_white2 ,R.string.color_white3 ,R.string.color_white4 ,R.string.color_white5 ,R.string.color_white6 ,R.string.color_white7  },
+                  {R.string.color_blue1  ,R.string.color_blue2  ,R.string.color_blue3  ,R.string.color_blue4  ,R.string.color_blue5  ,R.string.color_blue6  ,R.string.color_blue7   },
+                  {R.string.color_green1 ,R.string.color_green2 ,R.string.color_green3 ,R.string.color_green4 ,R.string.color_green5 ,R.string.color_green6 ,R.string.color_green7  },
+                  {R.string.color_red1   ,R.string.color_red2   ,R.string.color_red3   ,R.string.color_red4   ,R.string.color_red5   ,R.string.color_red6   ,R.string.color_red7    },
+                  {R.string.color_orange1,R.string.color_orange2,R.string.color_orange3,R.string.color_orange4,R.string.color_orange5,R.string.color_orange6,R.string.color_orange7 },
+          };
+  private static final int[][] colorsTet =
+          {
+                  {R.string.color_green1 ,R.string.color_green2 ,R.string.color_green3 ,R.string.color_green4 ,R.string.color_green5 ,R.string.color_green6 ,R.string.color_green7  },
+                  {R.string.color_yellow1,R.string.color_yellow2,R.string.color_yellow3,R.string.color_yellow4,R.string.color_yellow5,R.string.color_yellow6,R.string.color_yellow7 },
+                  {R.string.color_blue1  ,R.string.color_blue2  ,R.string.color_blue3  ,R.string.color_blue4  ,R.string.color_blue5  ,R.string.color_blue6  ,R.string.color_blue7   },
+                  {R.string.color_red1   ,R.string.color_red2   ,R.string.color_red3   ,R.string.color_red4   ,R.string.color_red5   ,R.string.color_red6   ,R.string.color_red7    },
+          };
+  private static final int[][] colorsOct =
+          {
+                  {R.string.color_violet1,R.string.color_violet2,R.string.color_violet3,R.string.color_violet4,R.string.color_violet5,R.string.color_violet6,R.string.color_violet7 },
+                  {R.string.color_grey1  ,R.string.color_grey2  ,R.string.color_grey3  ,R.string.color_grey4  ,R.string.color_grey5  ,R.string.color_grey6  ,R.string.color_grey7   },
+                  {R.string.color_blue1  ,R.string.color_blue2  ,R.string.color_blue3  ,R.string.color_blue4  ,R.string.color_blue5  ,R.string.color_blue6  ,R.string.color_blue7   },
+                  {R.string.color_red1   ,R.string.color_red2   ,R.string.color_red3   ,R.string.color_red4   ,R.string.color_red5   ,R.string.color_red6   ,R.string.color_red7    },
+                  {R.string.color_orange1,R.string.color_orange2,R.string.color_orange3,R.string.color_orange4,R.string.color_orange5,R.string.color_orange6,R.string.color_orange7 },
+                  {R.string.color_green1 ,R.string.color_green2 ,R.string.color_green3 ,R.string.color_green4 ,R.string.color_green5 ,R.string.color_green6 ,R.string.color_green7  },
+                  {R.string.color_white1 ,R.string.color_white2 ,R.string.color_white3 ,R.string.color_white4 ,R.string.color_white5 ,R.string.color_white6 ,R.string.color_white7  },
+                  {R.string.color_yellow1,R.string.color_yellow2,R.string.color_yellow3,R.string.color_yellow4,R.string.color_yellow5,R.string.color_yellow6,R.string.color_yellow7 },
+          };
+  private static final int[][] colorsDi4 =
+          {
+                  {R.string.color_yellow1,R.string.color_yellow2,R.string.color_yellow3,R.string.color_yellow4,R.string.color_yellow5,R.string.color_yellow6,R.string.color_yellow7 },
+                  {R.string.color_white1 ,R.string.color_white2 ,R.string.color_white3 ,R.string.color_white4 ,R.string.color_white5 ,R.string.color_white6 ,R.string.color_white7  },
+                  {R.string.color_blue1  ,R.string.color_blue2  ,R.string.color_blue3  ,R.string.color_blue4  ,R.string.color_blue5  ,R.string.color_blue6  ,R.string.color_blue7   },
+                  {R.string.color_red1   ,R.string.color_red2   ,R.string.color_red3   ,R.string.color_red4   ,R.string.color_red5   ,R.string.color_red6   ,R.string.color_red7    },
+          };
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static int getHexColor(int color,int variant) { return colorsHex[color][variant]; }
+  private static int getTetColor(int color,int variant) { return colorsTet[color][variant]; }
+  private static int getOctColor(int color,int variant) { return colorsOct[color][variant]; }
+  private static int getDi4Color(int color,int variant) { return colorsDi4[color][variant]; }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static String hexCornerMissingError(Resources res, int face0, int face1, int face2)
+    {
+    int j0 = getHexColor(face0,3);
+    int j1 = getHexColor(face1,3);
+    int j2 = getHexColor(face2,4);
+
+    String c0 = res.getString(j0);
+    String c1 = res.getString(j1);
+    String c2 = res.getString(j2);
+
+    return res.getString(R.string.solver_generic_missing_corner,c0,c1,c2);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static String hexCenterMissingError(Resources res, int face)
+    {
+    int color = getHexColor(face,2);
+    String clr= res.getString(color);
+    return res.getString(R.string.solver_generic_missing_center,clr);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static String hexEdgeMissingError(Resources res, int face0, int face1)
+    {
+    int j0 = getHexColor(face0,3);
+    int j1 = getHexColor(face1,6);
+
+    String c0 = res.getString(j0);
+    String c1 = res.getString(j1);
+
+    return res.getString(R.string.solver_generic_missing_edge,c0,c1);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static String hexEdgeTwistedError(Resources res, int color0, int color1)
+    {
+    int j0 = getHexColor(color0,3);
+    int j1 = getHexColor(color1,6);
+
+    String c0 = res.getString(j0);
+    String c1 = res.getString(j1);
+
+    return res.getString(R.string.solver_generic_twisted_edge,c0,c1);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static String hexCornerTwistedError(Resources res, int color0, int color1, int color2)
+    {
+    int j0 = getHexColor(color0,3);
+    int j1 = getHexColor(color1,3);
+    int j2 = getHexColor(color2,5);
+
+    String c0 = res.getString(j0);
+    String c1 = res.getString(j1);
+    String c2 = res.getString(j2);
+
+    return res.getString(R.string.solver_generic_twisted_corner,c0,c1,c2);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static String hexEdgeMonoError(Resources res, int color)
+    {
+    int j0 = getHexColor(color,3);
+    int j1 = getHexColor(color,6);
+    String c0 = res.getString(j0);
+    String c1 = res.getString(j1);
+
+    return res.getString(R.string.solver_generic_edge_mono,c0,c1);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static String hexEdgeTwiceError(Resources res, int color0, int color1)
+    {
+    int j0 = getHexColor(color0,3);
+    int j1 = getHexColor(color1,6);
+    String c0 = res.getString(j0);
+    String c1 = res.getString(j1);
+
+    return res.getString(R.string.solver_generic_edge_twice,c0,c1);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static String octCenterMissingError(Resources res, int face)
+    {
+    int index = getOctColor(face,2);
+    String color = res.getString(index);
+    return res.getString(R.string.solver_generic_missing_center,color);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static String octCornerMissingError(Resources res, int f1, int f2)
+    {
+    int i1 = getOctColor(f1,3);
+    int i2 = getOctColor(f2,4);
+    String c1 = res.getString(i1);
+    String c2 = res.getString(i2);
+    return res.getString(R.string.solver_generic_missing_corner2,c1,c2);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static String tetCornerMissingError(Resources res, int color0, int color1, int color2)
+    {
+    int j0 = getTetColor(color0,3);
+    int j1 = getTetColor(color1,3);
+    int j2 = getTetColor(color2,4);
+
+    String c0 = res.getString(j0);
+    String c1 = res.getString(j1);
+    String c2 = res.getString(j2);
+
+    return res.getString(R.string.solver_generic_missing_corner,c0,c1,c2);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static String tetEdgeMissingError(Resources res, int face0, int face1)
+    {
+    int j0 = getTetColor(face0,3);
+    int j1 = getTetColor(face1,6);
+
+    String c0 = res.getString(j0);
+    String c1 = res.getString(j1);
+
+    return res.getString(R.string.solver_generic_missing_edge,c0,c1);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static String tetCenterMissingError(Resources res, int face)
+    {
+    int j = getTetColor(face,2);
+    String c = res.getString(j);
+    return res.getString(R.string.solver_generic_missing_center,c);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static String tetVertexMissingError(Resources res, int color0, int color1, int color2)
+    {
+    int j0 = getTetColor(color0,3);
+    int j1 = getTetColor(color1,3);
+    int j2 = getTetColor(color2,4);
+
+    String c0 = res.getString(j0);
+    String c1 = res.getString(j1);
+    String c2 = res.getString(j2);
+
+    return res.getString(R.string.solver_generic_missing_vertex,c0,c1,c2);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static String di4EdgeThreeError(Resources res, int color)
+    {
+    int j0 = getDi4Color(color,7);
+    String c0 = res.getString(j0);
+    return res.getString(R.string.solver_generic_edge_three,c0);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static String error(Resources res, int[] err, int[] faceColors)
+    {
+    switch(err[0])
+      {
+      case ERROR_HEX_CORNER_MISSING    : return hexCornerMissingError(res,err[1],err[2],err[3]);
+      case ERROR_HEX_CENTER_MISSING    : return hexCenterMissingError(res,err[1]);
+      case ERROR_HEX_EDGE_MISSING      : return hexEdgeMissingError(res,err[1],err[2]);
+      case ERROR_HEX_EDGE_TWISTED      : return hexEdgeTwistedError(res,err[1],err[2]);
+      case ERROR_HEX_EDGE_MONOCHROMATIC: return hexEdgeMonoError(res,err[1]);
+      case ERROR_HEX_EDGE_TWICE        : return hexEdgeTwiceError(res,err[1],err[2]);
+      case ERROR_HEX_CORNER_TWISTED    : return hexCornerTwistedError(res,err[1],err[2],err[3]);
+
+      case ERROR_TET_CORNER_MISSING    : return tetCornerMissingError(res,err[1],err[2],err[3]);
+      case ERROR_TET_VERTEX_MISSING    : return tetVertexMissingError(res,err[1],err[2],err[3]);
+      case ERROR_TET_EDGE_MISSING      : return tetEdgeMissingError(res,faceColors[err[1]],faceColors[err[2]]);
+      case ERROR_TET_CENTER_MISSING    : return tetCenterMissingError(res,err[1]);
+
+      case ERROR_OCT_CENTER_MISSING    : return octCenterMissingError(res,err[1]);
+      case ERROR_OCT_CORNER_MISSING    : return octCornerMissingError(res,err[1],err[2]);
+
+      case ERROR_DI4_EDGE_THREE        : return di4EdgeThreeError(res,err[1]);
+
+      case ERROR_CORNERS_CANNOT        : return res.getString(R.string.solver_generic_corners_cannot);
+      case ERROR_EDGE_CANNOT           : return res.getString(R.string.solver_generic_edges_cannot);
+      case ERROR_CORNER_TWISTED        : return res.getString(R.string.solver_generic_corner_twist);
+      case ERROR_CORNER_TWIST_90       : return res.getString(R.string.solver_generic_corner_twist) + " (90)";
+      case ERROR_CORNER_TWIST_180      : return res.getString(R.string.solver_generic_corner_twist) + " (180)";
+      case ERROR_EDGE_TWISTED          : return res.getString(R.string.solver_generic_edge_twist);
+      case ERROR_TWO_CENTERS           : return res.getString(R.string.solver_generic_two_centers);
+      case ERROR_TWO_CORNERS           : return res.getString(R.string.solver_generic_two_corners);
+      case ERROR_TWO_EDGES             : return res.getString(R.string.solver_generic_two_edges);
+      case ERROR_FREE_CORNERS_NOT_EVEN : return res.getString(R.string.solver_generic_free_corners_odd);
+      case ERROR_FREE_CORNERS_ROTATED  : return res.getString(R.string.solver_generic_free_corners_rotated);
+      case ERROR_VERTICES_CANNOT       : return res.getString(R.string.solver_generic_vertices_cannot);
+      case ERROR_C_V_DONT_MATCH        : return res.getString(R.string.solver_generic_c_v_dont_match);
+      case ERROR_TWO_CORNERS_TWO_EDGES : return res.getString(R.string.solver_two_corners_two_edges);
+      }
+
+    return null;
+    }
+}
