1 /*
2  * Copyright © 2012 Linaro Limited
3  *
4  * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
5  *
6  * glmark2 is free software: you can redistribute it and/or modify it under the
7  * terms of the GNU General Public License as published by the Free Software
8  * Foundation, either version 3 of the License, or (at your option) any later
9  * version.
10  *
11  * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
14  * details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * glmark2.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  * Authors:
20  *  Alexandros Frantzis
21  */
22 package org.linaro.glmark2;
23 
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.Comparator;
27 
28 import android.app.Activity;
29 import android.app.AlertDialog;
30 import android.app.Dialog;
31 import android.content.Context;
32 import android.content.DialogInterface;
33 import android.content.DialogInterface.OnDismissListener;
34 import android.content.Intent;
35 import android.graphics.Color;
36 import android.os.Bundle;
37 import android.os.Parcelable;
38 import android.text.SpannableString;
39 import android.text.style.ForegroundColorSpan;
40 import android.util.Log;
41 import android.view.inputmethod.EditorInfo;
42 import android.view.KeyEvent;
43 import android.view.LayoutInflater;
44 import android.view.View;
45 import android.view.ViewGroup;
46 import android.view.WindowManager;
47 import android.widget.AdapterView;
48 import android.widget.AdapterView.OnItemClickListener;
49 import android.widget.AdapterView.OnItemLongClickListener;
50 import android.widget.ArrayAdapter;
51 import android.widget.Button;
52 import android.widget.EditText;
53 import android.widget.ListView;
54 import android.widget.TextView;
55 import android.widget.TextView.OnEditorActionListener;
56 
57 public class EditorActivity extends Activity {
58     public static final int DIALOG_SCENE_NAME_ID = 0;
59     public static final int DIALOG_SCENE_OPTION_TEXT_ID = 1;
60     public static final int DIALOG_SCENE_OPTION_LIST_ID = 2;
61 
62     public static final int ITEM_POSITION_SCENE_NAME_HEADER = 0;
63     public static final int ITEM_POSITION_SCENE_NAME = 1;
64     public static final int ITEM_POSITION_SCENE_OPTION_HEADER = 2;
65     public static final int ITEM_POSITION_SCENE_OPTION = 3;
66 
67     private EditorItemAdapter adapter;
68     private ArrayList<SceneInfo> sceneInfoList;
69     private String[] sceneNames;
70 
71     @Override
onCreate(Bundle savedInstanceState)72     public void onCreate(Bundle savedInstanceState) {
73         super.onCreate(savedInstanceState);
74         setContentView(R.layout.activity_editor);
75 
76         /* Get information about the available scenes */
77         sceneInfoList = getSceneInfoList();
78         sceneNames = getSceneNames();
79 
80         /* Read information sent by the main activity */
81         final int benchmarkPos = this.getIntent().getIntExtra("benchmark-pos", 0);
82         String benchmarkText = getIntent().getStringExtra("benchmark-text");
83         if (benchmarkText.isEmpty())
84             benchmarkText = sceneNames[0];
85 
86         /* Set up the run button */
87         Button runButton = (Button) findViewById(R.id.runButton);
88         runButton.setOnClickListener(new View.OnClickListener() {
89             public void onClick(View v) {
90                 Intent intent = new Intent(EditorActivity.this, Glmark2Activity.class);
91                 String args = "-b \"" + getBenchmarkDescriptionText() + "\"";
92                 intent.putExtra("args", args);
93                 startActivity(intent);
94             }
95         });
96 
97         /* Set up the save button */
98         Button button = (Button) findViewById(R.id.saveButton);
99         button.setOnClickListener(new View.OnClickListener() {
100             public void onClick(View v) {
101                 String newBenchmarkText = getBenchmarkDescriptionText();
102                 Intent intent = new Intent();
103                 intent.putExtra("benchmark-text", newBenchmarkText);
104                 intent.putExtra("benchmark-pos", benchmarkPos);
105                 setResult(RESULT_OK, intent);
106                 finish();
107             }
108         });
109 
110         /* Set up list view */
111         ListView lv = (ListView) findViewById(R.id.editorListView);
112         adapter = new EditorItemAdapter(this, R.layout.list_item,
113                                         getEditorItemList(benchmarkText));
114         lv.setAdapter(adapter);
115 
116         lv.setOnItemClickListener(new OnItemClickListener() {
117             public void onItemClick(AdapterView<?> parentView, View childView, int position, long id) {
118                 Bundle bundle = new Bundle();
119                 bundle.putInt("item-pos", position);
120                 /* Show the right dialog, depending on the clicked list position */
121                 if (position == ITEM_POSITION_SCENE_NAME) {
122                     showDialog(DIALOG_SCENE_NAME_ID, bundle);
123                 }
124                 else if (position >= ITEM_POSITION_SCENE_OPTION) {
125                     String[] values = adapter.getItem(position).option.acceptableValues;
126                     if (values.length == 0)
127                         showDialog(DIALOG_SCENE_OPTION_TEXT_ID, bundle);
128                     else
129                         showDialog(DIALOG_SCENE_OPTION_LIST_ID, bundle);
130                 }
131             }
132         });
133 
134         lv.setOnItemLongClickListener(new OnItemLongClickListener() {
135             public boolean onItemLongClick(AdapterView<?> parentView, View childView, int position, long id) {
136                 /* Reset the value of the long-clicked option */
137                 if (position >= ITEM_POSITION_SCENE_OPTION) {
138                     EditorItem item = adapter.getItem(position);
139                     item.value = null;
140                     adapter.notifyDataSetChanged();
141                 }
142                 return true;
143             }
144         });
145     }
146 
147     @Override
onCreateDialog(int id, Bundle bundle)148     protected Dialog onCreateDialog(int id, Bundle bundle) {
149         final int itemPos = bundle.getInt("item-pos");
150         Dialog dialog;
151         final int finalId = id;
152 
153         switch (id) {
154             case DIALOG_SCENE_NAME_ID:
155                 {
156                 AlertDialog.Builder builder = new AlertDialog.Builder(this);
157                 builder.setTitle("Pick a scene");
158                 builder.setItems(sceneNames, new DialogInterface.OnClickListener() {
159                     public void onClick(DialogInterface dialog, int item) {
160                         adapter.clear();
161                         for (EditorItem ei: getEditorItemList(sceneNames[item]))
162                             adapter.add(ei);
163                         adapter.notifyDataSetChanged();
164                         dismissDialog(DIALOG_SCENE_NAME_ID);
165                     }
166                 });
167                 dialog = builder.create();
168                 }
169                 break;
170 
171             case DIALOG_SCENE_OPTION_TEXT_ID:
172                 {
173                 AlertDialog.Builder builder = new AlertDialog.Builder(this);
174                 final EditorItem item = adapter.getItem(itemPos);
175                 final EditText input = new EditText(this);
176                 input.setSingleLine(true);
177                 if (item.value != null)
178                     input.setText(item.value);
179 
180                 input.setOnEditorActionListener(new OnEditorActionListener() {
181                     public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
182                         if (actionId == EditorInfo.IME_ACTION_DONE ||
183                             (event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER &&
184                              event.getAction() == KeyEvent.ACTION_UP))
185                         {
186                             item.value = v.getText().toString();
187                             adapter.notifyDataSetChanged();
188                             dismissDialog(DIALOG_SCENE_OPTION_TEXT_ID);
189                         }
190                         return true;
191                     }
192                 });
193                 builder.setTitle(item.option.name + ": " + item.option.description);
194                 dialog = builder.create();
195                 ((AlertDialog)dialog).setView(input, 15, 6, 15, 6);
196                 dialog.getWindow().setSoftInputMode(
197                         WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN |
198                         WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
199                         );
200 
201                 }
202                 break;
203 
204             case DIALOG_SCENE_OPTION_LIST_ID:
205                 {
206                 AlertDialog.Builder builder = new AlertDialog.Builder(this);
207                 final EditorItem item = adapter.getItem(itemPos);
208                 builder.setTitle(item.option.name + ": " + item.option.description);
209 
210                 builder.setItems(item.option.acceptableValues, new DialogInterface.OnClickListener() {
211                     public void onClick(DialogInterface dialog, int index) {
212                         item.value = item.option.acceptableValues[index];
213                         adapter.notifyDataSetChanged();
214                         dismissDialog(DIALOG_SCENE_OPTION_LIST_ID);
215                     }
216                 });
217 
218                 dialog = builder.create();
219                 }
220                 break;
221 
222             default:
223                 dialog = null;
224                 break;
225         }
226 
227         if (dialog != null) {
228             dialog.setOnDismissListener(new OnDismissListener() {
229                 public void onDismiss(DialogInterface dialog) {
230                     removeDialog(finalId);
231                 }
232             });
233         }
234 
235         return dialog;
236     }
237 
238     /**
239      * Gets the value of an option.
240      *
241      * @param benchArray an array of option strings ("opt=val")
242      * @param opt the options to get the value of
243      *
244      * @return the value or null
245      */
getOptionValue(String[] benchArray, String opt)246     private String getOptionValue(String[] benchArray, String opt) {
247         String ret = null;
248 
249         /* Search from the end to the beginning */
250         for (int n = benchArray.length - 1; n >= 0; n--) {
251             String s = benchArray[n].trim();
252             if (s.startsWith(opt + "=")) {
253                 int i = s.indexOf('=');
254                 if (i >= 0 && i + 1 < s.length()) {
255                     ret = s.substring(i + 1).trim();
256                     break;
257                 }
258             }
259         }
260 
261         return ret;
262     }
263 
264     /**
265      * Gets the benchmark description string of the current editing state.
266      *
267      * @return the string
268      */
getBenchmarkDescriptionText()269     private String getBenchmarkDescriptionText() {
270         String ret = "";
271 
272         for (int i = 0; i < adapter.getCount(); i++) {
273             /* Convert each list item to a proper string representation */
274             EditorItem item = adapter.getItem(i);
275             if (item == null)
276                 continue;
277 
278             String s = "";
279 
280             /*
281              * Append "opt=" if this is an option item, except the
282              * "__custom__" item.
283              */
284             if (item.option != null && item.value != null &&
285                 !item.option.name.equals("__custom__"))
286             {
287                 s += item.option.name + "=";
288             }
289 
290             /*
291              * Append the item value if this is not "__custom__".
292              */
293             if (item.value != null && !item.value.equals("__custom__"))
294                 s += item.value;
295 
296             /*
297              * Append ":" to the description string if needed.
298              */
299             if (!s.isEmpty() && !ret.isEmpty())
300                 ret += ":";
301 
302             /* Append the item representation */
303             ret += s;
304         }
305 
306         return ret;
307     }
308 
309     /**
310      * Creates an EditorItem list from a benchmark description string.
311      *
312      * @param benchDesc the benchmark description string
313      *
314      * @return the list
315      */
getEditorItemList(String benchDesc)316     private ArrayList<EditorItem> getEditorItemList(String benchDesc) {
317         String[] benchArray = benchDesc.split(":");
318         String benchName = benchArray[0].trim();
319 
320         if (benchName.isEmpty())
321             benchName = "__custom__";
322 
323         /* Find SceneInfo from name */
324         SceneInfo sceneInfo = null;
325         for (SceneInfo si: sceneInfoList) {
326             if (si.name.equals(benchName)) {
327                 sceneInfo = si;
328                 break;
329             }
330         }
331 
332         /* If we couldn't find a matching SceneInfo, use __custom__ */
333         if (sceneInfo == null) {
334             for (SceneInfo si: sceneInfoList) {
335                 if (si.name.equals("__custom__")) {
336                     sceneInfo = si;
337                     break;
338                 }
339             }
340         }
341 
342         ArrayList<EditorItem> l = new ArrayList<EditorItem>();
343 
344         /* Append null item for Scene header */
345         l.add(null);
346 
347         /* Append scene name item */
348         l.add(new EditorItem(null, sceneInfo.name));
349 
350         /* Append null item for Options header */
351         l.add(null);
352 
353         /* Append items to the list */
354         if (!sceneInfo.name.equals("__custom__")) {
355             /* Append scene option items */
356             for (SceneInfo.Option opt: sceneInfo.options)
357                 l.add(new EditorItem(opt, getOptionValue(benchArray, opt.name)));
358         }
359         else {
360             String desc = new String(benchDesc);
361             if (desc.startsWith("__custom__"))
362                 desc = "";
363 
364             /* Append scene option items (only one for __custom__) */
365             for (SceneInfo.Option opt: sceneInfo.options)
366                 l.add(new EditorItem(opt, desc));
367         }
368 
369         return l;
370     }
371 
372     /**
373      * Gets a list of information about the available scenes.
374      *
375      * @return the list
376      */
getSceneInfoList()377     private ArrayList<SceneInfo> getSceneInfoList() {
378         ArrayList<SceneInfo> l = new ArrayList<SceneInfo>();
379         SceneInfo customSceneInfo = new SceneInfo("__custom__");
380         customSceneInfo.addOption("__custom__", "Custom benchmark string", "", new String[0]);
381 
382         for (Parcelable p: getIntent().getParcelableArrayExtra("scene-info"))
383             l.add((SceneInfo)p);
384 
385         /* Sort SceneInfo list by name */
386         Collections.sort(l, new Comparator<SceneInfo>() {
387             public int compare(SceneInfo s1, SceneInfo s2) {
388                 return s1.name.compareTo(s2.name);
389             }
390         });
391 
392         /* Add the "__custom__" SceneInfo */
393         l.add(customSceneInfo);
394 
395         return l;
396     }
397 
398     /**
399      * Gets the array of scene names.
400      *
401      * @return the array
402      */
getSceneNames()403     private String[] getSceneNames() {
404         ArrayList<String> l = new ArrayList<String>();
405 
406         for (SceneInfo si: sceneInfoList) {
407             if (!si.name.isEmpty())
408                 l.add(si.name);
409         }
410 
411         String[] a = new String[0];
412         return l.toArray(a);
413     }
414 
415 
416     static private class EditorItem {
417         SceneInfo.Option option;
418 
EditorItem(SceneInfo.Option o, String value)419         public EditorItem(SceneInfo.Option o, String value) {
420             this.option = o;
421             this.value = value;
422         }
423 
424         public String value;
425     }
426 
427     /**
428      * A ListView adapter that creates list item views from EditorItems
429      */
430     private class EditorItemAdapter extends ArrayAdapter<EditorItem> {
431         static final int VIEW_TYPE_HEADER = 0;
432         static final int VIEW_TYPE_SCENE_NAME = 1;
433         static final int VIEW_TYPE_SCENE_OPTION = 2;
434         static final int VIEW_TYPE_COUNT = 3;
435 
436         public ArrayList<EditorItem> items;
437 
EditorItemAdapter(Context context, int textViewResourceId, ArrayList<EditorItem> items)438         public EditorItemAdapter(Context context, int textViewResourceId,
439                                  ArrayList<EditorItem> items)
440         {
441             super(context, textViewResourceId, items);
442             this.items = items;
443         }
444 
445         @Override
isEnabled(int position)446         public boolean isEnabled(int position) {
447             return position == ITEM_POSITION_SCENE_NAME ||
448                    position >= ITEM_POSITION_SCENE_OPTION;
449         }
450 
451         @Override
getItemViewType(int position)452         public int getItemViewType(int position) {
453             if (position == ITEM_POSITION_SCENE_NAME)
454                 return VIEW_TYPE_SCENE_NAME;
455             else if (position >= ITEM_POSITION_SCENE_OPTION)
456                 return VIEW_TYPE_SCENE_OPTION;
457             else
458                 return VIEW_TYPE_HEADER;
459         }
460 
461         @Override
getViewTypeCount()462         public int getViewTypeCount() {
463             return VIEW_TYPE_COUNT;
464         }
465 
466         @Override
getView(int position, View convertView, ViewGroup parent)467         public View getView(int position, View convertView, ViewGroup parent) {
468             int viewType = getItemViewType(position);
469             View v = null;
470 
471             if (viewType == VIEW_TYPE_HEADER)
472                 v = getViewHeader(position, convertView);
473             else if (viewType == VIEW_TYPE_SCENE_NAME)
474                 v = getViewScene(position, convertView);
475             else if (viewType == VIEW_TYPE_SCENE_OPTION)
476                 v = getViewOption(position, convertView);
477 
478             return v;
479         }
480 
getViewHeader(int position, View convertView)481         private View getViewHeader(int position, View convertView) {
482             /* Get the view/widget to use */
483             View v = convertView;
484             if (v == null) {
485                 LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
486                 v = vi.inflate(R.layout.list_header, null);
487             }
488 
489             TextView tv = (TextView) v;
490 
491             if (position == ITEM_POSITION_SCENE_NAME_HEADER)
492                 tv.setText("Scene");
493             else if (position == ITEM_POSITION_SCENE_OPTION_HEADER)
494                 tv.setText("Options");
495 
496             return tv;
497         }
498 
getViewScene(int position, View convertView)499         private View getViewScene(int position, View convertView) {
500             /* Get the view/widget to use */
501             View v = convertView;
502             if (v == null) {
503                 LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
504                 v = vi.inflate(R.layout.list_item, null);
505             }
506 
507             EditorItem item = items.get(position);
508 
509             TextView title = (TextView) v.findViewById(R.id.title);
510             TextView summary = (TextView) v.findViewById(R.id.summary);
511 
512             if (title != null)
513                 title.setText(item.value);
514             if (summary != null)
515                 summary.setText("The scene to use");
516 
517             return v;
518         }
519 
getViewOption(int position, View convertView)520         private View getViewOption(int position, View convertView) {
521             /* Get the view/widget to use */
522             View v = convertView;
523             if (v == null) {
524                 LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
525                 v = vi.inflate(R.layout.list_item, null);
526             }
527 
528             EditorItem item = items.get(position);
529 
530             TextView title = (TextView) v.findViewById(R.id.title);
531             TextView summary = (TextView) v.findViewById(R.id.summary);
532             boolean hasUserSetValue = item.value != null;
533             String value = hasUserSetValue ? item.value : item.option.defaultValue;
534 
535             if (title != null) {
536                 /* If the option has been edited by the user show it with emphasis */
537                 SpannableString titleText = new SpannableString(item.option.name + " = " + value);
538                 ForegroundColorSpan span = new ForegroundColorSpan(hasUserSetValue ? Color.CYAN : Color.LTGRAY);
539                 titleText.setSpan(span, item.option.name.length() + " = ".length(), titleText.length(), 0);
540                 title.setText(titleText);
541             }
542 
543             if (summary != null)
544                 summary.setText(item.option.description);
545 
546             return v;
547         }
548     }
549 }
550