diff mbox

[Branch,~glmark2-dev/glmark2/trunk] Rev 241: Android: Add options menu and implement menu actions.

Message ID 20120727101212.17537.86471.launchpad@ackee.canonical.com
State Accepted
Headers show

Commit Message

alexandros.frantzis@linaro.org July 27, 2012, 10:12 a.m. UTC
Merge authors:
  Alexandros Frantzis (afrantzis)
------------------------------------------------------------
revno: 241 [merge]
committer: Alexandros Frantzis <alexandros.frantzis@linaro.org>
branch nick: trunk
timestamp: Fri 2012-07-27 12:55:54 +0300
message:
  Android: Add options menu and implement menu actions.
added:
  android/res/drawable/
  android/res/drawable/menu_about.png
  android/res/drawable/menu_delete.png
  android/res/drawable/menu_load.png
  android/res/drawable/menu_save.png
  android/res/drawable/menu_settings.png
  android/res/layout/activity_about.xml
  android/res/layout/save_dialog.xml
  android/res/menu/
  android/res/menu/main_options_menu.xml
  android/res/xml/
  android/res/xml/preferences.xml
  android/src/org/linaro/glmark2/AboutActivity.java
  android/src/org/linaro/glmark2/BenchmarkListManager.java
  android/src/org/linaro/glmark2/MainPreferencesActivity.java
modified:
  android/AndroidManifest.xml
  android/res/values/strings.xml
  android/src/org/linaro/glmark2/MainActivity.java


--
lp:glmark2
https://code.launchpad.net/~glmark2-dev/glmark2/trunk

You are subscribed to branch lp:glmark2.
To unsubscribe from this branch go to https://code.launchpad.net/~glmark2-dev/glmark2/trunk/+edit-subscription
diff mbox

Patch

=== modified file 'android/AndroidManifest.xml'
--- android/AndroidManifest.xml	2012-07-19 09:18:25 +0000
+++ android/AndroidManifest.xml	2012-07-26 11:29:49 +0000
@@ -27,8 +27,23 @@ 
                 <action android:name="android.intent.action.MAIN" />
             </intent-filter>
         </activity>
+        <activity android:label="@string/title_activity_preferences"
+                  android:name="org.linaro.glmark2.MainPreferencesActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+        <activity android:label="@string/title_activity_about"
+                  android:name="org.linaro.glmark2.AboutActivity"
+                  android:theme="@android:style/Theme.Dialog">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
     </application>
     <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-feature android:glEsVersion="0x00020000"/>
     <uses-sdk android:minSdkVersion="9"/>
 </manifest>

=== added directory 'android/res/drawable'
=== added file 'android/res/drawable/menu_about.png'
Binary files android/res/drawable/menu_about.png	1970-01-01 00:00:00 +0000 and android/res/drawable/menu_about.png	2012-07-24 12:52:49 +0000 differ
=== added file 'android/res/drawable/menu_delete.png'
Binary files android/res/drawable/menu_delete.png	1970-01-01 00:00:00 +0000 and android/res/drawable/menu_delete.png	2012-07-24 14:22:47 +0000 differ
=== added file 'android/res/drawable/menu_load.png'
Binary files android/res/drawable/menu_load.png	1970-01-01 00:00:00 +0000 and android/res/drawable/menu_load.png	2012-07-24 12:52:49 +0000 differ
=== added file 'android/res/drawable/menu_save.png'
Binary files android/res/drawable/menu_save.png	1970-01-01 00:00:00 +0000 and android/res/drawable/menu_save.png	2012-07-24 12:52:49 +0000 differ
=== added file 'android/res/drawable/menu_settings.png'
Binary files android/res/drawable/menu_settings.png	1970-01-01 00:00:00 +0000 and android/res/drawable/menu_settings.png	2012-07-26 09:18:27 +0000 differ
=== added file 'android/res/layout/activity_about.xml'
--- android/res/layout/activity_about.xml	1970-01-01 00:00:00 +0000
+++ android/res/layout/activity_about.xml	2012-07-26 11:29:49 +0000
@@ -0,0 +1,43 @@ 
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical"
+              android:padding="10dp">
+
+    <TextView android:id="@+id/name_version"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:gravity="center_horizontal"
+              android:text="@string/about_name_version_format" />
+
+    <TextView android:id="@+id/description"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:gravity="center_horizontal"
+              android:text="@string/about_description" />
+
+    <TextView android:id="@+id/copyright"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:gravity="center_horizontal"
+              android:text="@string/about_copyright" />
+
+    <TextView android:id="@+id/url"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:gravity="center_horizontal"
+              android:text="@string/about_url" />
+
+    <TextView android:id="@+id/license1"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:gravity="center_horizontal"
+              android:text="@string/about_license_1" />
+
+    <TextView android:id="@+id/license2"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:gravity="center_horizontal"
+              android:text="@string/about_license_2" />
+</LinearLayout>
+

