Added some debugging tools.

Test: verified
Change-Id: Ie043c5ff8209c9aa78480d16a4c710377a0c1bbb
diff --git a/res/drawable/debug_msg_1.png b/res/drawable/debug_msg_1.png
new file mode 100644
index 0000000..862769a
--- /dev/null
+++ b/res/drawable/debug_msg_1.png
Binary files differ
diff --git a/res/drawable/debug_msg_2.png b/res/drawable/debug_msg_2.png
new file mode 100644
index 0000000..e4c6259
--- /dev/null
+++ b/res/drawable/debug_msg_2.png
Binary files differ
diff --git a/res/drawable/ic_debug_menu.xml b/res/drawable/ic_debug_menu.xml
new file mode 100644
index 0000000..f0ada10
--- /dev/null
+++ b/res/drawable/ic_debug_menu.xml
@@ -0,0 +1,13 @@
+<!--
+This file has no copyright assigned and is placed in the Public Domain.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="24dp"
+        android:width="24dp"
+        android:viewportHeight="399.0"
+        android:viewportWidth="425.8">
+    <path android:fillColor="?android:attr/colorControlNormal"
+          android:pathData="M210.4,167c24,3 52.4,8.3 65,31.5 9.3,13.7 17.4,28.4 29,40.4 20.4,10.2 40.8,22.7 55.5,40.6 18.3,27.6 12.7,67 -8.7,91.2 -24,22 -58.5,33 -90.5,25 -20.5,-4.2 -40.8,-16.3 -62,-9.5 -19.8,5.5 -39.3,14.2 -60.2,11.8 -20,-0.5 -37.8,-10.3 -54.6,-20 -19.5,-12.6 -31.7,-35.3 -30.4,-58.6 0,-35 26.4,-64 56,-79.3 12.6,-6 26,-12.7 32,-26.3 8.5,-15.8 16,-35.5 35,-41 11,-3.8 22.4,-5.4 33.8,-5.6z" />
+    <path android:fillColor="?android:attr/colorControlNormal"
+          android:pathData="M2.5,133.3c5,-18 19.7,-34.7 39,-37 13.8,-2 28,1.6 40.2,8 22.2,15 35,40 42.4,64.8 3.8,12 6.3,24.8 3.2,37 -2.8,14.8 -13.4,27 -27,32.8 -17.7,10.7 -41.2,9.2 -58,-2.4 -10,-6.7 -19,-15.3 -24.4,-26.2 -12.8,-23 -21.4,-50.5 -15.3,-77zM169,0.5s29.5,0.2 45.7,38c0,0 11.3,24.3 7.4,61.8 0,0 -0.5,37.8 -23,53.2 0,0 -30.6,27.8 -65.7,-9.6 0,0 -24.5,-26.4 -29.3,-63.5 0,0 -5.3,-38.4 18.5,-61.3 0,0 20,-19 46.5,-18.7zM314,6.2S350,17 346,58.4c0,0 2,33.2 -17.7,63.5 0,0 -20,37.3 -46.4,41.4 0,0 -29.2,9 -50.4,-36 0,0 -15.2,-51 8.3,-85.3 0,0 27.3,-50.8 74,-35.8zM394.4,108.4c17,5.2 29,21.6 30.5,39 1.6,15.8 -2,31.6 -7.4,46.3 -8.7,21.6 -24.2,40.4 -43.8,52.7 -14.8,8 -33.3,4.6 -47,-4.4 -5.5,-2.7 -9,-7.7 -11.2,-13.2 -9,-18 -10.4,-40 -2,-58.5 6.8,-17.4 15.4,-35.2 30,-47.5 11,-10 25.6,-18.3 41.2,-16.7 3.3,0.4 6.5,1.2 9.6,2.4z" />
+</vector>
\ No newline at end of file
diff --git a/res/menu/activity.xml b/res/menu/activity.xml
index 6e09e31..02975b1 100644
--- a/res/menu/activity.xml
+++ b/res/menu/activity.xml
@@ -34,6 +34,12 @@
 <!-- This group is being hidden when searching is in full bar mode-->
     <group android:id="@+id/group_hide_when_searching">
         <item
+            android:id="@+id/menu_debug"
+            android:title="Debug"
+            android:icon="@drawable/ic_debug_menu"
+            android:visible="false"
+            android:showAsAction="always" />
+        <item
             android:id="@+id/menu_grid"
             android:title="@string/menu_grid"
             android:icon="@drawable/ic_menu_view_grid"
