• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 
17 package com.googlecode.android_scripting.activity;
18 
19 import android.app.ListActivity;
20 import android.app.SearchManager;
21 import android.content.Intent;
22 import android.database.MatrixCursor;
23 import android.os.Bundle;
24 import android.util.TypedValue;
25 import android.view.ContextMenu;
26 import android.view.KeyEvent;
27 import android.view.Menu;
28 import android.view.MenuItem;
29 import android.view.View;
30 import android.view.ViewGroup;
31 import android.view.ContextMenu.ContextMenuInfo;
32 import android.widget.AdapterView;
33 import android.widget.AlphabetIndexer;
34 import android.widget.BaseAdapter;
35 import android.widget.ListView;
36 import android.widget.SectionIndexer;
37 import android.widget.TextView;
38 
39 import com.google.common.base.Predicate;
40 import com.google.common.collect.Collections2;
41 import com.google.common.collect.Lists;
42 import com.googlecode.android_scripting.BaseApplication;
43 import com.googlecode.android_scripting.Constants;
44 import com.googlecode.android_scripting.Log;
45 import com.googlecode.android_scripting.R;
46 import com.googlecode.android_scripting.facade.FacadeConfiguration;
47 import com.googlecode.android_scripting.interpreter.Interpreter;
48 import com.googlecode.android_scripting.interpreter.InterpreterConfiguration;
49 import com.googlecode.android_scripting.language.SupportedLanguages;
50 import com.googlecode.android_scripting.rpc.MethodDescriptor;
51 import com.googlecode.android_scripting.rpc.ParameterDescriptor;
52 import com.googlecode.android_scripting.rpc.RpcDeprecated;
53 import com.googlecode.android_scripting.rpc.RpcMinSdk;
54 
55 import java.lang.reflect.Method;
56 import java.util.HashSet;
57 import java.util.List;
58 import java.util.Set;
59 
60 public class ApiBrowser extends ListActivity {
61 
62   private boolean searchResultMode = false;
63 
64   private static enum RequestCode {
65     RPC_PROMPT
66   }
67 
68   private static enum MenuId {
69     EXPAND_ALL, COLLAPSE_ALL, SEARCH;
getId()70     public int getId() {
71       return ordinal() + Menu.FIRST;
72     }
73   }
74 
75   private static enum ContextMenuId {
76     INSERT_TEXT, PROMPT_PARAMETERS, HELP;
getId()77     public int getId() {
78       return ordinal() + Menu.FIRST;
79     }
80   }
81 
82   private List<MethodDescriptor> mMethodDescriptors;
83   private Set<Integer> mExpandedPositions;
84   private ApiBrowserAdapter mAdapter;
85   private boolean mIsLanguageSupported;
86 
87   @Override
onCreate(Bundle savedInstanceState)88   public void onCreate(Bundle savedInstanceState) {
89     super.onCreate(savedInstanceState);
90     CustomizeWindow.requestCustomTitle(this, "API Browser", R.layout.api_browser);
91     getListView().setFastScrollEnabled(true);
92     mExpandedPositions = new HashSet<Integer>();
93     updateAndFilterMethodDescriptors(null);
94     String scriptName = getIntent().getStringExtra(Constants.EXTRA_SCRIPT_PATH);
95     mIsLanguageSupported = SupportedLanguages.checkLanguageSupported(scriptName);
96     mAdapter = new ApiBrowserAdapter();
97     setListAdapter(mAdapter);
98     registerForContextMenu(getListView());
99     setResult(RESULT_CANCELED);
100   }
101 
updateAndFilterMethodDescriptors(final String query)102   private void updateAndFilterMethodDescriptors(final String query) {
103     mMethodDescriptors =
104         Lists.newArrayList(Collections2.filter(FacadeConfiguration.collectMethodDescriptors(),
105             new Predicate<MethodDescriptor>() {
106               @Override
107               public boolean apply(MethodDescriptor descriptor) {
108                 Method method = descriptor.getMethod();
109                 if (method.isAnnotationPresent(RpcDeprecated.class)) {
110                   return false;
111                 } else if (method.isAnnotationPresent(RpcMinSdk.class)) {
112                   int requiredSdkLevel = method.getAnnotation(RpcMinSdk.class).value();
113                   if (FacadeConfiguration.getSdkLevel() < requiredSdkLevel) {
114                     return false;
115                   }
116                 }
117                 if (query == null) {
118                   return true;
119                 }
120                 return descriptor.getName().toLowerCase().contains(query.toLowerCase());
121               }
122             }));
123   }
124 
125   @Override
onNewIntent(Intent intent)126   protected void onNewIntent(Intent intent) {
127     if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
128       searchResultMode = true;
129       final String query = intent.getStringExtra(SearchManager.QUERY);
130       ((TextView) findViewById(R.id.left_text)).setText(query);
131       updateAndFilterMethodDescriptors(query);
132       if (mMethodDescriptors.size() == 1) {
133         mExpandedPositions.add(0);
134       } else {
135         mExpandedPositions.clear();
136       }
137       mAdapter.notifyDataSetChanged();
138     }
139   }
140 
141   @Override
onKeyDown(int keyCode, KeyEvent event)142   public boolean onKeyDown(int keyCode, KeyEvent event) {
143     if (keyCode == KeyEvent.KEYCODE_BACK && searchResultMode) {
144       searchResultMode = false;
145       mExpandedPositions.clear();
146       ((TextView) findViewById(R.id.left_text)).setText("API Browser");
147       updateAndFilterMethodDescriptors("");
148       mAdapter.notifyDataSetChanged();
149       return true;
150     }
151     return super.onKeyDown(keyCode, event);
152   }
153 
154   @Override
onPrepareOptionsMenu(Menu menu)155   public boolean onPrepareOptionsMenu(Menu menu) {
156     super.onPrepareOptionsMenu(menu);
157     menu.clear();
158     menu.add(Menu.NONE, MenuId.EXPAND_ALL.getId(), Menu.NONE, "Expand All").setIcon(
159         android.R.drawable.ic_menu_add);
160     menu.add(Menu.NONE, MenuId.COLLAPSE_ALL.getId(), Menu.NONE, "Collapse All").setIcon(
161         android.R.drawable.ic_menu_close_clear_cancel);
162     menu.add(Menu.NONE, MenuId.SEARCH.getId(), Menu.NONE, "Search").setIcon(
163         R.drawable.ic_menu_search);
164     return true;
165   }
166 
167   @Override
onOptionsItemSelected(MenuItem item)168   public boolean onOptionsItemSelected(MenuItem item) {
169     super.onOptionsItemSelected(item);
170     int itemId = item.getItemId();
171     if (itemId == MenuId.EXPAND_ALL.getId()) {
172       for (int i = 0; i < mMethodDescriptors.size(); i++) {
173         mExpandedPositions.add(i);
174       }
175     } else if (itemId == MenuId.COLLAPSE_ALL.getId()) {
176       mExpandedPositions.clear();
177     } else if (itemId == MenuId.SEARCH.getId()) {
178       onSearchRequested();
179     }
180 
181     mAdapter.notifyDataSetInvalidated();
182     return true;
183   }
184 
185   @Override
onListItemClick(ListView l, View v, int position, long id)186   protected void onListItemClick(ListView l, View v, int position, long id) {
187     if (mExpandedPositions.contains(position)) {
188       mExpandedPositions.remove(position);
189     } else {
190       mExpandedPositions.add(position);
191     }
192     mAdapter.notifyDataSetInvalidated();
193   }
194 
195   @Override
onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo)196   public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
197     if (!mIsLanguageSupported) {
198       return;
199     }
200     menu.add(Menu.NONE, ContextMenuId.INSERT_TEXT.getId(), Menu.NONE, "Insert");
201     menu.add(Menu.NONE, ContextMenuId.PROMPT_PARAMETERS.getId(), Menu.NONE, "Prompt");
202   }
203 
204   @Override
onContextItemSelected(MenuItem item)205   public boolean onContextItemSelected(MenuItem item) {
206     AdapterView.AdapterContextMenuInfo info;
207     try {
208       info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
209     } catch (ClassCastException e) {
210       Log.e("Bad menuInfo", e);
211       return false;
212     }
213 
214     MethodDescriptor rpc = (MethodDescriptor) getListAdapter().getItem(info.position);
215     if (rpc == null) {
216       Log.v("No RPC selected.");
217       return false;
218     }
219 
220     if (item.getItemId() == ContextMenuId.INSERT_TEXT.getId()) {
221       // There's no activity to track calls to insert (like there is for prompt) so we track it
222       // here instead.
223       insertText(rpc, new String[0]);
224     } else if (item.getItemId() == ContextMenuId.PROMPT_PARAMETERS.getId()) {
225       Intent intent = new Intent(this, ApiPrompt.class);
226       intent.putExtra(Constants.EXTRA_API_PROMPT_RPC_NAME, rpc.getName());
227       ParameterDescriptor[] parameters = rpc.getParameterValues(new String[0]);
228       String[] values = new String[parameters.length];
229       int index = 0;
230       for (ParameterDescriptor parameter : parameters) {
231         values[index++] = parameter.getValue();
232       }
233       intent.putExtra(Constants.EXTRA_API_PROMPT_VALUES, values);
234       startActivityForResult(intent, RequestCode.RPC_PROMPT.ordinal());
235     } else if (item.getItemId() == ContextMenuId.HELP.getId()) {
236       String help = rpc.getDeclaringClass().getSimpleName() + ".html#" + rpc.getName();
237 
238     }
239     return true;
240   }
241 
242   @Override
onActivityResult(int requestCode, int resultCode, Intent data)243   protected void onActivityResult(int requestCode, int resultCode, Intent data) {
244     super.onActivityResult(requestCode, resultCode, data);
245     RequestCode request = RequestCode.values()[requestCode];
246     if (resultCode == RESULT_OK) {
247       switch (request) {
248       case RPC_PROMPT:
249         MethodDescriptor rpc =
250             FacadeConfiguration.getMethodDescriptor(data
251                 .getStringExtra(Constants.EXTRA_API_PROMPT_RPC_NAME));
252         String[] values = data.getStringArrayExtra(Constants.EXTRA_API_PROMPT_VALUES);
253         insertText(rpc, values);
254         break;
255       default:
256         break;
257       }
258     } else {
259       switch (request) {
260       case RPC_PROMPT:
261         break;
262       default:
263         break;
264       }
265     }
266   }
267 
insertText(MethodDescriptor rpc, String[] values)268   private void insertText(MethodDescriptor rpc, String[] values) {
269     String scriptText = getIntent().getStringExtra(Constants.EXTRA_SCRIPT_TEXT);
270     InterpreterConfiguration config =
271         ((BaseApplication) getApplication()).getInterpreterConfiguration();
272 
273     Interpreter interpreter =
274         config.getInterpreterByName(getIntent().getStringExtra(Constants.EXTRA_INTERPRETER_NAME));
275     String rpcHelpText = interpreter.getRpcText(scriptText, rpc, values);
276 
277     Intent intent = new Intent();
278     intent.putExtra(Constants.EXTRA_RPC_HELP_TEXT, rpcHelpText);
279     setResult(RESULT_OK, intent);
280     finish();
281   }
282 
283   private class ApiBrowserAdapter extends BaseAdapter implements SectionIndexer {
284 
285     private final AlphabetIndexer mIndexer;
286     private final MatrixCursor mCursor;
287 
ApiBrowserAdapter()288     public ApiBrowserAdapter() {
289       mCursor = new MatrixCursor(new String[] { "NAME" });
290       for (MethodDescriptor info : mMethodDescriptors) {
291         mCursor.addRow(new String[] { info.getName() });
292       }
293       mIndexer = new AlphabetIndexer(mCursor, 0, " ABCDEFGHIJKLMNOPQRSTUVWXYZ");
294     }
295 
296     @Override
getCount()297     public int getCount() {
298       return mMethodDescriptors.size();
299     }
300 
301     @Override
getItem(int position)302     public Object getItem(int position) {
303       return mMethodDescriptors.get(position);
304     }
305 
306     @Override
getItemId(int position)307     public long getItemId(int position) {
308       return position;
309     }
310 
311     @Override
getView(final int position, View convertView, ViewGroup parent)312     public View getView(final int position, View convertView, ViewGroup parent) {
313       TextView view;
314       if (convertView == null) {
315         view = new TextView(ApiBrowser.this);
316       } else {
317         view = (TextView) convertView;
318       }
319       view.setPadding(4, 4, 4, 4);
320       view.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
321       if (mExpandedPositions.contains(position)) {
322         view.setText(mMethodDescriptors.get(position).getHelp());
323       } else {
324         view.setText(mMethodDescriptors.get(position).getName());
325       }
326       return view;
327     }
328 
329     @Override
getPositionForSection(int section)330     public int getPositionForSection(int section) {
331       return mIndexer.getPositionForSection(section);
332     }
333 
334     @Override
getSectionForPosition(int position)335     public int getSectionForPosition(int position) {
336       return mIndexer.getSectionForPosition(position);
337     }
338 
339     @Override
getSections()340     public Object[] getSections() {
341       return mIndexer.getSections();
342     }
343   }
344 }
345