=== added file 'android/res/layout/save_dialog.xml'
--- android/res/layout/save_dialog.xml	1970-01-01 00:00:00 +0000
+++ android/res/layout/save_dialog.xml	2012-07-24 13:04:45 +0000
@@ -0,0 +1,28 @@ 
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="center_vertical" >
+
+    <RelativeLayout android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginLeft="15dip"
+                    android:layout_marginRight="15dip"
+                    android:layout_marginTop="6dip"
+                    android:layout_marginBottom="6dip"
+                    android:layout_weight="1">
+
+        <EditText android:id="@+id/listName"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content" />
+
+        <CheckBox android:id="@+id/external"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:layout_alignLeft="@id/listName"
+                  android:layout_below="@id/listName"
+                  android:text="@string/externalSaveDialogText"/>
+
+    </RelativeLayout>
+
+</LinearLayout>

=== added directory 'android/res/menu'
=== added file 'android/res/menu/main_options_menu.xml'
--- android/res/menu/main_options_menu.xml	1970-01-01 00:00:00 +0000
+++ android/res/menu/main_options_menu.xml	2012-07-26 09:18:27 +0000
@@ -0,0 +1,22 @@ 
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/save_benchmark_list"
+          android:title="@string/saveMainOptionsText"
+          android:icon="@drawable/menu_save" />
+
+    <item android:id="@+id/load_benchmark_list"
+          android:title="@string/loadMainOptionsText"
+          android:icon="@drawable/menu_load" />
+
+    <item android:id="@+id/delete_benchmark_list"
+          android:title="@string/deleteMainOptionsText"
+          android:icon="@drawable/menu_delete" />
+
+    <item android:id="@+id/settings"
+          android:title="@string/settingsMainOptionsText"
+          android:icon="@drawable/menu_settings" />
+
+    <item android:id="@+id/about"
+          android:title="@string/aboutMainOptionsText"
+          android:icon="@drawable/menu_about" />
+</menu>

=== modified file 'android/res/values/strings.xml'
--- android/res/values/strings.xml	2012-07-19 09:18:06 +0000
+++ android/res/values/strings.xml	2012-07-26 11:29:49 +0000
@@ -3,7 +3,38 @@ 
     <string name="app_name">GLMark2</string>
     <string name="title_activity_main">GLMark2</string>
     <string name="title_activity_editor">GLMark2 Benchmark Editor</string>
+    <string name="title_activity_preferences">GLMark2 Settings</string>
+    <string name="title_activity_about">About GLMark2</string>
     <string name="title_activity_glmark2">GLMark2</string>
     <string name="runButtonText">Run</string>
     <string name="saveButtonText">Save</string>
+
+    <string name="saveMainOptionsText">Save list</string>
+    <string name="loadMainOptionsText">Load list</string>
+    <string name="deleteMainOptionsText">Delete list</string>
+    <string name="settingsMainOptionsText">Settings</string>
+    <string name="aboutMainOptionsText">About</string>
+
+    <string name="externalSaveDialogText">Save to external storage</string>
+
+    <string name="runForeverPreferenceTitle">Run forever</string>
+    <string name="runForeverPreferenceSummary">Run indefinitely, looping from the last benchmark back to the first</string>
+
+    <string name="about_name_version_format">GLMark2 %1$s</string>
+    <string name="about_description">OpenGL (ES) 2.0 benchmark suite</string>
+    <string name="about_copyright">Copyright © 2010-2012 Linaro Limited</string>
+    <string name="about_url">http://launchpad.net/glmark2</string>
+    <string name="about_license_1">
+        glmark2 is free software: you can redistribute it and/or modify it
+        under the terms of the GNU General Public License as published by the
+        Free Software Foundation, either version 3 of the License, or (at your
+        option) any later version
+    </string>
+    <string name="about_license_2">
+        glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+        WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+        FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+        details.
+    </string>
+
 </resources>

