Add API demo for game style system UI interaction.

Shows the ideal way for a game to interact with system UI, hiding
the status bar and nav bar while being played, making them visible
when paused or otherwise interacting with controls of the game,
and determing layout of game controls to not be covered by the
status bar or nav bar.

Change-Id: Ia192c31321113d81f96c06c7a69a06ef72c4682a
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index d7bda31..f202d59 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -2469,6 +2469,16 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".view.GameActivity"
+                android:label="Views/System UI Visibility/Game"
+                android:theme="@android:style/Theme.Holo.NoActionBar"
+                android:enabled="@bool/atLeastKitKat">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".view.Switches" android:label="Views/Switches">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/samples/ApiDemos/res/layout/game.xml b/samples/ApiDemos/res/layout/game.xml
new file mode 100644
index 0000000..809a253
--- /dev/null
+++ b/samples/ApiDemos/res/layout/game.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- BEGIN_INCLUDE(complete) -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent" android:layout_height="match_parent"
+    >
+    <!-- This is the outer area of the entire game screen, extending out under
+         system UI elements. -->
+    <view class="com.example.android.apis.view.GameActivity$Content"
+        android:id="@+id/content"
+        android:src="@drawable/frantic"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:scaleType="center"
+        />
+    <!-- This is the inner area of the game, not covered by system UI elements.
+        Any UI elements that need to be accessible when the game is paused or other
+        states where the system UI is shown (such as in menus) should go here. -->
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:fitsSystemWindows="true"
+        android:animateLayoutChanges="true"
+        >
+        <Button
+            android:id="@+id/play"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="top|right"
+            android:textSize="28dp"
+            />
+    </FrameLayout>
+</FrameLayout>
+<!-- END_INCLUDE(complete) -->
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/TouchPaint.java b/samples/ApiDemos/src/com/example/android/apis/graphics/TouchPaint.java
index c3c8a6c..a863014 100644
--- a/samples/ApiDemos/src/com/example/android/apis/graphics/TouchPaint.java
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/TouchPaint.java
@@ -26,6 +26,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.util.AttributeSet;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.MotionEvent;
@@ -87,9 +88,6 @@
     /** Is fading mode enabled? */
     boolean mFading;
 
-    /** The index of the current color to use. */
-    int mColorIndex;
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -104,10 +102,10 @@
         // the contents of the bitmap.
         if (savedInstanceState != null) {
             mFading = savedInstanceState.getBoolean("fading", true);
-            mColorIndex = savedInstanceState.getInt("color", 0);
+            mView.mColorIndex = savedInstanceState.getInt("color", 0);
         } else {
             mFading = true;
-            mColorIndex = 0;
+            mView.mColorIndex = 0;
         }
     }
 
@@ -161,7 +159,7 @@
         // Save away the fading state to restore if needed later.  Note that
         // we do not currently save the contents of the display.
         outState.putBoolean("fading", mFading);
-        outState.putInt("color", mColorIndex);
+        outState.putInt("color", mView.mColorIndex);
     }
 
     @Override
@@ -225,9 +223,9 @@
      *
      * It handles all of the input events and drawing functions.
      */
-    class PaintView extends View {
+    public static class PaintView extends View {
         private static final int FADE_ALPHA = 0x06;
-        private static final int MAX_FADE_STEPS = 256 / FADE_ALPHA + 4;
+        private static final int MAX_FADE_STEPS = 256 / (FADE_ALPHA/2) + 4;
         private static final int TRACKBALL_SCALE = 10;
 
         private static final int SPLAT_VECTORS = 40;
@@ -235,21 +233,31 @@
         private final Random mRandom = new Random();
         private Bitmap mBitmap;
         private Canvas mCanvas;
-        private final Paint mPaint;
-        private final Paint mFadePaint;
+        private final Paint mPaint = new Paint();
+        private final Paint mFadePaint = new Paint();
         private float mCurX;
         private float mCurY;
         private int mOldButtonState;
         private int mFadeSteps = MAX_FADE_STEPS;
 
+        /** The index of the current color to use. */
+        int mColorIndex;
+
         public PaintView(Context c) {
             super(c);
+            init();
+        }
+
+        public PaintView(Context c, AttributeSet attrs) {
+            super(c, attrs);
+            init();
+        }
+
+        private void init() {
             setFocusable(true);
 
-            mPaint = new Paint();
             mPaint.setAntiAlias(true);
 
-            mFadePaint = new Paint();
             mFadePaint.setColor(BACKGROUND_COLOR);
             mFadePaint.setAlpha(FADE_ALPHA);
         }
@@ -273,6 +281,31 @@
             }
         }
 
