• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.documentsui.queries;
17 
18 import static com.android.documentsui.base.Shared.DEBUG;
19 
20 import android.content.Context;
21 import android.support.annotation.VisibleForTesting;
22 import android.text.TextUtils;
23 import android.util.Log;
24 
25 import com.android.documentsui.DocumentsApplication;
26 import com.android.documentsui.R;
27 import com.android.documentsui.base.DebugFlags;
28 import com.android.documentsui.base.EventHandler;
29 import com.android.documentsui.base.Features;
30 
31 import java.util.ArrayList;
32 import java.util.List;
33 
34 public final class CommandInterceptor implements EventHandler<String> {
35 
36     @VisibleForTesting
37     static final String COMMAND_PREFIX = ":";
38 
39     private static final String TAG = "CommandInterceptor";
40 
41     private final List<EventHandler<String[]>> mCommands = new ArrayList<>();
42 
43     private Features mFeatures;
44 
CommandInterceptor(Features features)45     public CommandInterceptor(Features features) {
46         mFeatures = features;
47 
48         mCommands.add(this::quickViewer);
49         mCommands.add(this::gestureScale);
50         mCommands.add(this::jobProgressDialog);
51         mCommands.add(this::archiveCreation);
52         mCommands.add(this::docInspector);
53         mCommands.add(this::docDetails);
54         mCommands.add(this::forcePaging);
55     }
56 
add(EventHandler<String[]> handler)57     public void add(EventHandler<String[]> handler) {
58         mCommands.add(handler);
59     }
60 
61     @Override
accept(String query)62     public boolean accept(String query) {
63         if (!mFeatures.isDebugSupportEnabled()) {
64             return false;
65         }
66 
67         if (!mFeatures.isCommandInterceptorEnabled()) {
68             if (DEBUG) Log.v(TAG, "Skipping input, command interceptor disabled.");
69             return false;
70         }
71 
72         if (query.length() > COMMAND_PREFIX.length() && query.startsWith(COMMAND_PREFIX)) {
73             String[] tokens = query.substring(COMMAND_PREFIX.length()).split("\\s+");
74             for (EventHandler<String[]> command : mCommands) {
75                 if (command.accept(tokens)) {
76                     return true;
77                 }
78             }
79             Log.d(TAG, "Unrecognized debug command: " + query);
80         }
81         return false;
82     }
83 
quickViewer(String[] tokens)84     private boolean quickViewer(String[] tokens) {
85         if ("qv".equals(tokens[0])) {
86             if (tokens.length == 2 && !TextUtils.isEmpty(tokens[1])) {
87                 DebugFlags.setQuickViewer(tokens[1]);
88                 Log.i(TAG, "Set quick viewer to: " + tokens[1]);
89                 return true;
90             } else {
91                 Log.w(TAG, "Invalid command structure: " + TextUtils.join(" ", tokens));
92             }
93         } else if ("deqv".equals(tokens[0])) {
94             Log.i(TAG, "Unset quick viewer");
95             DebugFlags.setQuickViewer(null);
96             return true;
97         }
98         return false;
99     }
100 
gestureScale(String[] tokens)101     private boolean gestureScale(String[] tokens) {
102         if ("gs".equals(tokens[0])) {
103             if (tokens.length == 2 && !TextUtils.isEmpty(tokens[1])) {
104                 boolean enabled = asBool(tokens[1]);
105                 mFeatures.forceFeature(R.bool.feature_gesture_scale, enabled);
106                 Log.i(TAG, "Set gesture scale enabled to: " + enabled);
107                 return true;
108             }
109             Log.w(TAG, "Invalid command structure: " + TextUtils.join(" ", tokens));
110         }
111         return false;
112     }
113 
jobProgressDialog(String[] tokens)114     private boolean jobProgressDialog(String[] tokens) {
115         if ("jpd".equals(tokens[0])) {
116             if (tokens.length == 2 && !TextUtils.isEmpty(tokens[1])) {
117                 boolean enabled = asBool(tokens[1]);
118                 mFeatures.forceFeature(R.bool.feature_job_progress_dialog, enabled);
119                 Log.i(TAG, "Set job progress dialog enabled to: " + enabled);
120                 return true;
121             }
122             Log.w(TAG, "Invalid command structure: " + TextUtils.join(" ", tokens));
123         }
124         return false;
125     }
126 
archiveCreation(String[] tokens)127     private boolean archiveCreation(String[] tokens) {
128         if ("zip".equals(tokens[0])) {
129             if (tokens.length == 2 && !TextUtils.isEmpty(tokens[1])) {
130                 boolean enabled = asBool(tokens[1]);
131                 mFeatures.forceFeature(R.bool.feature_archive_creation, enabled);
132                 Log.i(TAG, "Set gesture scale enabled to: " + enabled);
133                 return true;
134             }
135             Log.w(TAG, "Invalid command structure: " + TextUtils.join(" ", tokens));
136         }
137         return false;
138     }
139 
docInspector(String[] tokens)140     private boolean docInspector(String[] tokens) {
141         if ("inspect".equals(tokens[0])) {
142             if (tokens.length == 2 && !TextUtils.isEmpty(tokens[1])) {
143                 boolean enabled = asBool(tokens[1]);
144                 mFeatures.forceFeature(R.bool.feature_inspector, enabled);
145                 Log.i(TAG, "Set doc inspector enabled to: " + enabled);
146                 return true;
147             }
148             Log.w(TAG, "Invalid command structure: " + TextUtils.join(" ", tokens));
149         }
150         return false;
151     }
152 
docDetails(String[] tokens)153     private boolean docDetails(String[] tokens) {
154         if ("docinfo".equals(tokens[0])) {
155             if (tokens.length == 2 && !TextUtils.isEmpty(tokens[1])) {
156                 boolean enabled = asBool(tokens[1]);
157                 DebugFlags.setDocumentDetailsEnabled(enabled);
158                 Log.i(TAG, "Set doc details enabled to: " + enabled);
159                 return true;
160             }
161             Log.w(TAG, "Invalid command structure: " + TextUtils.join(" ", tokens));
162         }
163         return false;
164     }
165 
forcePaging(String[] tokens)166     private boolean forcePaging(String[] tokens) {
167         if ("page".equals(tokens[0])) {
168             if (tokens.length >= 2) {
169                 try {
170                     int offset = Integer.parseInt(tokens[1]);
171                     int limit = (tokens.length == 3) ? Integer.parseInt(tokens[2]) : -1;
172                     DebugFlags.setForcedPaging(offset, limit);
173                     Log.i(TAG, "Set forced paging to offset: " + offset + ", limit: " + limit);
174                     return true;
175                 } catch (NumberFormatException e) {
176                     Log.w(TAG, "Command input does not contain valid numbers: "
177                             + TextUtils.join(" ", tokens));
178                     return false;
179                 }
180             } else {
181                 Log.w(TAG, "Invalid command structure: " + TextUtils.join(" ", tokens));
182             }
183         } else if ("deqv".equals(tokens[0])) {
184             Log.i(TAG, "Unset quick viewer");
185             DebugFlags.setQuickViewer(null);
186             return true;
187         }
188         return false;
189     }
190 
asBool(String val)191     private final boolean asBool(String val) {
192         if (val == null || val.equals("0")) {
193             return false;
194         }
195         if (val.equals("1")) {
196             return true;
197         }
198         return Boolean.valueOf(val);
199     }
200 
201     public static final class DumpRootsCacheHandler implements EventHandler<String[]> {
202         private final Context mContext;
203 
DumpRootsCacheHandler(Context context)204         public DumpRootsCacheHandler(Context context) {
205             mContext = context;
206         }
207 
208         @Override
accept(String[] tokens)209         public boolean accept(String[] tokens) {
210             if ("dumpCache".equals(tokens[0])) {
211                 DocumentsApplication.getProvidersCache(mContext).logCache();
212                 return true;
213             }
214             return false;
215         }
216     }
217 
218     /**
219      * Wraps {@link CommandInterceptor} in a tiny decorator that adds support for
220      * enabling CommandInterceptor feature based on some magic query input.
221      *
222      * <p>It's like super meta, maaaannn.
223      */
createDebugModeFlipper( Features features, Runnable debugFlipper, CommandInterceptor interceptor)224     public static final EventHandler<String> createDebugModeFlipper(
225             Features features,
226             Runnable debugFlipper,
227             CommandInterceptor interceptor) {
228 
229         if (!features.isDebugSupportEnabled()) {
230             return interceptor;
231         }
232 
233         String magicString1 = COMMAND_PREFIX + "wwssadadba";
234         String magicString2 = "up up down down left right left right b a";
235 
236         return new EventHandler<String>() {
237             @Override
238             public boolean accept(String query) {
239                 assert(features.isDebugSupportEnabled());
240 
241                 if (magicString1.equals(query) || magicString2.equals(query)) {
242                     debugFlipper.run();
243                 }
244                 return interceptor.accept(query);
245             }
246         };
247     }
248 }
249