=== added directory 'android/res/xml'
=== added file 'android/res/xml/preferences.xml'
--- android/res/xml/preferences.xml	1970-01-01 00:00:00 +0000
+++ android/res/xml/preferences.xml	2012-07-26 09:18:27 +0000
@@ -0,0 +1,7 @@ 
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+    <CheckBoxPreference android:key="run_forever"
+                        android:title="@string/runForeverPreferenceTitle"
+                        android:summary="@string/runForeverPreferenceSummary"
+                        android:defaultValue="false" />
+</PreferenceScreen>

=== added file 'android/src/org/linaro/glmark2/AboutActivity.java'
--- android/src/org/linaro/glmark2/AboutActivity.java	1970-01-01 00:00:00 +0000
+++ android/src/org/linaro/glmark2/AboutActivity.java	2012-07-26 11:29:49 +0000
@@ -0,0 +1,52 @@ 
+/*
+ * Copyright © 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Alexandros Frantzis
+ */
+package org.linaro.glmark2;
+
+import android.app.Activity;
+import android.content.pm.PackageInfo;
+import android.os.Bundle;
+import android.view.Window;
+import android.widget.TextView;
+
+public class AboutActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        requestWindowFeature(Window.FEATURE_NO_TITLE);
+        setContentView(R.layout.activity_about);
+
+        /* Get the application version */
+        String versionName = "?";
+
+        try {
+            PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 0);
+            versionName = info.versionName;
+        }
+        catch (Exception e) {
+        }
+
+        /* Display the application version */
+        TextView tv = (TextView) findViewById(R.id.name_version);
+        String formatString = getString(R.string.about_name_version_format);
+        tv.setText(String.format(formatString, versionName));
+    }
+}