+        public void text(String text) {
+            if (mBitmap != null) {
+                final int width = mBitmap.getWidth();
+                final int height = mBitmap.getHeight();
+                mPaint.setColor(COLORS[mColorIndex]);
+                mPaint.setAlpha(255);
+                int size = height;
+                mPaint.setTextSize(size);
+                Rect bounds = new Rect();
+                mPaint.getTextBounds(text, 0, text.length(), bounds);
+                int twidth = bounds.width();
+                twidth += (twidth/4);
+                if (twidth > width) {
+                    size = (size*width)/twidth;
+                    mPaint.setTextSize(size);
+                    mPaint.getTextBounds(text, 0, text.length(), bounds);
+                }
+                Paint.FontMetrics fm = mPaint.getFontMetrics();
+                mCanvas.drawText(text, (width-bounds.width())/2,
+                        ((height-size)/2) - fm.ascent, mPaint);
+                mFadeSteps = 0;
+                invalidate();
+            }
+        }
+
         @Override
         protected void onSizeChanged(int w, int h, int oldw, int oldh) {
             int curW = mBitmap != null ? mBitmap.getWidth() : 0;
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/GameActivity.java b/samples/ApiDemos/src/com/example/android/apis/view/GameActivity.java
new file mode 100644
index 0000000..a025427
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/view/GameActivity.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.view;
+
+import android.app.ActionBar;
+import android.app.ActionBar.Tab;
+import android.app.Activity;
+import android.app.FragmentTransaction;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.SearchView;
+import android.widget.SeekBar;
+import android.widget.ShareActionProvider;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.SearchView.OnQueryTextListener;
+
+import com.example.android.apis.R;
+import com.example.android.apis.graphics.TouchPaint;
+
+/**
+ * This activity demonstrates how to use the system UI flags to
+ * implement an immersive game.
+ */
+public class GameActivity extends Activity {
+
+    /**
+     * Implementation of a view for the game, filling the entire screen.
+     */
+//BEGIN_INCLUDE(content)
+    public static class Content extends TouchPaint.PaintView implements
+            View.OnSystemUiVisibilityChangeListener, View.OnClickListener {
+        Activity mActivity;
+        Button mPlayButton;
+        boolean mPaused;
+        int mLastSystemUiVis;
+        boolean mUpdateSystemUi;
+
+        Runnable mFader = new Runnable() {
+            @Override public void run() {
+                fade();
+                if (mUpdateSystemUi) {
+                    updateNavVisibility();
+                }
+                if (!mPaused) {
+                    getHandler().postDelayed(mFader, 1000/30);
+                }
+            }
+        };
+
+        public Content(Context context, AttributeSet attrs) {
+            super(context, attrs);
+            setOnSystemUiVisibilityChangeListener(this);
+        }
+
+        public void init(Activity activity, Button playButton) {
+            // This called by the containing activity to supply the surrounding
+            // state of the game that it will interact with.
+            mActivity = activity;
+            mPlayButton = playButton;
+            mPlayButton.setOnClickListener(this);
+            setGamePaused(true);
+        }
+
+        @Override public void onSystemUiVisibilityChange(int visibility) {
+            // Detect when we go out of nav-hidden mode, to reset back to having
+            // it hidden; our game wants those elements to stay hidden as long
+            // as it is being played and stay shown when paused.
+            int diff = mLastSystemUiVis ^ visibility;
+            mLastSystemUiVis = visibility;
+            if (!mPaused && (diff&SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
+                    && (visibility&SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
+                // We are running and the system UI navigation has become
+                // shown...  we want it to remain hidden, so update our system
+                // UI state at the next game loop.
+                mUpdateSystemUi = true;
+            }
+        }
+
+        @Override protected void onWindowVisibilityChanged(int visibility) {
+            super.onWindowVisibilityChanged(visibility);
+
+            // When we become visible or invisible, play is paused.
+            setGamePaused(true);
+        }
+
+        @Override
+        public void onWindowFocusChanged(boolean hasWindowFocus) {
+            super.onWindowFocusChanged(hasWindowFocus);
+
+            // When we become visible or invisible, play is paused.
+            // Optional: pause game when window loses focus.  This will cause it to
+            // pause, for example, when the notification shade is pulled down.
+            if (!hasWindowFocus) {
+                //setGamePaused(true);
+            }
+        }
+
+        @Override public void onClick(View v) {
+            if (v == mPlayButton) {
+                // Clicking on the play/pause button toggles its state.
+                setGamePaused(!mPaused);
+            }
+        }
+
+        void setGamePaused(boolean paused) {
+            mPaused = paused;
+            mPlayButton.setText(paused ? R.string.play : R.string.pause);
+            setKeepScreenOn(!paused);
+            updateNavVisibility();
+            Handler h = getHandler();
+            if (h != null) {
+                getHandler().removeCallbacks(mFader);
+                if (!paused) {
+                    mFader.run();
+                    text("Draw!");
+                }
+            }
+        }
+
+        void updateNavVisibility() {
+            int newVis = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                    | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                    | SYSTEM_UI_FLAG_LAYOUT_STABLE;
+            if (!mPaused) {
+                newVis |= SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_FULLSCREEN
+                        | SYSTEM_UI_FLAG_HIDE_NAVIGATION  | SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+            }
+
+            // Set the new desired visibility.
+            setSystemUiVisibility(newVis);
+            mUpdateSystemUi = false;
+        }
+    }
+//END_INCLUDE(content)
+
+    Content mContent;
+
+    public GameActivity() {
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.game);
+        mContent = (Content)findViewById(R.id.content);
+        mContent.init(this, (Button)findViewById(R.id.play));
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+
+        // Pause game when its activity is paused.
+        mContent.setGamePaused(true);
+    }
+}