Merge "Allow debug mode to be enabled on touch devices." into arc-apps
am: 1d1a16333d

Change-Id: Iba394e5701474dab207149641c130675d67ebc7b
diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java
index 9e67be0..0538bce 100644
--- a/src/com/android/documentsui/BaseActivity.java
+++ b/src/com/android/documentsui/BaseActivity.java
@@ -45,6 +45,7 @@
 import com.android.documentsui.Injector.Injected;
 import com.android.documentsui.NavigationViewManager.Breadcrumb;
 import com.android.documentsui.base.DocumentInfo;
+import com.android.documentsui.base.EventHandler;
 import com.android.documentsui.base.RootInfo;
 import com.android.documentsui.base.Shared;
 import com.android.documentsui.base.State;
@@ -178,9 +179,17 @@
         // We piggy back on search input as it is the only text input
         // area in the app. But the functionality is independent
         // of "regular" search query processing.
-        CommandInterceptor dbgCommands = new CommandInterceptor(mInjector.features);
-        dbgCommands.add(new CommandInterceptor.DumpRootsCacheHandler(this));
-        mSearchManager = new SearchViewManager(searchListener, dbgCommands, icicle);
+        final CommandInterceptor cmdInterceptor = new CommandInterceptor(mInjector.features);
+        cmdInterceptor.add(new CommandInterceptor.DumpRootsCacheHandler(this));
+
+        // A tiny decorator that adds support for enabling CommandInterceptor
+        // based on query input. It's sorta like CommandInterceptor, but its metaaahhh.
+        EventHandler<String> queryInterceptor =
+                CommandInterceptor.createDebugModeFlipper(
+                        mInjector.features,
+                        mInjector.debugHelper::toggleDebugMode,
+                        cmdInterceptor);
+        mSearchManager = new SearchViewManager(searchListener, queryInterceptor, icicle);
         mSortController = SortController.create(this, mState.derivedMode, mState.sortModel);
 
         mPreferencesMonitor = new PreferencesMonitor(
diff --git a/src/com/android/documentsui/base/DebugHelper.java b/src/com/android/documentsui/base/DebugHelper.java
index ab35645..f649dfb 100644
--- a/src/com/android/documentsui/base/DebugHelper.java
+++ b/src/com/android/documentsui/base/DebugHelper.java
@@ -26,29 +26,35 @@
  * Debug menu tools requested by QA Fred.
  */
 public class DebugHelper {
+
     private static final String TAG = "DebugHelper";
-    private static final int[][] code = new int[][] {
+
+    private static final int[][] sCode = 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[][] {
+
+    private static final int[][] sColors = new int[][] {
             {0xFFDB3236, 0xFFB71C1C},
             {0xFF3cba54, 0xFF1B5E20},
             {0xFFf4c20d, 0xFFF9A825},
             {0xFF4885ed, 0xFF0D47A1}
     };
-    private static final Pair[] messages = new Pair[]{
+
+    @SuppressWarnings("unchecked")
+    private static final Pair<String, Integer>[] sMessages = new Pair[]{
             new Pair<>("Woof Woof", R.drawable.debug_msg_1),
             new Pair<>("ワンワン", R.drawable.debug_msg_2)
     };
 
-    private boolean debugEnabled = false;
-    private long lastTime = 0;
-    private int position = 0;
-    private int codeIndex = 0;
-    private int colorIndex = 0;
-    private int messageIndex = 0;
-    private Injector<?> mInjector;
+    private final Injector<?> mInjector;
+
+    private boolean mDebugEnabled;
+    private long mLastTime;
+    private int mPosition;
+    private int mCodeIndex;
+    private int mColorIndex;
+    private int mMessageIndex;
 
     public DebugHelper(Injector<?> injector) {
         mInjector = injector;
@@ -57,56 +63,60 @@
     public int[] getNextColors() {
         assert (mInjector.features.isDebugSupportEnabled());
 
-        if (colorIndex == colors.length) {
-            colorIndex = 0;
+        if (mColorIndex == sColors.length) {
+            mColorIndex = 0;
         }
 
-        return colors[colorIndex++];
+        return sColors[mColorIndex++];
     }
 
     public Pair<String, Integer> getNextMessage() {
         assert (mInjector.features.isDebugSupportEnabled());
 
-        if (messageIndex == messages.length) {
-            messageIndex = 0;
+        if (mMessageIndex == sMessages.length) {
+            mMessageIndex = 0;
         }
 
-        return messages[messageIndex++];
+        return sMessages[mMessageIndex++];
     }
 
     public void debugCheck(long time, int keyCode) {
-        if (time == lastTime) {
+        if (time == mLastTime) {
             return;
         }
-        lastTime = time;
+        mLastTime = time;
 
-        if (position == 0) {
-            for (int i = 0; i < code.length; i++) {
-                if (keyCode == code[i][0]) {
-                    codeIndex = i;
+        if (mPosition == 0) {
+            for (int i = 0; i < sCode.length; i++) {
+                if (keyCode == sCode[i][0]) {
+                    mCodeIndex = i;
                     break;
                 }
             }
         }
 
-        if (keyCode == code[codeIndex][position]) {
-            position++;
-        } else if (position  > 2 || (position == 2 && keyCode != code[codeIndex][0])) {
-            position = 0;
+        if (keyCode == sCode[mCodeIndex][mPosition]) {
+            mPosition++;
+        } else if (mPosition  > 2 || (mPosition == 2 && keyCode != sCode[mCodeIndex][0])) {
+            mPosition = 0;
         }
 
-        if (position == code[codeIndex].length) {
-            position = 0;
-            debugEnabled = !debugEnabled;
-            // Actions is content-scope, so it can technically be null, though
-            // not likely.
-            if (mInjector.actions != null) {
-                mInjector.actions.setDebugMode(debugEnabled);
-            }
+        if (mPosition == sCode[mCodeIndex].length) {
+            mPosition = 0;
+            toggleDebugMode();
+        }
+    }
 
-            if (Shared.VERBOSE) {
-                Log.v(TAG, "Debug mode " + (debugEnabled ? "on" : "off"));
-            }
+    public void toggleDebugMode() {
+        mDebugEnabled = !mDebugEnabled;
+        // Actions is content-scope, so it can technically be null, though
+        // not likely.
+        if (mInjector.actions != null) {
+            mInjector.actions.setDebugMode(mDebugEnabled);
+        }
+
+        if (Shared.VERBOSE) {
+            Log.v(TAG, "Debug mode " + (mDebugEnabled ? "on" : "off"));
         }
     }
 }
diff --git a/src/com/android/documentsui/base/Features.java b/src/com/android/documentsui/base/Features.java
index 82c4eab..7a8fed6 100644
--- a/src/com/android/documentsui/base/Features.java
+++ b/src/com/android/documentsui/base/Features.java
@@ -90,8 +90,8 @@
 
         @Override
         public boolean isCommandInterceptorEnabled() {
-            return !mUserMgr.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES)
-                    && isEnabled(R.bool.feature_command_interceptor);
+            assert(isDebugPolicyEnabled());
+            return isEnabled(R.bool.feature_command_interceptor);
         }
 
         @Override
@@ -104,10 +104,13 @@
             return isEnabled(R.bool.feature_content_refresh);
         }
 
+        private boolean isDebugPolicyEnabled() {
+            return !mUserMgr.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES);
+        }
+
         @Override
         public boolean isDebugSupportEnabled() {
-            return !mUserMgr.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES)
-                    && !mUserMgr.hasUserRestriction(UserManager.DISALLOW_FUN);
+            return isDebugPolicyEnabled() && isFunPolicyEnabled();
         }
 
         @Override
@@ -115,6 +118,10 @@
             return isEnabled(R.bool.feature_folders_in_search_results);
         }
 
+        private boolean isFunPolicyEnabled() {
+            return !mUserMgr.hasUserRestriction(UserManager.DISALLOW_FUN);
+        }
+
         @Override
         public boolean isGestureScaleEnabled() {
             return isEnabled(R.bool.feature_gesture_scale);
diff --git a/src/com/android/documentsui/queries/CommandInterceptor.java b/src/com/android/documentsui/queries/CommandInterceptor.java
index f82502d..d428a1a 100644
--- a/src/com/android/documentsui/queries/CommandInterceptor.java
+++ b/src/com/android/documentsui/queries/CommandInterceptor.java
@@ -59,6 +59,10 @@
 
     @Override
     public boolean accept(String query) {
+        if (!mFeatures.isDebugSupportEnabled()) {
+            return false;
+        }
+
         if (!mFeatures.isCommandInterceptorEnabled()) {
             if (DEBUG) Log.v(TAG, "Skipping input, command interceptor disabled.");
             return false;
@@ -196,4 +200,35 @@
             return false;
         }
     }
+
+    /**
+     * Wraps {@link CommandInterceptor} in a tiny decorator that adds support for
+     * enabling CommandInterceptor feature based on some magic query input.
+     *
+     * <p>It's like super meta, maaaannn.
+     */
+    public static final EventHandler<String> createDebugModeFlipper(
+            Features features,
+            Runnable debugFlipper,
+            CommandInterceptor interceptor) {
+
+        if (!features.isDebugSupportEnabled()) {
+            return interceptor;
+        }
+
+        String magicString1 = COMMAND_PREFIX + "wwssadadba";
+        String magicString2 = "up up down down left right left right b a";
+
+        return new EventHandler<String>() {
+            @Override
+            public boolean accept(String query) {
+                assert(features.isDebugSupportEnabled());
+
+                if (magicString1.equals(query) || magicString2.equals(query)) {
+                    debugFlipper.run();
+                }
+                return interceptor.accept(query);
+            }
+        };
+    }
 }
diff --git a/src/com/android/documentsui/queries/SearchViewManager.java b/src/com/android/documentsui/queries/SearchViewManager.java
index a57326a..6a2790d 100644
--- a/src/com/android/documentsui/queries/SearchViewManager.java
+++ b/src/com/android/documentsui/queries/SearchViewManager.java
@@ -35,6 +35,7 @@
 import com.android.documentsui.R;
 import com.android.documentsui.base.DocumentInfo;
 import com.android.documentsui.base.DocumentStack;
+import com.android.documentsui.base.EventHandler;
 import com.android.documentsui.base.RootInfo;
 import com.android.documentsui.base.Shared;
 
@@ -48,7 +49,7 @@
     private static final String TAG = "SearchManager";
 
     private final SearchManagerListener mListener;
-    private final CommandInterceptor mCommandProcessor;
+    private final EventHandler<String> mCommandProcessor;
 
     private @Nullable String mCurrentSearch;
     private boolean mSearchExpanded;
@@ -61,7 +62,7 @@
 
     public SearchViewManager(
             SearchManagerListener listener,
-            CommandInterceptor commandProcessor,
+            EventHandler<String> commandProcessor,
             @Nullable Bundle savedState) {
 
         assert (listener != null);