commit 0254cfd7d32ccde1633541f137dce5fd65f31790
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Sat Sep 19 22:49:28 2020 +0100

    Add hiding the Navigation bar (still unfinished, the bar re-appears when a DialogFragment or a PopupWindow appears)
    Integrate the Level Spinner and the Play button into one
    Move the Menu button up

diff --git a/src/main/java/org/distorted/main/RubikActivity.java b/src/main/java/org/distorted/main/RubikActivity.java
index da9675af..45b38a82 100644
--- a/src/main/java/org/distorted/main/RubikActivity.java
+++ b/src/main/java/org/distorted/main/RubikActivity.java
@@ -27,6 +27,7 @@ import android.preference.PreferenceManager;
 import androidx.appcompat.app.AppCompatActivity;
 
 import android.util.DisplayMetrics;
+import android.view.View;
 
 import com.google.firebase.analytics.FirebaseAnalytics;
 
@@ -68,6 +69,7 @@ public class RubikActivity extends AppCompatActivity
     private FirebaseAnalytics mFirebaseAnalytics;
     private static int mScreenWidth, mScreenHeight;
     private boolean mPolicyAccepted, mIsChinese;
+    private int mCurrentApiVersion;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -87,7 +89,71 @@ public class RubikActivity extends AppCompatActivity
       mScreenHeight=displaymetrics.heightPixels;
 
       mIsChinese = localeIsChinese();
-/*
+
+      hideNavigationBar();
+
+      // askForPermissions();
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private void hideNavigationBar()
+      {
+      mCurrentApiVersion = android.os.Build.VERSION.SDK_INT;
+
+      final int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                      | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                      | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                      | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+                      | View.SYSTEM_UI_FLAG_FULLSCREEN
+                      | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+
+      // This work only for android 4.4+
+      if(mCurrentApiVersion >= Build.VERSION_CODES.KITKAT)
+        {
+        getWindow().getDecorView().setSystemUiVisibility(flags);
+
+        // Code below is to handle presses of Volume up or Volume down.
+        // Without this, after pressing volume buttons, the navigation bar will
+        // show up and won't hide
+        final View decorView = getWindow().getDecorView();
+
+        decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener()
+          {
+          @Override
+          public void onSystemUiVisibilityChange(int visibility)
+            {
+            if((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0)
+              {
+              decorView.setSystemUiVisibility(flags);
+              }
+            }
+          });
+        }
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    @Override
+    public void onWindowFocusChanged(boolean hasFocus)
+      {
+      super.onWindowFocusChanged(hasFocus);
+
+      if(mCurrentApiVersion >= Build.VERSION_CODES.KITKAT && hasFocus)
+        {
+        getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                                                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                                                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                                                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+                                                        | View.SYSTEM_UI_FLAG_FULLSCREEN
+                                                        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+        }
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private void askForPermissions()
+      {
       final int REQUEST_EXTERNAL_STORAGE = 1;
 
       String[] PERMISSIONS_STORAGE =
@@ -102,7 +168,6 @@ public class RubikActivity extends AppCompatActivity
         {
         androidx.core.app.ActivityCompat.requestPermissions( this, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE );
         }
-*/
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/states/RubikStatePlay.java b/src/main/java/org/distorted/states/RubikStatePlay.java
index 4af628d2..2d6e09fe 100644
--- a/src/main/java/org/distorted/states/RubikStatePlay.java
+++ b/src/main/java/org/distorted/states/RubikStatePlay.java
@@ -22,25 +22,16 @@ package org.distorted.states;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.graphics.drawable.BitmapDrawable;
-import android.os.Build;
 import android.os.Bundle;
-import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
 import android.widget.Button;
 import android.widget.GridLayout;
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
 import android.widget.PopupWindow;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.appcompat.widget.AppCompatSpinner;
 
 import org.distorted.dialogs.RubikDialogAbout;
 import org.distorted.dialogs.RubikDialogScores;