diff --git a/res/values/config.xml b/res/values/config.xml
index 8178bca..1e1341c 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -40,5 +40,6 @@
     <bool name="feature_remote_actions">true</bool>
     <bool name="feature_system_keyboard_navigation">true</bool>
     <bool name="feature_virtual_files_sharing">true</bool>
+    <bool name="feature_debug_mode">false</bool>
 
 </resources>
diff --git a/src/com/android/documentsui/AbstractActionHandler.java b/src/com/android/documentsui/AbstractActionHandler.java
index 4baf840..2b98244 100644
--- a/src/com/android/documentsui/AbstractActionHandler.java
+++ b/src/com/android/documentsui/AbstractActionHandler.java
@@ -27,12 +27,14 @@
 import android.content.Loader;
 import android.content.pm.ResolveInfo;
 import android.database.Cursor;
+import android.graphics.drawable.ColorDrawable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.provider.DocumentsContract;
 import android.support.annotation.VisibleForTesting;
 import android.util.Log;
+import android.util.Pair;
 import android.view.DragEvent;
 
 import com.android.documentsui.AbstractActionHandler.CommonAddons;
@@ -57,6 +59,7 @@
 import com.android.documentsui.selection.Selection;
 import com.android.documentsui.selection.SelectionManager;
 import com.android.documentsui.sidebar.EjectRootTask;
+import com.android.documentsui.ui.Snackbars;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -351,6 +354,32 @@
     }
 
     @Override
+    public void setDebugMode(boolean enabled) {
+        mState.debugMode = enabled;
+        mActivity.invalidateOptionsMenu();
+
+        if (enabled) {
+            showDebugMessage();
+        } else {
+            mActivity.getActionBar().setBackgroundDrawable(new ColorDrawable(
+                    mActivity.getResources().getColor(R.color.primary)));
+            mActivity.getWindow().setStatusBarColor(
+                    mActivity.getResources().getColor(R.color.primary_dark));
+        }
+    }
+
+    @Override
+    public void showDebugMessage() {
+        int[] colors = mInjector.debugHelper.getNextColors();
+        Pair<String, Integer> messagePair = mInjector.debugHelper.getNextMessage();
+
+        Snackbars.showCustomTextWithImage(mActivity, messagePair.first, messagePair.second);
+
+        mActivity.getActionBar().setBackgroundDrawable(new ColorDrawable(colors[0]));
+        mActivity.getWindow().setStatusBarColor(colors[1]);
+    }
+
+    @Override
     public void cutToClipboard() {
         throw new UnsupportedOperationException("Cut not supported!");
     }
diff --git a/src/com/android/documentsui/ActionHandler.java b/src/com/android/documentsui/ActionHandler.java
index 3d81c93..03e977e 100644
--- a/src/com/android/documentsui/ActionHandler.java
+++ b/src/com/android/documentsui/ActionHandler.java
@@ -135,6 +135,9 @@
 
     void viewInOwner();
 
