• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.skia.viewer;
2 
3 import android.text.Editable;
4 import android.text.TextWatcher;
5 import android.view.LayoutInflater;
6 import android.view.SurfaceView;
7 import android.view.View;
8 import android.view.ViewGroup;
9 import android.widget.AdapterView;
10 import android.widget.BaseAdapter;
11 import android.widget.CompoundButton;
12 import android.widget.EditText;
13 import android.widget.LinearLayout;
14 import android.widget.Spinner;
15 import android.widget.Switch;
16 import android.widget.TextView;
17 
18 import org.json.JSONArray;
19 import org.json.JSONException;
20 import org.json.JSONObject;
21 
22 import java.util.ArrayList;
23 
24 /*
25     The navigation drawer requires ListView, so we implemented this BaseAdapter for that ListView.
26     However, the ListView does not provide good support for updating just a single child view.
27     For example, a frequently changed child view such as FPS state will reset the spinner of
28     all other child views; although I didn't change other child views and directly return
29     the convertView in BaseAdapter.getView(int position, View convertView, ViewGroup parent).
30 
31     Therefore, our adapter only returns one LinearLayout for the ListView.
32     Within that LinearLayout, we maintain views ourselves so we can efficiently update its children.
33  */
34 public class StateAdapter extends BaseAdapter implements AdapterView.OnItemSelectedListener {
35     private static final String NAME = "name";
36     private static final String VALUE = "value";
37     private static final String OPTIONS = "options";
38     private static final String BACKEND_STATE_NAME = "Backend";
39     private static final String REFRESH_STATE_NAME = "Refresh";
40     private static final String ON = "ON";
41     private static final String OFF = "OFF";
42     private static final int FILTER_LENGTH = 20;
43 
44     private ViewerActivity mViewerActivity;
45     private LinearLayout mLayout;
46     private JSONArray mStateJson;
47 
StateAdapter(ViewerActivity viewerActivity)48     public StateAdapter(ViewerActivity viewerActivity) {
49         mViewerActivity = viewerActivity;
50         try {
51             mStateJson = new JSONArray("[{\"name\": \"Please\", " +
52                     "\"value\": \"Initialize\", \"options\": []}]");
53         } catch (JSONException e) {
54             e.printStackTrace();
55         }
56     }
57 
setState(String stateJson)58     public void setState(String stateJson) {
59         try {
60             mStateJson = new JSONArray(stateJson);
61             if (mLayout != null) {
62                 updateDrawer();
63             } else {
64                 notifyDataSetChanged();
65             }
66         } catch (JSONException e) {
67             e.printStackTrace();
68         }
69     }
70 
71     // The first list item is the mLayout that contains a list of state items
72     @Override
getCount()73     public int getCount() {
74         return 1;
75     }
76 
77     @Override
getItem(int position)78     public Object getItem(int position) {
79         return null;
80     }
81 
82     @Override
getItemId(int position)83     public long getItemId(int position) {
84         return 0;
85     }
86 
87     @Override
getView(int position, View convertView, ViewGroup parent)88     public View getView(int position, View convertView, ViewGroup parent) {
89         switch (position) {
90             case 0: {
91                 if (mLayout == null) {
92                     mLayout = new LinearLayout(mViewerActivity);
93                     mLayout.setOrientation(LinearLayout.VERTICAL);
94                     updateDrawer();
95                 }
96                 return mLayout;
97             }
98             default: {
99                 return null;
100             }
101         }
102     }
103 
populateView(JSONObject item, View view)104     private void populateView(JSONObject item, View view) throws JSONException {
105         LinearLayout itemView = (LinearLayout) view;
106         TextView nameText = (TextView) itemView.findViewById(R.id.nameText);
107         TextView valueText = (TextView) itemView.findViewById(R.id.valueText);
108         Spinner optionSpinner = (Spinner) itemView.findViewById(R.id.optionSpinner);
109 
110         String value = item.getString(VALUE);
111         itemView.setTag(item.toString()); // To save unnecessary view update
112         itemView.setTag(R.integer.value_tag_key, value);
113 
114         nameText.setText(item.getString(NAME));
115 
116         JSONArray options = item.getJSONArray(OPTIONS);
117         if (options.length() == 0) {
118             valueText.setText(value);
119             valueText.setVisibility(View.VISIBLE);
120             optionSpinner.setVisibility(View.GONE);
121         } else {
122             ArrayList<String> optionList = new ArrayList<>();
123             String[] optionStrings = new String[options.length()];
124             for (int j = 0; j < options.length(); j++) {
125                 optionList.add(options.getString(j));
126             }
127             final OptionAdapter adapter = new OptionAdapter(mViewerActivity,
128                     android.R.layout.simple_spinner_dropdown_item, optionList, optionSpinner);
129             adapter.setCurrentOption(value);
130             optionSpinner.setAdapter(adapter);
131             if (optionStrings.length >= FILTER_LENGTH) {
132                 View existingView = itemView.getChildAt(1);
133                 if (!(existingView instanceof EditText)) {
134                     EditText filterText = new EditText(mViewerActivity);
135                     filterText.setHint("Filter");
136                     itemView.addView(filterText, 1);
137                     filterText.addTextChangedListener(new TextWatcher() {
138                         @Override
139                         public void beforeTextChanged(CharSequence s, int start, int cnt,
140                                 int after) {
141                         }
142 
143                         @Override
144                         public void onTextChanged(CharSequence s, int start, int before, int cnt) {
145                         }
146 
147                         @Override
148                         public void afterTextChanged(Editable s) {
149                             adapter.getFilter().filter(s.toString());
150                         }
151                     });
152                 }
153             }
154             optionSpinner.setSelection(optionList.indexOf(value));
155             optionSpinner.setOnItemSelectedListener(this);
156             optionSpinner.setVisibility(View.VISIBLE);
157             valueText.setVisibility(View.GONE);
158         }
159     }
inflateItemView(JSONObject item)160     private View inflateItemView(JSONObject item) throws JSONException {
161         LinearLayout itemView = (LinearLayout)
162                 LayoutInflater.from(mViewerActivity).inflate(R.layout.state_item, null);
163         populateView(item, itemView);
164         return itemView;
165     }
166 
updateDrawer()167     private void updateDrawer() {
168         try {
169             if (mStateJson.length() < mLayout.getChildCount()) {
170                 mLayout.removeViews(
171                         mStateJson.length(), mLayout.getChildCount() - mStateJson.length());
172             }
173             for (int i = 0; i < mStateJson.length(); i++) {
174                 JSONObject stateObject = mStateJson.getJSONObject(i);
175                 View childView = mLayout.getChildAt(i);
176                 if (childView != null) {
177                     if (stateObject.toString().equals(childView.getTag())) {
178                         continue; // No update, reuse the old view and skip the remaining step
179                     } else {
180                         populateView(stateObject, childView);
181                     }
182                 } else {
183                     mLayout.addView(inflateItemView(stateObject), i);
184                 }
185             }
186         } catch (JSONException e) {
187             e.printStackTrace();
188         }
189     }
190 
191     @Override
onItemSelected(AdapterView<?> parent, View view, int position, long id)192     public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
193         if (view == null) {
194             return;
195         }
196         View stateItem = (View) parent.getParent();
197         String stateName = ((TextView) stateItem.findViewById(R.id.nameText)).getText().toString();
198         String stateValue = ((TextView) view).getText().toString();
199         if (!stateValue.equals(stateItem.getTag(R.integer.value_tag_key))) {
200             stateItem.setTag(null); // Reset the tag to let updateDrawer update this item view.
201             mViewerActivity.onStateChanged(stateName, stateValue);
202         }
203 
204         // Due to the current Android limitation, we're required to recreate the SurfaceView for
205         // switching to/from the Raster backend.
206         // (Although we can switch between GPU backend without recreating the SurfaceView.)
207         final Object oldValue = stateItem.getTag(R.integer.value_tag_key);
208         if (stateName.equals(BACKEND_STATE_NAME)
209                 && oldValue != null && !stateValue.equals(oldValue)) {
210             LinearLayout mainLayout = (LinearLayout) mViewerActivity.findViewById(R.id.mainLayout);
211             mainLayout.removeAllViews();
212             SurfaceView surfaceView = new SurfaceView(mViewerActivity);
213             surfaceView.setId(R.id.surfaceView);
214             surfaceView.getHolder().addCallback(mViewerActivity);
215             surfaceView.setOnTouchListener(mViewerActivity);
216             mainLayout.addView(surfaceView);
217         }
218     }
219 
220     @Override
onNothingSelected(AdapterView<?> parent)221     public void onNothingSelected(AdapterView<?> parent) {
222         // do nothing
223     }
224 }
225