commit cc88f2fa1471244e15e5c40d02f3a0b51c8c6f48
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Tue Jun 29 17:03:26 2021 +0200

    Give visual indication when dragging is locked.

diff --git a/src/main/java/org/distorted/helpers/MovesAndLockController.java b/src/main/java/org/distorted/helpers/MovesAndLockController.java
index bf0950ac..eec13831 100644
--- a/src/main/java/org/distorted/helpers/MovesAndLockController.java
+++ b/src/main/java/org/distorted/helpers/MovesAndLockController.java
@@ -27,12 +27,15 @@ import org.distorted.main.R;
 import org.distorted.main.RubikActivity;
 
 import java.util.ArrayList;
+import java.util.Timer;
+import java.util.TimerTask;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 public class MovesAndLockController implements MovesFinished
   {
   private static final int MILLIS_PER_DEGREE = 6;
+  private static final int LOCK_TIME = 300;
 
   private static class Move
     {
@@ -50,12 +53,17 @@ public class MovesAndLockController implements MovesFinished
   private boolean mCanPrevMove;
   private TwistyPreRender mPre;
   private ImageButton mPrevButton, mLockButton;
+  private long mLockTime;
+  private Timer mTimer;
+  private boolean mTimerRunning;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
 
   public MovesAndLockController()
     {
+    mTimerRunning = false;
+    mLockTime = 0;
     mCanPrevMove = true;
 
     if( mMoves==null ) mMoves = new ArrayList<>();
@@ -67,19 +75,29 @@ public class MovesAndLockController implements MovesFinished
   public void toggleLock(TwistyActivity act)
     {
     act.toggleLock();
-    mLockButton.setImageResource(getLockIcon(act));
+    mLockButton.setImageResource(getLockIcon(act,false));
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public int getLockIcon(TwistyActivity act)
+  public int getLockIcon(TwistyActivity act, boolean red)
     {
     if( act.retLocked() )
       {
-      return RubikActivity.getDrawable(R.drawable.ui_small_locked,
-                                       R.drawable.ui_medium_locked,
-                                       R.drawable.ui_big_locked,
-                                       R.drawable.ui_huge_locked);
+      if( red )
+        {
+        return RubikActivity.getDrawable(R.drawable.ui_small_locked_red,
+                                         R.drawable.ui_medium_locked_red,
+                                         R.drawable.ui_big_locked_red,
+                                         R.drawable.ui_huge_locked_red);
+        }
+      else
+        {
+        return RubikActivity.getDrawable(R.drawable.ui_small_locked,
+                                         R.drawable.ui_medium_locked,
+                                         R.drawable.ui_big_locked,
+                                         R.drawable.ui_huge_locked);
+        }
       }
     else
       {
@@ -158,6 +176,58 @@ public class MovesAndLockController implements MovesFinished
       });
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void changeLockIcon(TwistyActivity act, final boolean red)
+    {
+    act.runOnUiThread(new Runnable()
+      {
+      @Override
+      public void run()
+        {
+        if( mLockButton!=null )
+          mLockButton.setImageResource(getLockIcon(act,red));
+        }
+      });
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void reddenLock(final TwistyActivity act)
+    {
+    mLockTime = System.currentTimeMillis();
+
+    if( !mTimerRunning )
+      {
+      changeLockIcon(act,true);
+
+      mTimerRunning = true;
+      mTimer = new Timer();
+
+      mTimer.scheduleAtFixedRate(new TimerTask()
+        {
+        @Override
+        public void run()
+          {
+          act.runOnUiThread(new Runnable()
+            {
+            @Override
+            public void run()
+              {
+              if( System.currentTimeMillis()-mLockTime > LOCK_TIME )
+                {
+                changeLockIcon(act,false);
+                mTimer.cancel();
+                mTimer = null;
+                mTimerRunning = false;
+                }
+              }
+            });
+          }
+        }, 0, LOCK_TIME);
+      }
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public void addMove(TwistyActivity act, int axis, int row, int angle)
@@ -202,7 +272,7 @@ public class MovesAndLockController implements MovesFinished
 
   public void setupLockButton(final TwistyActivity act, final float width)
     {
-    final int icon = getLockIcon(act);
+    final int icon = getLockIcon(act,false);
     mLockButton = new TransparentImageButton(act, icon, width,LinearLayout.LayoutParams.MATCH_PARENT);
 
     mLockButton.setOnClickListener( new View.OnClickListener()
@@ -225,7 +295,7 @@ public class MovesAndLockController implements MovesFinished
       public void run()
         {
         if( mLockButton!=null )
-          mLockButton.setImageResource(getLockIcon(act));
+          mLockButton.setImageResource(getLockIcon(act,false));
         }
       });
     }
diff --git a/src/main/java/org/distorted/main/RubikSurfaceView.java b/src/main/java/org/distorted/main/RubikSurfaceView.java
index 5fc9ebeb..c565fda6 100644
--- a/src/main/java/org/distorted/main/RubikSurfaceView.java
+++ b/src/main/java/org/distorted/main/RubikSurfaceView.java
@@ -342,10 +342,16 @@ public class RubikSurfaceView extends GLSurfaceView
         else
           {
           final RubikActivity act = (RubikActivity)getContext();
-
-          mDragging           = (!act.isLocked() || mIsAutomatic);
+          final boolean locked= act.isLocked();
+          mDragging           = (!locked || mIsAutomatic);
           mBeginningRotation  = false;
           mContinuingRotation = false;
+
+          if( locked )
+            {
+            RubikScreenPlay play = (RubikScreenPlay) ScreenList.PLAY.getScreenClass();
+            play.reddenLock(act);
+            }
           }
         }
       }
diff --git a/src/main/java/org/distorted/screens/RubikScreenBase.java b/src/main/java/org/distorted/screens/RubikScreenBase.java
index 83aaa692..be4ab6d7 100644
--- a/src/main/java/org/distorted/screens/RubikScreenBase.java
+++ b/src/main/java/org/distorted/screens/RubikScreenBase.java
@@ -81,4 +81,11 @@ abstract class RubikScreenBase extends RubikScreenAbstract
     {
     mController.addMove(act,axis,row,angle);
     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void reddenLock(final TwistyActivity act)
+    {
+    mController.reddenLock(act);
+    }
   }
diff --git a/src/main/java/org/distorted/screens/RubikScreenSolving.java b/src/main/java/org/distorted/screens/RubikScreenSolving.java
index b24457f0..8f665113 100644
--- a/src/main/java/org/distorted/screens/RubikScreenSolving.java
+++ b/src/main/java/org/distorted/screens/RubikScreenSolving.java
@@ -44,7 +44,7 @@ public class RubikScreenSolving extends RubikScreenBase
   private Timer mTimer;
   private long mStartTime;
   private boolean mRunning;
-  private RubikScores mScores;
+  private final RubikScores mScores;
   private long mElapsed;
   private ImageButton mBackButton;
 
diff --git a/src/main/java/org/distorted/tutorials/TutorialState.java b/src/main/java/org/distorted/tutorials/TutorialState.java
index 00a25b4a..63fdf373 100644
--- a/src/main/java/org/distorted/tutorials/TutorialState.java
+++ b/src/main/java/org/distorted/tutorials/TutorialState.java
@@ -131,4 +131,11 @@ public class TutorialState
     {
     mController = new MovesAndLockController();
     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void reddenLock(final TwistyActivity act)
+    {
+    mController.reddenLock(act);
+    }
 }
diff --git a/src/main/java/org/distorted/tutorials/TutorialSurfaceView.java b/src/main/java/org/distorted/tutorials/TutorialSurfaceView.java
index 22e0dd71..1df199ac 100644
--- a/src/main/java/org/distorted/tutorials/TutorialSurfaceView.java
+++ b/src/main/java/org/distorted/tutorials/TutorialSurfaceView.java
@@ -298,9 +298,16 @@ public class TutorialSurfaceView extends GLSurfaceView
         else
           {
           final TutorialActivity act = (TutorialActivity)getContext();
-          mDragging           = !act.isLocked();
+          boolean locked      = act.isLocked();
+          mDragging           = !locked;
           mContinuingRotation = false;
           mBeginningRotation  = false;
+
+          if( locked )
+            {
+            TutorialState state = act.getState();
+            state.reddenLock(act);
+            }
           }
       }
 