=== added file 'android/src/org/linaro/glmark2/BenchmarkListManager.java'
--- android/src/org/linaro/glmark2/BenchmarkListManager.java	1970-01-01 00:00:00 +0000
+++ android/src/org/linaro/glmark2/BenchmarkListManager.java	2012-07-26 12:13:44 +0000
@@ -0,0 +1,189 @@ 
+/*
+ * Copyright © 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Alexandros Frantzis
+ */
+package org.linaro.glmark2;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.io.*;
+
+import android.app.Activity;
+import android.os.Environment;
+
+class BenchmarkListManager {
+
+    private ArrayList<String> benchmarks;
+    private Activity activity;
+
+    BenchmarkListManager(Activity activity, ArrayList<String> benchmarks)
+    {
+        this.activity = activity;
+        if (benchmarks == null) {
+            this.benchmarks = new ArrayList<String>();
+            this.benchmarks.add("Add benchmark...");
+        }
+        else {
+            this.benchmarks = benchmarks;
+        }
+    }
+
+    /** 
+     * Gets the list holding the benchmarks.
+     *
+     * The reference to this list is constant for the life of
+     * the BenchmarkListManager,
+     * 
+     * @return the operation error code
+     */
+    ArrayList<String> getBenchmarkList() {
+        return benchmarks;
+    }
+
+    /** 
+     * Gets the saved benchmark lists.
+     * 
+     * Each list name is prefixed with either "internal/" or "external/"
+     * to denote in which storage area it is saved in.
+     * 
+     * @return an array containing the saved list names
+     */
+     String[] getSavedLists() {
+        File externalPath = getSavedListPath(true);
+        File internalPath = getSavedListPath(false);
+        ArrayList<String> lists = new ArrayList<String>();
+
+        if (externalPath != null && externalPath.isDirectory()) {
+            for (File f: externalPath.listFiles())
+                lists.add("external/" + f.getName());
+        }
+
+        if (internalPath != null && internalPath.isDirectory()) {
+            for (File f: internalPath.listFiles())
+                lists.add("internal/" + f.getName());
+        }
+
+        Collections.sort(lists);
+
+        String[] a = new String[0];
+        return lists.toArray(a);
+    }
+
+    /** 
+     * Saves the current benchmark list to a file.
+     * 
+     * @param listName the list filename
+     * @param external whether the file is to be stored in external storage
+     */
+    void saveBenchmarkList(String listName, boolean external) throws Exception {
+        File listPath = getSavedListPath(external);
+        if (listPath == null)
+            throw new Exception("External storage not present");
+
+        listPath.mkdirs();
+
+        File f = new File(listPath, listName);
+
+        BufferedWriter out = new BufferedWriter(new FileWriter(f));
+        try {
+            for (int i = 0; i < benchmarks.size() - 1; i++) {
+                out.write(benchmarks.get(i));
+                out.newLine();
+            }
+        }
+        catch (Exception ex) {
+            throw ex;
+        }
+        finally {
+            out.close();
+        }
+    }
+
+    /** 
+     * Loads a benchmark list from a file.
+     * 
+     * @param listName the list filename
+     * @param external whether the file is stored in external storage
+     */
+    void loadBenchmarkList(String listName, boolean external) throws Exception {
+        /* Get the list file path */
+        File listPath = getSavedListPath(external);
+        if (listPath == null)
+            throw new Exception("External storage not present");
+
+        File f = new File(listPath, listName);
+
+        ArrayList<String> newBenchmarks = new ArrayList<String>();
+
+        /* Read benchmarks from file */
+        BufferedReader reader = new BufferedReader(new FileReader(f));
+        String line = null;
+
+        while ((line = reader.readLine()) != null)
+            newBenchmarks.add(line);
+
+        /* If everything went well, replace current benchmarks */
+        benchmarks.clear();
+        benchmarks.addAll(newBenchmarks);
+        benchmarks.add("Add benchmark...");
+    }
+
+    /** 
+     * Delete a benchmark list file.
+     * 
+     * @param listName the list filename
+     * @param external whether the file is stored in external storage
+     */
+    void deleteBenchmarkList(String listName, boolean external) throws Exception {
+        /* Get the list file path */
+        File listPath = getSavedListPath(external);
+        if (listPath == null)
+            throw new Exception("External storage not present");
+
+        File f = new File(listPath, listName);
+        f.delete();
+    }
+
+    /** 
+     * Gets the path where benchmark lists are saved in.
+     * 
+     * @param external whether to get the path for external storage
+     * 
+     * @return the saved list path
+     */
+    private File getSavedListPath(boolean external) {
+        File f = null;
+
+        if (external) {
+            String state = Environment.getExternalStorageState();
+            if (!Environment.MEDIA_MOUNTED.equals(state))
+                return null;
+            f = activity.getExternalFilesDir(null);
+        }
+        else {
+            f = activity.getFilesDir();
+        }
+
+        if (f != null)
+            f = new File(f, "lists");
+
+        return f;
+    }
+}

=== modified file 'android/src/org/linaro/glmark2/MainActivity.java'
--- android/src/org/linaro/glmark2/MainActivity.java	2012-07-06 13:31:01 +0000
+++ android/src/org/linaro/glmark2/MainActivity.java	2012-07-26 12:13:44 +0000
@@ -31,21 +31,36 @@ 
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnDismissListener;
 import android.content.Intent;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
 import android.widget.BaseAdapter;
 import android.widget.ArrayAdapter;
 import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.Button;
+import android.widget.EditText;
+import android.widget.CheckBox;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.inputmethod.EditorInfo;
 import android.util.Log;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.AdapterView.OnItemLongClickListener;
 import android.widget.AdapterView;