+    void setDebugMode(boolean enabled);
+    void showDebugMessage();
+
     /**
      * Allow action handler to be initialized in a new scope.
      * @return this
diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java
index 30f0dec..cd3b57e 100644
--- a/src/com/android/documentsui/BaseActivity.java
+++ b/src/com/android/documentsui/BaseActivity.java
@@ -35,6 +35,7 @@
 import android.support.annotation.LayoutRes;
 import android.support.annotation.VisibleForTesting;
 import android.util.Log;
+import android.view.KeyEvent;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
@@ -346,6 +347,10 @@
                 getInjector().actions.selectAllFiles();
                 return true;
 
+            case R.id.menu_debug:
+                getInjector().actions.showDebugMessage();
+                return true;
+
             default:
                 return super.onOptionsItemSelected(item);
         }
@@ -568,6 +573,14 @@
         }
     }
 
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        if (event.getAction() == KeyEvent.ACTION_DOWN) {
+            mInjector.debugHelper.debugCheck(event.getDownTime(), event.getKeyCode());
+        }
+        return super.dispatchKeyEvent(event);
+    }
+
     /**
      * Pops the top entry off the directory stack, and returns the user to the previous directory.
      * If the directory stack only contains one item, this method does nothing.
diff --git a/src/com/android/documentsui/Injector.java b/src/com/android/documentsui/Injector.java
index 8f22d22..387a9ab 100644
--- a/src/com/android/documentsui/Injector.java
+++ b/src/com/android/documentsui/Injector.java
@@ -27,6 +27,7 @@
 import com.android.documentsui.base.EventHandler;
 import com.android.documentsui.base.Features;
 import com.android.documentsui.dirlist.DocumentsAdapter;
+import com.android.documentsui.base.DebugHelper;
 import com.android.documentsui.prefs.ScopedPreferences;
 import com.android.documentsui.queries.SearchViewManager;
 import com.android.documentsui.selection.SelectionManager;
@@ -66,6 +67,8 @@
 
     private final Model mModel;
 
+    public final DebugHelper debugHelper;
+
     // must be initialized before calling super.onCreate because prefs
     // are used in State initialization.
     public Injector(
@@ -92,6 +95,7 @@
         this.messages = messages;
         this.dialogs = dialogs;
         this.mModel = model;
+        this.debugHelper = new DebugHelper(this);
     }
 
     public Model getModel() {
diff --git a/src/com/android/documentsui/MenuManager.java b/src/com/android/documentsui/MenuManager.java
index 29f55f5..c965c32 100644
--- a/src/com/android/documentsui/MenuManager.java
+++ b/src/com/android/documentsui/MenuManager.java
@@ -76,6 +76,7 @@
         updateNewWindow(menu.findItem(R.id.menu_new_window));
         updateModePicker(menu.findItem(R.id.menu_grid), menu.findItem(R.id.menu_list));
         updateAdvanced(menu.findItem(R.id.menu_advanced));
+        updateDebug(menu.findItem(R.id.menu_debug));
 
         Menus.disableHiddenItems(menu);
     }
@@ -221,6 +222,10 @@
                 ? R.string.menu_advanced_hide : R.string.menu_advanced_show);
     }
 
+    protected void updateDebug(MenuItem debug) {
+        debug.setVisible(mState.debugMode);
+    }
+
     protected void updateSettings(MenuItem settings) {
         settings.setVisible(false);
     }
diff --git a/src/com/android/documentsui/base/DebugHelper.java b/src/com/android/documentsui/base/DebugHelper.java
new file mode 100644
index 0000000..ce899d1
--- /dev/null
+++ b/src/com/android/documentsui/base/DebugHelper.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2017 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.android.documentsui.base;
+
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.documentsui.Injector;
+import com.android.documentsui.R;
+
+/**
+ * Debug menu tools requested by QA Fred.
+ */
+public class DebugHelper {
+    private static final String TAG = "DebugHelper";
+    private static final int[][] code = new int[][] {
+            {19, 19, 20, 20, 21, 22, 21, 22, 30, 29},
+            {51, 51, 47, 47, 29, 32, 29, 32, 30, 29}
+    };
+    private static final int[][] colors = new int[][] {
+            {0xFFDB3236, 0xFFB71C1C},
+            {0xFF3cba54, 0xFF1B5E20},
+            {0xFFf4c20d, 0xFFF9A825},
+            {0xFF4885ed, 0xFF0D47A1}
+    };
+    private static final Pair[] messages = new Pair[]{
+            new Pair<>("Woof Woof", R.drawable.debug_msg_1),
+            new Pair<>("ワンワン", R.drawable.debug_msg_2)
+    };
+
+    private boolean debugEnabled = false;
+    private Injector mInjector;
+    private long lastTime = 0;
+    private int position = 0;
+    private int codeIndex = 0;
+    private int colorIndex = 0;
+    private int messageIndex = 0;
+
+    public DebugHelper(Injector injector) {
+        mInjector = injector;
+    }
+
+    public int[] getNextColors() {
+        if (colorIndex == colors.length) {
+            colorIndex = 0;
+        }
+
+        return colors[colorIndex++];
+    }
+
+    public Pair<String, Integer> getNextMessage() {
+        if (messageIndex == messages.length) {
+            messageIndex = 0;
+        }
+
+        return messages[messageIndex++];
+    }
+
+    public void debugCheck(long time, int keyCode) {
+        if (time == lastTime) {
+            return;
+        }
+        lastTime = time;
+
+        if (position == 0) {
+            for (int i = 0; i < code.length; i++) {
+                if (keyCode == code[i][0]) {
+                    codeIndex = i;
+                    break;
+                }
+            }
+        }
+
+        if (keyCode == code[codeIndex][position]) {
+            position++;
+        } else if (position  > 2 || (position == 2 && keyCode != code[codeIndex][0])) {
+            position = 0;
+        }
+
+        if (position == code[codeIndex].length) {
+            position = 0;
+            debugEnabled = !debugEnabled;
+            mInjector.actions.setDebugMode(debugEnabled);
+
+            if (Shared.VERBOSE) {
+                Log.v(TAG, "Debug mode " + (debugEnabled ? "on" : "off"));
+            }
+        }
+    }
+}
diff --git a/src/com/android/documentsui/base/State.java b/src/com/android/documentsui/base/State.java
index d50dbdb..0be6f67 100644
--- a/src/com/android/documentsui/base/State.java
+++ b/src/com/android/documentsui/base/State.java
@@ -73,6 +73,8 @@
     /** Derived from local preferences */
     public @ViewMode int derivedMode = MODE_GRID;
 