@@ -54,11 +45,10 @@ import java.util.ArrayList;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-public class RubikStatePlay extends RubikStateAbstract implements AdapterView.OnItemSelectedListener,
-                                                                  RubikPreRender.ActionFinishedListener
+public class RubikStatePlay extends RubikStateAbstract implements RubikPreRender.ActionFinishedListener
   {
   private static final int DURATION_MILLIS = 750;
-  private static final int DEF_LEVEL =  1;
+  private static final int LEVELS_SHOWN = 10;
   public  static final int DEF_OBJECT= RubikObjectList.CUBE.ordinal();
   public  static final int DEF_SIZE  =  3;
 
@@ -67,17 +57,15 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
 
   private ImageButton mObjButton, mMenuButton, mPrevButton, mSolveButton;
   private Button mPlayButton;
-  private PopupWindow mObjectPopup, mMenuPopup;
+  private PopupWindow mObjectPopup, mMenuPopup, mPlayPopup;
   private int mObject = DEF_OBJECT;
   private int mSize   = DEF_SIZE;
-  private int mObjectSize, mMenuLayoutWidth, mMenuLayoutHeight;
+  private int mObjectSize, mMenuLayoutWidth, mMenuLayoutHeight, mPlayLayoutWidth;
   private GridLayout mObjectGrid;
-  private AppCompatSpinner mLevelSpinner;
-  private ArrayAdapter<String> mSpinnerAdapter;
   private int mLevelValue;
-  private int mBarHeight;
   private float mButtonSize, mMenuItemSize, mMenuTextSize;
   private int mColCount, mRowCount;
+  private LinearLayout mPlayLayout;
 
   private ArrayList<Move> mMoves;
   private boolean mCanPrevMove;
@@ -125,8 +113,12 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
     setupObjectWindow(act,width);
     setupObjectButton(act,width);
     layoutTop.addView(mObjButton);
-    setupLevelSpinner(act,width);
-    layoutTop.addView(mLevelSpinner);
+
+    setupMenuWindow(act,width);
+    setupMenuButton(act,width);
+    layoutTop.addView(mMenuButton);
+
+    setupPlayWindow(act,width);
     setupPlayButton(act,width);
     layoutTop.addView(mPlayButton);
 
@@ -141,10 +133,6 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
 
     LinearLayout layoutRight = act.findViewById(R.id.mainBarRight);
     layoutRight.removeAllViews();
-
-    setupMenuWindow(act,width);
-    setupMenuButton(act,width);
-    layoutRight.addView(mMenuButton);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -185,61 +173,12 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
       });
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setupLevelSpinner(final RubikActivity act, final float width)
-    {
-    int padding = (int)(width*RubikActivity.PADDING);
-    int margin  = (int)(width*RubikActivity.MARGIN);
-    int spin = RubikActivity.getDrawable(R.drawable.ui_small_spinner,R.drawable.ui_big_spinner, R.drawable.ui_big_spinner, R.drawable.ui_huge_spinner);
-
-    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT, 1.0f);
-    params.topMargin    = margin;
-    params.bottomMargin = margin;
-    params.leftMargin   = margin;
-    params.rightMargin  = margin;
-
-    mLevelSpinner = new AppCompatSpinner(act);
-    mLevelSpinner.setLayoutParams(params);
-    mLevelSpinner.setPadding(padding,0,padding,0);
-    mLevelSpinner.setBackgroundResource(spin);
-    mLevelSpinner.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
-
-    mLevelSpinner.setOnItemSelectedListener(this);
-    int sizeIndex = RubikObjectList.getSizeIndex(mObject,mSize);
-    int maxLevel = RubikObjectList.getMaxLevel(mObject, sizeIndex);
-    String[] levels = new String[maxLevel];
-
-    for(int i=0; i<maxLevel; i++)
-      {
-      levels[i] = act.getString(R.string.lv_placeholder,i+1);
-      }
-
-    if( mLevelValue>maxLevel ) mLevelValue=1;
-
-    mSpinnerAdapter = new ArrayAdapter<String>(act, android.R.layout.simple_spinner_item, levels)
-      {
-      @NonNull
-      public View getView(int position, View convertView, @NonNull ViewGroup parent)
-        {
-        View v = super.getView(position, convertView, parent);
-        TextView tv = ((TextView) v);
-        tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, mButtonSize);
-        return v;
-        }
-      };
-
-    mSpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-    mLevelSpinner.setAdapter(mSpinnerAdapter);
-    mLevelSpinner.setSelection(mLevelValue-1);
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private void setupPlayButton(final RubikActivity act, final float width)
     {
-    int padding = (int)(width*RubikActivity.PADDING);
-    int margin  = (int)(width*RubikActivity.MARGIN);
+    final int padding = (int)(width*RubikActivity.PADDING);
+    final int margin  = (int)(width*RubikActivity.MARGIN);
     LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT, 1.2f);
     params.topMargin    = margin;
     params.bottomMargin = margin;
@@ -255,9 +194,10 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
     mPlayButton.setOnClickListener( new View.OnClickListener()
       {
       @Override
-      public void onClick(View v)
+      public void onClick(View view)
         {
-        act.getPreRender().scrambleObject(mLevelValue);
+        mPlayPopup.showAsDropDown(view, margin, margin, Gravity.RIGHT);
+        mPlayPopup.update(view, mPlayLayoutWidth, (int)(LEVELS_SHOWN*(mMenuItemSize+margin)+margin));
         }
       });
     }
