Home | History | Annotate | Download | only in activity
      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;
     70     public int getId() {
     71       return ordinal() + Menu.FIRST;
     72     }
     73   }
     74 
     75   private static enum ContextMenuId {
     76     INSERT_TEXT, PROMPT_PARAMETERS, HELP;
     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
     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 
    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
    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
    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
    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
    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
    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
    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
    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
    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 
    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 
    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
    297     public int getCount() {
    298       return mMethodDescriptors.size();
    299     }
    300 
    301     @Override
    302     public Object getItem(int position) {
    303       return mMethodDescriptors.get(position);
    304     }
    305 
    306     @Override
    307     public long getItemId(int position) {
    308       return position;
    309     }
    310 
    311     @Override
    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
    330     public int getPositionForSection(int section) {
    331       return mIndexer.getPositionForSection(section);
    332     }
    333 
    334     @Override
    335     public int getSectionForPosition(int position) {
    336       return mIndexer.getSectionForPosition(position);
    337     }
    338 
    339     @Override
    340     public Object[] getSections() {
    341       return mIndexer.getSections();
    342     }
    343   }
    344 }
    345