+import android.widget.TextView.OnEditorActionListener;
 
 public class MainActivity extends Activity {
-    public static final int DIALOG_BENCHMARK_ACTIONS_ID = 0;
+    public static final int DIALOG_ERROR_ID = 0;
+    public static final int DIALOG_BENCHMARK_ACTIONS_ID = 1;
+    public static final int DIALOG_SAVE_LIST_ID = 2;
+    public static final int DIALOG_LOAD_LIST_ID = 3;
+    public static final int DIALOG_DELETE_LIST_ID = 4;
 
     /**
      * The supported benchmark item actions.
@@ -54,9 +69,9 @@ 
         EDIT, DELETE, CLONE, MOVEUP, MOVEDOWN
     }
 
-    ArrayList<String> benchmarks;
     BaseAdapter adapter;
     SceneInfo[] sceneInfoList;
+    BenchmarkListManager benchmarkListManager;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -73,7 +88,7 @@ 
     @Override
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
-        outState.putStringArrayList("benchmarks", benchmarks);
+        outState.putStringArrayList("benchmarks", benchmarkListManager.getBenchmarkList());
     }
 
     @Override
@@ -83,13 +98,25 @@ 
                 BenchmarkItemAction.DELETE, BenchmarkItemAction.CLONE,
                 BenchmarkItemAction.MOVEUP, BenchmarkItemAction.MOVEDOWN
         };
-        final int benchmarkPos = bundle.getInt("benchmark-pos");
         final int finalId = id;
 
         Dialog dialog;
 
         switch (id) {
+            case DIALOG_ERROR_ID:
+                {
+                AlertDialog.Builder builder = new AlertDialog.Builder(this);
+                builder.setMessage(bundle.getString("message") + ": " +
+                                   bundle.getString("detail"));
+                builder.setCancelable(false);
+                builder.setPositiveButton("OK", null);
+                dialog = builder.create();
+                }
+                break;
+
             case DIALOG_BENCHMARK_ACTIONS_ID:
+                {
+                final int benchmarkPos = bundle.getInt("benchmark-pos");
                 AlertDialog.Builder builder = new AlertDialog.Builder(this);
                 builder.setTitle("Pick an action");
                 builder.setItems(benchmarkActions, new DialogInterface.OnClickListener() {
@@ -99,6 +126,100 @@ 
                     }
                 });
                 dialog = builder.create();
+                }
+                break;
+
+            case DIALOG_SAVE_LIST_ID:
+                {
+                AlertDialog.Builder builder = new AlertDialog.Builder(this);
+                View layout = getLayoutInflater().inflate(R.layout.save_dialog, null);
+                final EditText input = (EditText) layout.findViewById(R.id.listName);
+                final CheckBox checkBox = (CheckBox) layout.findViewById(R.id.external);
+
+                input.setOnEditorActionListener(new OnEditorActionListener() {
+                    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+                        if (actionId == EditorInfo.IME_ACTION_DONE ||
+                            (event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER &&
+                             event.getAction() == KeyEvent.ACTION_UP))
+                        {
+                            String listName = v.getText().toString();
+                            try {
+                                benchmarkListManager.saveBenchmarkList(listName,
+                                                                       checkBox.isChecked());
+                            }
+                            catch (Exception ex) {
+                                Bundle bundle = new Bundle();
+                                bundle.putString("message", "Cannot save list to file " + listName);
+                                bundle.putString("detail", ex.getMessage());
+                                showDialog(DIALOG_ERROR_ID, bundle);
+                            }
+                            dismissDialog(DIALOG_SAVE_LIST_ID);
+                        }
+                        return true;
+                    }
+                });
+
+                builder.setTitle("Save list as");
+                builder.setView(layout);
+
+                dialog = builder.create();
+                dialog.getWindow().setSoftInputMode(
+                        WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN |
+                        WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
+                        );
+                }
+                break;
+
+            case DIALOG_LOAD_LIST_ID:
+            case DIALOG_DELETE_LIST_ID:
+                {
+                AlertDialog.Builder builder = new AlertDialog.Builder(this);
+                if (id == DIALOG_LOAD_LIST_ID)
+                    builder.setTitle("Load list");
+                else
+                    builder.setTitle("Delete list");
+                final String[] savedLists = benchmarkListManager.getSavedLists();
+
+                builder.setItems(savedLists, new DialogInterface.OnClickListener() {
+                    public void onClick(DialogInterface dialog, int index) {
+                        String desc = savedLists[index];
+                        String filename = "";
+                        boolean external = false;
+
+                        if (desc.startsWith("internal/")) {
+                            filename = desc.replace("internal/", "");
+                            external = false;
+                        }
+                        else if (desc.startsWith("external/")) {
+                            filename = desc.replace("external/", "");
+                            external = true;
+                        }
+                            
+                        try {
+                            if (finalId == DIALOG_LOAD_LIST_ID) {
+                                benchmarkListManager.loadBenchmarkList(filename, external);
+                                adapter.notifyDataSetChanged();
+                            }
+                            else {
+                                benchmarkListManager.deleteBenchmarkList(filename, external);
+                            }
+
+                        }
+                        catch (Exception ex) {
+                            Bundle bundle = new Bundle();
+                            if (finalId == DIALOG_LOAD_LIST_ID)
+                                bundle.putString("message", "Cannot load list " + desc);
+                            else
+                                bundle.putString("message", "Cannot delete list " + desc);
+                            bundle.putString("detail", ex.getMessage());
+                            showDialog(DIALOG_ERROR_ID, bundle);
+                        }
+                        dismissDialog(finalId);
+                    }
+                });
+
+                dialog = builder.create();
+                }
                 break;
 
             default:
@@ -118,6 +239,46 @@ 
     }
 
     @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.main_options_menu, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        boolean ret = true;
+
+        switch (item.getItemId()) {
+            case R.id.save_benchmark_list:
+                showDialog(DIALOG_SAVE_LIST_ID);
+                ret = true;
+                break;
+            case R.id.load_benchmark_list:
+                showDialog(DIALOG_LOAD_LIST_ID);
+                ret = true;
+                break;
+            case R.id.delete_benchmark_list:
+                showDialog(DIALOG_DELETE_LIST_ID);
+                ret = true;
+                break;
+            case R.id.settings:
+                startActivity(new Intent(MainActivity.this, MainPreferencesActivity.class));
+                ret = true;
+                break;
+            case R.id.about:
+                startActivity(new Intent(MainActivity.this, AboutActivity.class));
+                ret = true;
+                break;
+            default:
+                ret = super.onOptionsItemSelected(item);
+                break;
+        }
+
+        return ret;
+    }
+
+    @Override
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
         if (resultCode == RESULT_OK) {
             String benchmarkText = data.getStringExtra("benchmark-text");
@@ -133,14 +294,9 @@ 
      */
     private void init(ArrayList<String> savedBenchmarks)
     {
-        /* Fill in the benchmark list */
-        if (savedBenchmarks == null) {
-            benchmarks = new ArrayList<String>();
-            benchmarks.add("Add benchmark...");
-        }
-        else {
-            benchmarks = savedBenchmarks;
-        }
+        /* Initialize benchmark list manager */
+        benchmarkListManager = new BenchmarkListManager(this, savedBenchmarks);
+        final ArrayList<String> benchmarks = benchmarkListManager.getBenchmarkList();
 
         /* Get Scene information */
         sceneInfoList = Glmark2Native.getSceneInfo(getAssets());
@@ -149,10 +305,13 @@ 
         Button button = (Button) findViewById(R.id.runButton);
         button.setOnClickListener(new View.OnClickListener() {
             public void onClick(View v) {
+                SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
                 Intent intent = new Intent(MainActivity.this, Glmark2Activity.class);
                 String args = "";
                 for (int i = 0; i < benchmarks.size() - 1; i++)
                     args += "-b " + benchmarks.get(i) + " ";
+                if (prefs.getBoolean("run_forever", false))
+                    args += "--run-forever ";
                 if (!args.isEmpty())
                     intent.putExtra("args", args);
                 startActivity(intent);
@@ -200,6 +359,7 @@ 
     private void doBenchmarkItemAction(int position, BenchmarkItemAction action, String data)
     {
         int scrollPosition = position;
+        final ArrayList<String> benchmarks = benchmarkListManager.getBenchmarkList();
 
         switch(action) {
             case EDIT:

=== added file 'android/src/org/linaro/glmark2/MainPreferencesActivity.java'
--- android/src/org/linaro/glmark2/MainPreferencesActivity.java	1970-01-01 00:00:00 +0000
+++ android/src/org/linaro/glmark2/MainPreferencesActivity.java	2012-07-26 12:17:29 +0000
@@ -0,0 +1,33 @@ 
+/*
+ * Copyright © 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Alexandros Frantzis
+ */
+package org.linaro.glmark2;
+ 
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+ 
+public class MainPreferencesActivity extends PreferenceActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        addPreferencesFromResource(R.xml.preferences);
+    }
+}