@@ -266,11 +206,11 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
 
   private void setupMenuButton(final RubikActivity act, final float width)
     {
-    int padding = (int)(width*RubikActivity.PADDING);
-    int margin  = (int)(width*RubikActivity.MARGIN);
-    int icon = RubikActivity.getDrawable(R.drawable.ui_small_menu,R.drawable.ui_medium_menu, R.drawable.ui_big_menu, R.drawable.ui_huge_menu);
+    final int padding = (int)(width*RubikActivity.PADDING);
+    final int margin  = (int)(width*RubikActivity.MARGIN);
+    final int icon = RubikActivity.getDrawable(R.drawable.ui_small_menu,R.drawable.ui_medium_menu, R.drawable.ui_big_menu, R.drawable.ui_huge_menu);
 
-    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
+    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT, 1.2f);
     params.topMargin    = margin;
     params.bottomMargin = margin;
     params.leftMargin   = margin;
@@ -288,14 +228,7 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
         {
         if( act.getPreRender().canPlay() )
           {
-          if( mBarHeight<=0 )
-            {
-            DisplayMetrics displaymetrics = new DisplayMetrics();
-            act.getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
-            mBarHeight=displaymetrics.heightPixels/10;
-            }
-
-          mMenuPopup.showAsDropDown(view, 0, -mMenuLayoutHeight-mBarHeight, Gravity.LEFT);
+          mMenuPopup.showAsDropDown(view, (int)(-width/12), margin, Gravity.CENTER);
           mMenuPopup.update(view, mMenuLayoutWidth, mMenuLayoutHeight);
           }
         }
@@ -427,7 +360,7 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
               mObject = obj;
               mSize   = sizes[index];
               act.changeObject(list,sizes[index], true);
-              adjustSpinner(act);
+              adjustLevels(act);
               mMoves.clear();
               }
 
@@ -462,16 +395,21 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
     int margin  = (int)(width*RubikActivity.MARGIN);
     int padding = (int)(width*RubikActivity.PADDING);
 
-    LinearLayout.LayoutParams p0 = new LinearLayout.LayoutParams( (int)width/2 - 2*padding, (int)mMenuItemSize);
-    p0.setMargins(margin, 0, margin, margin);
-    LinearLayout.LayoutParams p1 = new LinearLayout.LayoutParams( (int)width/2 - 2*padding, (int)mMenuItemSize);
-    p1.setMargins(margin, margin, margin, margin);
+    mMenuLayoutWidth = (int)(width/2);
+    mMenuLayoutHeight= (int)(2*margin + NUM_BUTTONS*(mMenuItemSize+margin));
+
+    LinearLayout.LayoutParams pT = new LinearLayout.LayoutParams( mMenuLayoutWidth - 2*padding, (int)mMenuItemSize);
+    pT.setMargins(margin, 0, margin, margin);
+    LinearLayout.LayoutParams pM = new LinearLayout.LayoutParams( mMenuLayoutWidth - 2*padding, (int)mMenuItemSize);
+    pM.setMargins(margin, margin, margin, margin);
+    LinearLayout.LayoutParams pB = new LinearLayout.LayoutParams( mMenuLayoutWidth - 2*padding, (int)mMenuItemSize);
+    pB.setMargins(margin, margin, margin, 2*margin);
 
     for(int i=0; i<NUM_BUTTONS; i++)
       {
       final int but = i;
       Button button = new Button(act);
-      button.setLayoutParams(i==0 ? p1:p0);
+      button.setLayoutParams(pT);//i==0 ? pT : (i==NUM_BUTTONS-1 ? pB : pM));
       button.setText(BUTTON_LABELS[i]);
       button.setTextSize(TypedValue.COMPLEX_UNIT_PX, mMenuTextSize);
 
@@ -481,15 +419,29 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
         public void onClick(View v)
           {
           mMenuPopup.dismiss();
-          Action(act,but);
+          MenuAction(act,but);
           }
         });
 
       menuLayout.addView(button);
       }
+    }
 
-    mMenuLayoutWidth = (int)(width/2);
-    mMenuLayoutHeight= (int)(margin + NUM_BUTTONS*(mMenuItemSize+margin));
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setupPlayWindow(final RubikActivity act, final float width)
+    {
+    LayoutInflater layoutInflater = (LayoutInflater)act.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+    final View layout = layoutInflater.inflate(R.layout.popup_play, null);
+    mPlayLayout = layout.findViewById(R.id.playGrid);
+
+    mPlayLayoutWidth = (int)(width*0.4f);
+
+    mPlayPopup = new PopupWindow(act);
+    mPlayPopup.setContentView(layout);
+    mPlayPopup.setFocusable(true);
+
+    adjustLevels(act);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -522,9 +474,10 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
         }
       }
     }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void Action(RubikActivity act, int button)