+    public boolean debugMode = false;
+
     /** Current sort state */
     public SortModel sortModel;
 
diff --git a/src/com/android/documentsui/ui/Snackbars.java b/src/com/android/documentsui/ui/Snackbars.java
index 3996206..1e81889 100644
--- a/src/com/android/documentsui/ui/Snackbars.java
+++ b/src/com/android/documentsui/ui/Snackbars.java
@@ -19,7 +19,9 @@
 import android.annotation.StringRes;
 import android.app.Activity;
 import android.support.design.widget.Snackbar;
+import android.view.Gravity;
 import android.view.View;
+import android.widget.TextView;
 
 import com.android.documentsui.R;
 import com.android.documentsui.base.Shared;
@@ -70,6 +72,17 @@
         makeSnackbar(activity, R.string.rename_error, Snackbar.LENGTH_SHORT).show();
     }
 
+    public static final void showCustomTextWithImage(Activity activity, String text, int imageRes) {
+        Snackbar snackbar = makeSnackbar(activity, text, Snackbar.LENGTH_SHORT);
+        View snackbarLayout = snackbar.getView();
+        TextView textView = (TextView)snackbarLayout.findViewById(
+                android.support.design.R.id.snackbar_text);
+        textView.setGravity(Gravity.CENTER_HORIZONTAL);
+        textView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
+        textView.setCompoundDrawablesWithIntrinsicBounds(imageRes, 0, 0, 0);
+        snackbar.show();
+    }
+
     public static final Snackbar makeSnackbar(Activity activity, @StringRes int messageId,
             int duration) {
         return Snackbars.makeSnackbar(
diff --git a/tests/common/com/android/documentsui/testing/TestMenu.java b/tests/common/com/android/documentsui/testing/TestMenu.java
index 1a6489f..b8617cc 100644
--- a/tests/common/com/android/documentsui/testing/TestMenu.java
+++ b/tests/common/com/android/documentsui/testing/TestMenu.java
@@ -58,6 +58,7 @@
                 R.id.menu_grid,
                 R.id.menu_list,
                 R.id.menu_advanced,
+                R.id.menu_debug,
                 R.id.menu_eject_root,
                 R.id.menu_view_in_owner);
     }
diff --git a/tests/unit/com/android/documentsui/files/MenuManagerTest.java b/tests/unit/com/android/documentsui/files/MenuManagerTest.java
index 9ca4431..843c38e 100644
--- a/tests/unit/com/android/documentsui/files/MenuManagerTest.java
+++ b/tests/unit/com/android/documentsui/files/MenuManagerTest.java
@@ -74,6 +74,7 @@
     private TestMenuItem advanced;
     private TestMenuItem eject;
     private TestMenuItem view;
+    private TestMenuItem debug;
 
     private TestFeatures features;
     private TestSelectionDetails selectionDetails;
@@ -111,6 +112,7 @@
         advanced = testMenu.findItem(R.id.menu_advanced);
         eject = testMenu.findItem(R.id.menu_eject_root);
         view = testMenu.findItem(R.id.menu_view_in_owner);
+        debug = testMenu.findItem(R.id.menu_debug);
 
         features = new TestFeatures();
 
@@ -311,6 +313,7 @@
         advanced.assertInvisible();
         advanced.assertTitle(R.string.menu_advanced_show);
         createDir.assertDisabled();
+        debug.assertInvisible();
         assertTrue(testSearchManager.updateMenuCalled());
     }