+  private void MenuAction(RubikActivity act, int button)
     {
     switch(button)
       {
@@ -555,7 +508,6 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
 
   public void savePreferences(SharedPreferences.Editor editor)
     {
-    editor.putInt("statePlay_level" , mLevelValue);
     editor.putInt("statePlay_object", mObject);
     editor.putInt("statePlay_size"  , mSize);
 
@@ -570,13 +522,18 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
       mMenuPopup.dismiss();
       mMenuPopup = null;
       }
+
+    if( mPlayPopup!=null )
+      {
+      mPlayPopup.dismiss();
+      mPlayPopup = null;
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public void restorePreferences(SharedPreferences preferences)
     {
-    mLevelValue = preferences.getInt("statePlay_level" , DEF_LEVEL );
     mObject     = preferences.getInt("statePlay_object", DEF_OBJECT);
     mSize       = preferences.getInt("statePlay_size"  , DEF_SIZE  );
     }
@@ -601,7 +558,7 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
         mObject = obj.ordinal();
         mSize   = size;
 
-        if( mLevelSpinner!=null ) adjustSpinner(act);
+        if( mPlayLayout!=null ) adjustLevels(act);
         }
 
       return success;
@@ -612,10 +569,10 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void adjustSpinner(RubikActivity act)
+  private void adjustLevels(final RubikActivity act)
     {
     int sizeIndex = RubikObjectList.getSizeIndex(mObject,mSize);
-    int maxLevel  = RubikObjectList.getMaxLevel(mObject, sizeIndex);
+    int maxLevel = RubikObjectList.getMaxLevel(mObject, sizeIndex);
     String[] levels = new String[maxLevel];
 
     for(int i=0; i<maxLevel; i++)
@@ -623,20 +580,42 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
       levels[i] = act.getString(R.string.lv_placeholder,i+1);
       }
 
-    mSpinnerAdapter = new ArrayAdapter<String>(act, android.R.layout.simple_spinner_item, levels)
+    if( mLevelValue>maxLevel ) mLevelValue=1;
+
+    float width = act.getScreenWidthInPixels();
+    int margin  = (int)(width*RubikActivity.MARGIN);
+    int padding = (int)(width*RubikActivity.PADDING);
+
+    LinearLayout.LayoutParams pM = new LinearLayout.LayoutParams( mPlayLayoutWidth - 2*padding, (int)mMenuItemSize);
+    pM.setMargins(margin, 0, margin, margin);
+    LinearLayout.LayoutParams pT = new LinearLayout.LayoutParams( mPlayLayoutWidth - 2*padding, (int)mMenuItemSize);
+    pT.setMargins(margin, margin, margin, margin);
+    LinearLayout.LayoutParams pB = new LinearLayout.LayoutParams( mPlayLayoutWidth - 2*padding, (int)mMenuItemSize);
+    pB.setMargins(margin, margin, margin, 2*margin);
+
+    mPlayLayout.removeAllViews();
+
+    for(int i=0; i<maxLevel; i++)
       {
-      @NonNull
-      public View getView(int position, View convertView, @NonNull ViewGroup parent)
+      final int but = i;
+      Button button = new Button(act);
+      button.setLayoutParams(i==0 ? pT : (i==maxLevel-1 ? pB : pM));
+      button.setText(levels[i]);
+      button.setTextSize(TypedValue.COMPLEX_UNIT_PX, mMenuTextSize);
+
+      button.setOnClickListener( new View.OnClickListener()
         {
-        View v = super.getView(position, convertView, parent);
-        TextView tv = ((TextView) v);
-        tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, mButtonSize);
-        return v;
-        }
-      };
+        @Override
+        public void onClick(View v)
+          {
+          mPlayPopup.dismiss();
+          mLevelValue = but+1;
+          act.getPreRender().scrambleObject(mLevelValue);
+          }
+        });
 
-    mSpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-    mLevelSpinner.setAdapter(mSpinnerAdapter);
+      mPlayLayout.addView(button);
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -667,17 +646,6 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
     return mSize;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
-    {
-    mLevelValue = pos+1;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onNothingSelected(AdapterView<?> parent) { }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public void onActionFinished(final long effectID)
diff --git a/src/main/res/layout/popup_play.xml b/src/main/res/layout/popup_play.xml
new file mode 100644
index 00000000..831c45a4
--- /dev/null
+++ b/src/main/res/layout/popup_play.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <LinearLayout
+        android:id="@+id/playGrid"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+    </LinearLayout>
+
+</ScrollView>
\ No newline at end of file
