← Back to team overview

openlp-android team mailing list archive

[Merge] lp:~johanmynhardt/openlp/android into lp:openlp/android

 

Johan Mynhardt has proposed merging lp:~johanmynhardt/openlp/android into lp:openlp/android.

Requested reviews:
  Tim Bentley (trb143)
  Jonathan Corwin (j-corwin)

For more details, see:
https://code.launchpad.net/~johanmynhardt/openlp/android/+merge/62858

Click-able items in the Service and Slide ListViews, which send the service/slide live.
Improved Layout: 
 - previous and next buttons are now next to each other and at the bottom, which handles better on the hand.
 - List item layout is better structured and slide item numbers or verse tags are now in bold white text.
JSON handler for parsing slide and service items and creating requests.
Added Slide in data package to handle slide/service items parsed from the JSON response.
Better (less verbose) error handling inside the code.
Organised packages to reflect data and activities.


-- 
https://code.launchpad.net/~johanmynhardt/openlp/android/+merge/62858
Your team OpenLP Android Developers is subscribed to branch lp:openlp/android.
=== modified file 'AndroidManifest.xml'
--- AndroidManifest.xml	2011-05-03 22:11:35 +0000
+++ AndroidManifest.xml	2011-05-30 10:27:27 +0000
@@ -1,22 +1,40 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android";
-          package="org.openlp.android"
-          android:versionCode="1"
-          android:versionName="0.1">
+<manifest
+        xmlns:android="http://schemas.android.com/apk/res/android";
+        package="org.openlp.android"
+        android:versionCode="1"
+        android:versionName="0.1">
     <uses-sdk android:minSdkVersion="7"/>
     <uses-permission android:name="android.permission.INTERNET"/>
 
-    <application android:icon="@drawable/openlp_logo" android:label="@string/app_name">
-        <activity android:name=".OpenLP"
-                  android:label="@string/app_name">
+    <application
+            android:icon="@drawable/openlp_logo"
+            android:label="@string/app_name">
+        <activity
+                android:name=".OpenLP"
+                android:label="@string/app_name">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
-		<activity android:name=".Misc"/>
-		<activity android:name=".Preferences" android:label="@string/preferences"/>
-        <activity android:name=".Slide"/>
-        <activity android:name=".Service"/>
+        <activity
+                android:name=".activity.SearchableActivity"
+                android:label="@string/searchResults">
+            <intent-filter>
+                <action android:name="android.intent.action.SEARCH"/>
+            </intent-filter>
+            <meta-data
+                    android:name="android.app.searchable"
+                    android:resource="@xml/searchable"/>
+        </activity>
+
+        <activity android:name=".activity.Misc"/>
+        <activity android:name=".activity.Preferences" android:label="@string/preferences"/>
+        <activity android:name=".activity.Slide"/>
+        <activity android:name=".activity.Service"/>
+        <meta-data
+                android:name="android.app.default_searchable"
+                android:value=".activity.SearchableActivity"/>
     </application>
 </manifest>
\ No newline at end of file

=== modified file 'OpenLP.apk'
Binary files OpenLP.apk	2011-05-08 21:05:10 +0000 and OpenLP.apk	2011-05-30 10:27:27 +0000 differ
=== added file 'res/layout/group_child.xml'
--- res/layout/group_child.xml	1970-01-01 00:00:00 +0000
+++ res/layout/group_child.xml	2011-05-30 10:27:27 +0000
@@ -0,0 +1,14 @@
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android";
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+    <TextView
+            android:id="@+id/groupChildText"
+            android:layout_height="wrap_content"
+            android:layout_width="fill_parent"
+            android:paddingLeft="20dip"
+            android:gravity="center_vertical"
+            android:minLines="3"
+            />
+</LinearLayout>
\ No newline at end of file

=== added file 'res/layout/group_parent.xml'
--- res/layout/group_parent.xml	1970-01-01 00:00:00 +0000
+++ res/layout/group_parent.xml	2011-05-30 10:27:27 +0000
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android";
+        android:layout_width="fill_parent" android:baselineAligned="false" android:orientation="horizontal" android:layout_height="wrap_content">
+    <TextView
+            android:id="@+id/groupParentText"
+            android:layout_height="wrap_content"
+            android:paddingLeft="5dip"
+            android:minLines="3"
+            android:focusable="false"
+            android:gravity="center_vertical|right" android:layout_width="fill_parent" android:layout_weight="1"
+            />
+    <TextView
+            android:id="@+id/parentChildCount"
+            android:width="40dip"
+            android:paddingRight="5dip"
+            android:layout_width="fill_parent" 
+            android:layout_weight="1" 
+            android:layout_height="wrap_content"
+            android:gravity="center_vertical|right"
+            />
+</LinearLayout>
\ No newline at end of file

=== removed file 'res/layout/listitem.xml'
--- res/layout/listitem.xml	2011-03-12 18:34:47 +0000
+++ res/layout/listitem.xml	1970-01-01 00:00:00 +0000
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android";
-    android:layout_width="fill_parent" android:layout_height="fill_parent">
-    <LinearLayout
-    android:orientation="vertical"
-    android:layout_width="0dip" android:layout_weight="1"
-    android:layout_height="fill_parent">
-        <TextView
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:id="@+id/listitem" />
-    </LinearLayout>
-</LinearLayout>
\ No newline at end of file

=== modified file 'res/layout/main.xml'
--- res/layout/main.xml	2011-03-08 21:02:48 +0000
+++ res/layout/main.xml	2011-05-30 10:27:27 +0000
@@ -1,22 +1,22 @@
 <?xml version="1.0" encoding="utf-8"?>
 <TabHost xmlns:android="http://schemas.android.com/apk/res/android";
-    android:id="@android:id/tabhost"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent">
+         android:id="@android:id/tabhost"
+         android:layout_width="fill_parent"
+         android:layout_height="fill_parent">
     <LinearLayout
-        android:orientation="vertical"
-        android:layout_width="fill_parent"
-        android:layout_height="fill_parent"
-        android:padding="5dp">
+            android:orientation="vertical"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            android:padding="5dp">
         <TabWidget
-            android:id="@android:id/tabs"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content" />
+                android:id="@android:id/tabs"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"/>
         <FrameLayout
-            android:id="@android:id/tabcontent"
-            android:layout_width="fill_parent"
-            android:layout_height="fill_parent"
-            android:padding="5dp" />
+                android:id="@android:id/tabcontent"
+                android:layout_width="fill_parent"
+                android:layout_height="fill_parent"
+                android:padding="0dp"/>
     </LinearLayout>
 </TabHost>
 

=== added file 'res/layout/search.xml'
--- res/layout/search.xml	1970-01-01 00:00:00 +0000
+++ res/layout/search.xml	2011-05-30 10:27:27 +0000
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android";
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:orientation="vertical">
+    <TableLayout
+            android:id="@+id/tableLayout1"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent">
+        <ExpandableListView
+                android:layout_weight="1"
+                android:layout_height="wrap_content"
+                android:id="@+id/list"
+                android:layout_width="wrap_content"
+                />
+    </TableLayout>
+</LinearLayout>

=== modified file 'res/layout/service.xml'
--- res/layout/service.xml	2011-05-07 20:01:46 +0000
+++ res/layout/service.xml	2011-05-30 10:27:27 +0000
@@ -1,26 +1,40 @@
 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout
         xmlns:android="http://schemas.android.com/apk/res/android";
+        android:layout_width="fill_parent"
+        android:baselineAligned="false"
         android:orientation="vertical"
-        android:layout_width="fill_parent"
         android:layout_height="fill_parent">
-    <Button
-            android:text="@string/prev"
-            android:id="@+id/prev"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            />
-    <Button
-            android:text="@string/next"
-            android:id="@+id/next"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"/>
-<!--    <TextView
-            android:id="@+id/services"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"/>-->
-    <ListView
-            android:id="@+id/list"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"/>
+    <TableLayout
+            android:id="@+id/tableLayout1"
+            android:layout_width="fill_parent"
+            android:layout_gravity="fill"
+            android:layout_height="fill_parent">
+        <ListView
+                android:layout_gravity="top"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:id="@+id/list"
+                android:layout_width="fill_parent"/>
+        <LinearLayout
+                android:baselineAligned="false"
+                android:id="@+id/linearLayout1"
+                android:layout_width="fill_parent"
+                android:layout_gravity="fill"
+                android:layout_height="wrap_content"
+                android:layout_weight="0">
+            <Button
+                    android:text="@string/prev"
+                    android:layout_weight="1"
+                    android:id="@+id/prev"
+                    android:layout_height="wrap_content"
+                    android:layout_width="fill_parent"/>
+            <Button
+                    android:text="@string/next"
+                    android:layout_weight="1"
+                    android:id="@+id/next"
+                    android:layout_height="wrap_content"
+                    android:layout_width="fill_parent"/>
+        </LinearLayout>
+    </TableLayout>
 </LinearLayout>

=== modified file 'res/layout/slide.xml'
--- res/layout/slide.xml	2011-05-07 20:01:46 +0000
+++ res/layout/slide.xml	2011-05-30 10:27:27 +0000
@@ -1,21 +1,37 @@
 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout
         xmlns:android="http://schemas.android.com/apk/res/android";
-        android:orientation="vertical"
         android:layout_width="fill_parent"
-        android:layout_height="fill_parent">
-    <Button
-            android:text="@string/prev"
-            android:id="@+id/prev" android:layout_width="fill_parent"
-            android:layout_height="wrap_content"/>
-    <Button
-            android:text="@string/next"
-            android:id="@+id/next"
+        android:layout_height="fill_parent"
+        android:orientation="vertical">
+    <TableLayout
+            android:id="@+id/tableLayout1"
             android:layout_width="fill_parent"
-            android:layout_height="wrap_content"/>
-    <ListView
-            android:id="@+id/list"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"/>
+            android:layout_height="fill_parent">
+        <ListView
+                android:layout_weight="1"
+                android:layout_height="wrap_content"
+                android:id="@+id/list"
+                android:layout_width="wrap_content"/>
+        <LinearLayout
+                android:layout_height="wrap_content"
+                android:id="@+id/linearLayout1"
+                android:layout_width="fill_parent">
+            <Button
+                    android:text="@string/prev"
+                    android:layout_weight="1"
+                    android:id="@+id/prev"
+                    android:layout_height="wrap_content"
+                    android:layout_width="fill_parent"
+                    android:layout_gravity="center_vertical"/>
+            <Button
+                    android:text="@string/next"
+                    android:layout_weight="1"
+                    android:id="@+id/next"
+                    android:layout_height="wrap_content"
+                    android:layout_width="fill_parent"
+                    android:layout_gravity="center"/>
+        </LinearLayout>
+    </TableLayout>
 
 </LinearLayout>

=== added file 'res/layout/slide_list_item.xml'
--- res/layout/slide_list_item.xml	1970-01-01 00:00:00 +0000
+++ res/layout/slide_list_item.xml	2011-05-30 10:27:27 +0000
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android";
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent">
+    <TextView
+            android:id="@+id/rowItemMarker"
+            android:layout_width="wrap_content"
+            android:layout_height="fill_parent"
+            android:gravity="center"
+            android:textStyle="bold"
+            android:textColor="@color/white"
+            />
+    <TextView
+            android:id="@+id/rowItemText"
+            android:layout_width="wrap_content"
+            android:layout_height="fill_parent"
+            android:gravity="center_vertical"
+            android:minLines="2"
+            android:textSize="8pt"/>
+</LinearLayout>
\ No newline at end of file

=== modified file 'res/menu/menu.xml'
--- res/menu/menu.xml	2011-04-27 18:33:17 +0000
+++ res/menu/menu.xml	2011-05-30 10:27:27 +0000
@@ -2,4 +2,9 @@
 <menu xmlns:android="http://schemas.android.com/apk/res/android";>
     <item android:enabled="true" android:titleCondensed="@string/settings" android:title="@string/settings"
           android:id="@+id/preferences"/>
+	<item
+		android:id="@+id/menuSearch"
+		android:enabled="true"
+		android:title="Search"
+		android:titleCondensed="Search"/>
 </menu>

=== added file 'res/values-en/colors.xml'
--- res/values-en/colors.xml	1970-01-01 00:00:00 +0000
+++ res/values-en/colors.xml	2011-05-30 10:27:27 +0000
@@ -0,0 +1,4 @@
+<resources>
+    <color name="lightGreen">#ff66ff33</color>
+    <color name="white">#ffffffff</color>
+</resources>
\ No newline at end of file

=== modified file 'res/values-en/strings.xml'
--- res/values-en/strings.xml	2011-05-08 11:41:49 +0000
+++ res/values-en/strings.xml	2011-05-30 10:27:27 +0000
@@ -13,6 +13,8 @@
     <string name="blank">Blank</string>
     <string name="unblank">Unblank</string>
     <string name="alert">Alert:</string>
+    <string name="alertHint">Enter alert message</string>
+    <string name="alertTextNull">Please enter a message to send.</string>
     <string name="send">Send</string>
     <string name="Slide">Slide</string>
     <string name="slides">Slides</string>
@@ -20,6 +22,7 @@
     <string name="misc">Miscellaneous</string>
     <string name="preferenceCategoryTitleServer">Server</string>
     <string name="url">Server</string>
+    <string name="urlHint">Hostname or IP</string>
     <string name="port">Port</string>
     <string name="enableCustomTimeouts">Enable Custom Timeouts</string>
     <string name="customTimeoutsSummary">Check to modify timeout settings</string>
@@ -33,9 +36,13 @@
     <string name="fail">Connection failed</string>
     <string name="jsonfail">JSON failed</string>
     <string name="loading">Connecting...</string>
+    <string name="searching">Searching...</string>
     <string name="toggleBlankOn">Display Blanked</string>
     <string name="toggleBlankOff">Display Unblanked</string>
     <string name="loadingServiceItems">Loading Service Items...</string>
     <string name="loadingSlideItems">Loading Slide Items...</string>
+	<string name="searchHint">Search OpenLP</string>
+    <string name="searchResults">Search Results</string>
+    <string name="showingResults">Showing Results for \'%s\'</string>
 
 </resources>

=== added file 'res/values/colors.xml'
--- res/values/colors.xml	1970-01-01 00:00:00 +0000
+++ res/values/colors.xml	2011-05-30 10:27:27 +0000
@@ -0,0 +1,4 @@
+<resources>
+    <color name="lightGreen">#ff66ff33</color>
+    <color name="white">#ffffffff</color>
+</resources>
\ No newline at end of file

=== modified file 'res/values/strings.xml'
--- res/values/strings.xml	2011-05-08 11:48:35 +0000
+++ res/values/strings.xml	2011-05-30 10:27:27 +0000
@@ -14,6 +14,7 @@
     <string name="unblank">Unblank</string>
     <string name="alert">Alert:</string>
     <string name="alertHint">Enter alert message</string>
+    <string name="alertTextNull">Please enter a message to send.</string>
     <string name="send">Send</string>
     <string name="Slide">Slide</string>
     <string name="slides">Slides</string>
@@ -35,9 +36,13 @@
     <string name="fail">Connection failed</string>
     <string name="jsonfail">JSON failed</string>
     <string name="loading">Connecting...</string>
+    <string name="searching">Searching...</string>
     <string name="toggleBlankOn">Display Blanked</string>
     <string name="toggleBlankOff">Display Unblanked</string>
     <string name="loadingServiceItems">Loading Service Items...</string>
     <string name="loadingSlideItems">Loading Slide Items...</string>
+	<string name="searchHint">Search OpenLP</string>
+    <string name="searchResults">Search Results</string>
+    <string name="showingResults">Showing Results for \'%s\'</string>
 
 </resources>

=== added file 'res/xml/searchable.xml'
--- res/xml/searchable.xml	1970-01-01 00:00:00 +0000
+++ res/xml/searchable.xml	2011-05-30 10:27:27 +0000
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<searchable
+		xmlns:android="http://schemas.android.com/apk/res/android";
+		android:label="@string/app_name"
+		android:hint="@string/searchHint"
+		>
+	
+</searchable>
\ No newline at end of file

=== removed file 'src/org/openlp/android/Misc.java'
--- src/org/openlp/android/Misc.java	2011-05-07 20:01:46 +0000
+++ src/org/openlp/android/Misc.java	1970-01-01 00:00:00 +0000
@@ -1,131 +0,0 @@
-/******************************************************************************
- * OpenLP - Open Source Lyrics Projection                                      *
- * --------------------------------------------------------------------------- *
- * Copyright (c) 2008-2011 Raoul Snyman                                        *
- * Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael      *
- * Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler,        *
- * Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout,      *
- * Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund             *
- * --------------------------------------------------------------------------- *
- * This program 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; version 2 of the License.                              *
- *                                                                             *
- * This program 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 this program; if not, write to the Free Software Foundation, Inc., 59  *
- * Temple Place, Suite 330, Boston, MA 02111-1307 USA                          *
- *******************************************************************************/
-package org.openlp.android;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.Toast;
-import android.widget.ToggleButton;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.json.JSONStringer;
-import org.openlp.android.api.Api;
-import org.openlp.android.utility.WebCallAsyncTask;
-
-import java.io.UnsupportedEncodingException;
-
-public class Misc extends Activity implements Api {
-    private final Context context = this;
-
-    public Button.OnClickListener mSend = new Button.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            EditText edittext = (EditText) findViewById(R.id.alert);
-            String alert;
-            try {
-                JSONObject jo = new JSONObject();
-                jo.put("text", edittext.getText());
-                alert = new JSONStringer().object().key("request").value(jo).endObject().toString();
-                alert = java.net.URLEncoder.encode(alert, "UTF-8");
-            }
-            catch (JSONException e) {
-                alert = String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage());
-            }
-            catch (UnsupportedEncodingException e) {
-                alert = String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage());
-            }
-
-            try {
-                new WebCallAsyncTask(context).execute(String.format("%s%s", ALERT, alert));
-            }
-            catch (Exception e) {
-                Toast.makeText(getApplicationContext(), e.toString(), Toast.LENGTH_SHORT).show();
-            }
-        }
-    };
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        Log.i(LOG_TAG, "onCreate");
-        setContentView(R.layout.misc);
-
-        final ToggleButton toggleButton = (ToggleButton) findViewById(R.id.toggleDisplayButton);
-
-        toggleButton.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                try {
-                    if (!toggleButton.isChecked()) {
-                        new WebCallAsyncTask(context).execute(DISPLAY_SHOW);
-                    }
-                    else {
-                        new WebCallAsyncTask(context).execute(DISPLAY_HIDE);
-                    }
-                }
-                catch (Exception e) {
-                    Toast.makeText(context, String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage()),
-                            Toast.LENGTH_SHORT).show();
-                }
-            }
-        });
-
-        findViewById(R.id.send).setOnClickListener(mSend);
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        Log.d(LOG_TAG, "Resume");
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        MenuInflater inflater = getMenuInflater();
-        inflater.inflate(R.menu.menu, menu);
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        // Handle item selection
-        switch (item.getItemId()) {
-            case R.id.preferences:
-                startActivity(new Intent(this, Preferences.class));
-                return true;
-            default:
-                return super.onOptionsItemSelected(item);
-        }
-    }
-
-    private final String LOG_TAG = Misc.class.getName();
-}

=== modified file 'src/org/openlp/android/OpenLP.java'
--- src/org/openlp/android/OpenLP.java	2011-05-08 11:41:49 +0000
+++ src/org/openlp/android/OpenLP.java	2011-05-30 10:27:27 +0000
@@ -28,6 +28,10 @@
 import android.os.Bundle;
 import android.util.Log;
 import android.widget.TabHost;
+import org.openlp.android.activity.Misc;
+import org.openlp.android.activity.Preferences;
+import org.openlp.android.activity.Service;
+import org.openlp.android.activity.Slide;
 
 /**
  * OpenLP-Android initialisation point.

=== removed file 'src/org/openlp/android/Preferences.java'
--- src/org/openlp/android/Preferences.java	2011-05-07 20:01:46 +0000
+++ src/org/openlp/android/Preferences.java	1970-01-01 00:00:00 +0000
@@ -1,44 +0,0 @@
-/******************************************************************************
- * OpenLP - Open Source Lyrics Projection                                      *
- * --------------------------------------------------------------------------- *
- * Copyright (c) 2008-2011 Raoul Snyman                                        *
- * Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael      *
- * Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler,        *
- * Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout,      *
- * Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund             *
- * --------------------------------------------------------------------------- *
- * This program 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; version 2 of the License.                              *
- *                                                                             *
- * This program 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 this program; if not, write to the Free Software Foundation, Inc., 59  *
- * Temple Place, Suite 330, Boston, MA 02111-1307 USA                          *
- *******************************************************************************/
-package org.openlp.android;
-
-import android.os.Bundle;
-import android.preference.PreferenceActivity;
-import android.util.Log;
-
-/**
- * Credits:
- * http://www.kaloer.com/android-preferences
- * http://androidpartaker.wordpress.com/2010/07/11/android-preferences/
- */
-public class Preferences extends PreferenceActivity {
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        Log.d(LOG_TAG, "Launching preferences");
-        getPreferenceManager().setSharedPreferencesName(getString(R.string.keySharedPreferences));
-        addPreferencesFromResource(R.xml.preferences);
-    }
-
-    private final String LOG_TAG = Preferences.class.getName();
-}

=== removed file 'src/org/openlp/android/Service.java'
--- src/org/openlp/android/Service.java	2011-05-08 11:41:49 +0000
+++ src/org/openlp/android/Service.java	1970-01-01 00:00:00 +0000
@@ -1,229 +0,0 @@
-/******************************************************************************
- * OpenLP - Open Source Lyrics Projection                                      *
- * --------------------------------------------------------------------------- *
- * Copyright (c) 2008-2011 Raoul Snyman                                        *
- * Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael      *
- * Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler,        *
- * Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout,      *
- * Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund             *
- * --------------------------------------------------------------------------- *
- * This program 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; version 2 of the License.                              *
- *                                                                             *
- * This program 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 this program; if not, write to the Free Software Foundation, Inc., 59  *
- * Temple Place, Suite 330, Boston, MA 02111-1307 USA                          *
- *******************************************************************************/
-package org.openlp.android;
-
-import android.app.Activity;
-import android.app.ProgressDialog;
-import android.content.Context;
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.ListView;
-import android.widget.Toast;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.openlp.android.api.Api;
-import org.openlp.android.utility.OpenLPHttpClient;
-import org.openlp.android.utility.StringHelper;
-import org.openlp.android.utility.WebCallAsyncTask;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Activity for managing service objects.
- */
-public class Service extends Activity implements Api {
-    private final String LOG_TAG = Service.class.getName();
-    private final Context context = this;
-
-    private ListView listView;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        Log.i(LOG_TAG, "onCreate");
-
-        setContentView(R.layout.service);
-        listView = (ListView) findViewById(R.id.list);
-        findViewById(R.id.prev).setOnClickListener(mPrev);
-        findViewById(R.id.next).setOnClickListener(mNext);
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        Log.d(LOG_TAG, "Resume");
-
-        new FetchServiceItemsTask(this).execute(SERVICE_LIST);
-    }
-
-    public Button.OnClickListener mPrev = new Button.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            try {
-                new WebCallAsyncTask(context).execute(SERVICE_PREVIOUS);
-            }
-            catch (Exception e) {
-                Toast.makeText(getApplicationContext(),
-                        String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage()),
-                        Toast.LENGTH_SHORT).show();
-            }
-        }
-    };
-
-    public Button.OnClickListener mNext = new Button.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            try {
-                new WebCallAsyncTask(context).execute(SERVICE_NEXT);
-            }
-            catch (Exception e) {
-                Toast.makeText(getApplicationContext(),
-                        String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage()),
-                        Toast.LENGTH_SHORT).show();
-            }
-        }
-    };
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        MenuInflater inflater = getMenuInflater();
-        inflater.inflate(R.menu.menu, menu);
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case R.id.preferences:
-                startActivity(new Intent(this, Preferences.class));
-                return true;
-            default:
-                return super.onOptionsItemSelected(item);
-        }
-    }
-
-    /**
-     * Asynchronous task to fetch the service items.
-     */
-    class FetchServiceItemsTask extends AsyncTask<String, String[], Void> {
-        Service serviceActivity;
-        ProgressDialog progressDialog;
-        String error;
-
-        FetchServiceItemsTask(Service serviceActivity) {
-            this.serviceActivity = serviceActivity;
-        }
-
-        @Override
-        protected void onPreExecute() {
-            super.onPreExecute();
-            progressDialog = ProgressDialog.show(Service.this, getString(R.string.loading),
-                    getString(R.string.loadingServiceItems));
-        }
-
-        @Override
-        protected Void doInBackground(String... strings) {
-            OpenLPHttpClient httpClient = new OpenLPHttpClient(getApplicationContext());
-            HttpResponse response = null;
-            String returnString = "";
-
-            Log.d(LOG_TAG, "Processing:" + Arrays.asList(strings));
-            try {
-                httpClient.setUrl(strings[0]);
-                if (httpClient.getUrl().getHost().trim().length() <= 0) {
-                    startActivity(new Intent(serviceActivity, Preferences.class));
-                }
-                else {
-                    response = httpClient.execute();
-                }
-
-                if (response != null && response.getStatusLine().getStatusCode() == 200) {
-                    HttpEntity entity = response.getEntity();
-                    // If the response does not enclose an entity, there is no need
-                    // to worry about connection release
-
-                    if (entity != null) {
-                        InputStream instream = entity.getContent();
-                        String result = StringHelper.convertStreamToString(instream);
-                        Log.i(LOG_TAG, result);
-
-                        // Build the return string.
-                        JSONObject jObject = new JSONObject(result);
-                        JSONObject results = jObject.getJSONObject("results");
-                        JSONArray items = results.getJSONArray("items");
-                        List<String> serviceItemList = new ArrayList<String>();
-                        returnString = "";
-
-                        for (int i = 0; i < items.length(); i++) {
-                            serviceItemList.add(items.getJSONObject(i).getString("title"));
-                        }
-
-                        instream.close();
-
-                        Log.i(LOG_TAG, String.format("Service Items: %s", serviceItemList));
-                        publishProgress(serviceItemList.toArray(new String[]{}));
-                    }
-                }
-                else {
-                    returnString = String.format("%s %s", getString(R.string.unable), response);
-                }
-            }
-            catch (JSONException ex) {
-                returnString = String.format("%s: %s", ex.getClass().getSimpleName(), ex.getMessage());
-            }
-            catch (IOException e) {
-                returnString = String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage());
-            }
-            catch (URISyntaxException e) {
-                returnString = String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage());
-            }
-
-            if (returnString.trim().length() > 0) {
-                error = returnString;
-                Log.e(LOG_TAG, returnString);
-            }
-            return null;
-        }
-
-        @Override
-        protected void onProgressUpdate(String[]... values) {
-            super.onProgressUpdate(values);
-            listView.setAdapter(new ArrayAdapter<String>(serviceActivity, android.R.layout.simple_list_item_1, values[0]));
-        }
-
-        @Override
-        protected void onPostExecute(Void v) {
-            super.onPostExecute(v);
-            progressDialog.dismiss();
-            if (error != null && error.trim().length() > 0) {
-                Toast.makeText(context, error, Toast.LENGTH_LONG).show();
-            }
-        }
-    }
-}
\ No newline at end of file

=== removed file 'src/org/openlp/android/Slide.java'
--- src/org/openlp/android/Slide.java	2011-05-08 11:41:49 +0000
+++ src/org/openlp/android/Slide.java	1970-01-01 00:00:00 +0000
@@ -1,233 +0,0 @@
-/******************************************************************************
- * OpenLP - Open Source Lyrics Projection                                      *
- * --------------------------------------------------------------------------- *
- * Copyright (c) 2008-2011 Raoul Snyman                                        *
- * Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael      *
- * Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler,        *
- * Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout,      *
- * Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund             *
- * --------------------------------------------------------------------------- *
- * This program 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; version 2 of the License.                              *
- *                                                                             *
- * This program 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 this program; if not, write to the Free Software Foundation, Inc., 59  *
- * Temple Place, Suite 330, Boston, MA 02111-1307 USA                          *
- *******************************************************************************/
-package org.openlp.android;
-
-import android.app.Activity;
-import android.app.ProgressDialog;
-import android.content.Context;
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.ListView;
-import android.widget.Toast;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.openlp.android.api.Api;
-import org.openlp.android.utility.OpenLPHttpClient;
-import org.openlp.android.utility.StringHelper;
-import org.openlp.android.utility.WebCallAsyncTask;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-public class Slide extends Activity implements Api {
-    private final Context context = this;
-    private final String LOG_TAG = Slide.class.getName();
-    private ListView slideList;
-
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        Log.d(LOG_TAG, "onCreate");
-        setContentView(R.layout.slide);
-
-        slideList = (ListView) findViewById(R.id.list);
-        findViewById(R.id.prev).setOnClickListener(mPrev);
-        findViewById(R.id.next).setOnClickListener(mNext);
-
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        Log.d(LOG_TAG, "Resume");
-
-        new FetchSlideItemsTask(this).execute(LIVE_TEXT);
-    }
-
-    public Button.OnClickListener mPrev = new Button.OnClickListener() {
-        public void onClick(View v) {
-            try {
-                new WebCallAsyncTask(context).execute(LIVE_PREVIOUS);
-            }
-            catch (Exception e) {
-                Toast.makeText(getApplicationContext(),
-                        String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage()),
-                        Toast.LENGTH_SHORT).show();
-                Log.e(LOG_TAG, e.toString(), e);
-            }
-        }
-    };
-
-    public Button.OnClickListener mNext = new Button.OnClickListener() {
-        public void onClick(View v) {
-            try {
-                new WebCallAsyncTask(context).execute(LIVE_NEXT);
-            }
-            catch (Exception e) {
-                Toast.makeText(getApplicationContext(),
-                        String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage()),
-                        Toast.LENGTH_SHORT).show();
-                Log.e(LOG_TAG, e.toString(), e);
-            }
-        }
-    };
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        MenuInflater inflater = getMenuInflater();
-        inflater.inflate(R.menu.menu, menu);
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        // Handle item selection
-        switch (item.getItemId()) {
-            case R.id.preferences:
-                startActivity(new Intent(this, Preferences.class));
-                return true;
-            default:
-                return super.onOptionsItemSelected(item);
-        }
-    }
-
-    class FetchSlideItemsTask extends AsyncTask<String, String[], Void> {
-        Slide slideActivity;
-        ProgressDialog progressDialog;
-        String error;
-
-        FetchSlideItemsTask(Slide slideActivity) {
-            this.slideActivity = slideActivity;
-        }
-
-        @Override
-        protected void onPreExecute() {
-            super.onPreExecute();
-            progressDialog = ProgressDialog.show(Slide.this, getString(R.string.loading), getString(R.string.loadingSlideItems));
-        }
-
-        @Override
-        protected Void doInBackground(String... strings) {
-            OpenLPHttpClient httpClient = new OpenLPHttpClient(getApplicationContext());
-            HttpResponse response = null;
-            String returnString = "";
-
-            Log.d(LOG_TAG, "Processing:" + Arrays.asList(strings));
-            try {
-                httpClient.setUrl(strings[0]);
-                if (httpClient.getUrl().getHost().trim().length() <= 0) {
-                    startActivity(new Intent(slideActivity, Preferences.class));
-                }
-                else {
-                    response = httpClient.execute();
-                }
-
-                if (response != null && response.getStatusLine().getStatusCode() == 200) {
-                    HttpEntity entity = response.getEntity();
-                    // If the response does not enclose an entity, there is no need
-                    // to worry about connection release
-
-                    if (entity != null) {
-                        InputStream instream = entity.getContent();
-
-                        String result = StringHelper.convertStreamToString(instream);
-                        Log.i(LOG_TAG, result);
-
-                        // Load the requested page converted to a string into a
-                        // JSONObject.
-
-                        // Build the return string.
-                        JSONObject jObject = new JSONObject(result);
-                        JSONObject results = jObject.getJSONObject("results");
-                        JSONArray slides = results.getJSONArray("slides");
-                        List<String> result1 = new ArrayList<String>();
-                        returnString = "";
-
-                        for (int i = 0; i < slides.length(); i++) {
-                            result1.add(slides.getJSONObject(i).getString("text"));
-                        }
-
-                        instream.close();
-                        Log.d(LOG_TAG, String.format("slides: %s", result1));
-                        publishProgress(result1.toArray(new String[]{}));
-                    }
-                }
-                else {
-                    returnString = String.format("%s %s", getString(R.string.unable), response);
-                }
-            }
-            catch (JSONException ex) {
-                returnString = String.format("%s: %s", ex.getClass().getSimpleName(), ex.getMessage());
-            }
-            catch (MalformedURLException e) {
-                returnString = String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage());
-            }
-            catch (IOException e) {
-                returnString = String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage());
-            }
-            catch (URISyntaxException e) {
-                returnString = String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage());
-            }
-            catch (IllegalArgumentException e) {
-                returnString = String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage());
-            }
-
-            if (returnString.trim().length() > 0) {
-                error = returnString;
-                Log.e(LOG_TAG, returnString);
-            }
-            return null;
-        }
-
-        @Override
-        protected void onPostExecute(Void aVoid) {
-            super.onPostExecute(aVoid);
-            progressDialog.dismiss();
-
-            if (error != null && error.trim().length() > 0) {
-                Toast.makeText(context, error, Toast.LENGTH_LONG).show();
-            }
-        }
-
-        @Override
-        protected void onProgressUpdate(String[]... values) {
-            super.onProgressUpdate(values);
-            slideList.setAdapter(new ArrayAdapter<String>(slideActivity, android.R.layout.simple_list_item_1, values[0]));
-        }
-    }
-}

=== added directory 'src/org/openlp/android/activity'
=== added file 'src/org/openlp/android/activity/Misc.java'
--- src/org/openlp/android/activity/Misc.java	1970-01-01 00:00:00 +0000
+++ src/org/openlp/android/activity/Misc.java	2011-05-30 10:27:27 +0000
@@ -0,0 +1,121 @@
+/******************************************************************************
+ * OpenLP - Open Source Lyrics Projection                                      *
+ * --------------------------------------------------------------------------- *
+ * Copyright (c) 2008-2011 Raoul Snyman                                        *
+ * Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael      *
+ * Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler,        *
+ * Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout,      *
+ * Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund             *
+ * --------------------------------------------------------------------------- *
+ * This program 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; version 2 of the License.                              *
+ *                                                                             *
+ * This program 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 this program; if not, write to the Free Software Foundation, Inc., 59  *
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA                          *
+ *******************************************************************************/
+package org.openlp.android.activity;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+import android.widget.ToggleButton;
+import org.openlp.android.R;
+import org.openlp.android.api.Api;
+import org.openlp.android.utility.JSONHandler;
+import org.openlp.android.utility.WebCallAsyncTask;
+
+public class Misc extends Activity implements Api {
+    private final Context context = this;
+
+    public Button.OnClickListener mSend = new Button.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            EditText edittext = (EditText) findViewById(R.id.alert);
+            String alert;
+            try {
+                if (edittext.getText().toString().trim().length() > 0) {
+                    alert = JSONHandler.createRequestJSON("text", edittext.getText().toString());
+                    new WebCallAsyncTask(context, ALERT).execute(alert);
+                }
+                else {
+                    Toast.makeText(getBaseContext(), getString(R.string.alertTextNull), Toast.LENGTH_SHORT).show();
+                }
+            }
+            catch (JSONHandler.JSONHandlerException e) {
+                Toast.makeText(getApplicationContext(), e.toString(), Toast.LENGTH_SHORT).show();
+            }
+        }
+    };
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(LOG_TAG, "onCreate");
+        setContentView(R.layout.misc);
+
+        final ToggleButton toggleButton = (ToggleButton) findViewById(R.id.toggleDisplayButton);
+
+        toggleButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                try {
+                    if (!toggleButton.isChecked()) {
+                        new WebCallAsyncTask(context).execute(DISPLAY_SHOW);
+                    }
+                    else {
+                        new WebCallAsyncTask(context).execute(DISPLAY_HIDE);
+                    }
+                }
+                catch (Exception e) {
+                    Toast.makeText(context, String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage()),
+                            Toast.LENGTH_SHORT).show();
+                }
+            }
+        });
+
+        findViewById(R.id.send).setOnClickListener(mSend);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        Log.d(LOG_TAG, "Resume");
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.menu, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        // Handle item selection
+        switch (item.getItemId()) {
+            case R.id.preferences:
+                startActivity(new Intent(this, Preferences.class));
+                return true;
+            default:
+                return super.onOptionsItemSelected(item);
+        }
+    }
+
+    private final String LOG_TAG = Misc.class.getName();
+}

=== added file 'src/org/openlp/android/activity/Preferences.java'
--- src/org/openlp/android/activity/Preferences.java	1970-01-01 00:00:00 +0000
+++ src/org/openlp/android/activity/Preferences.java	2011-05-30 10:27:27 +0000
@@ -0,0 +1,45 @@
+/******************************************************************************
+ * OpenLP - Open Source Lyrics Projection                                      *
+ * --------------------------------------------------------------------------- *
+ * Copyright (c) 2008-2011 Raoul Snyman                                        *
+ * Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael      *
+ * Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler,        *
+ * Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout,      *
+ * Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund             *
+ * --------------------------------------------------------------------------- *
+ * This program 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; version 2 of the License.                              *
+ *                                                                             *
+ * This program 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 this program; if not, write to the Free Software Foundation, Inc., 59  *
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA                          *
+ *******************************************************************************/
+package org.openlp.android.activity;
+
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.util.Log;
+import org.openlp.android.R;
+
+/**
+ * Credits:
+ * http://www.kaloer.com/android-preferences
+ * http://androidpartaker.wordpress.com/2010/07/11/android-preferences/
+ */
+public class Preferences extends PreferenceActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.d(LOG_TAG, "Launching preferences");
+        getPreferenceManager().setSharedPreferencesName(getString(R.string.keySharedPreferences));
+        addPreferencesFromResource(R.xml.preferences);
+    }
+
+    private final String LOG_TAG = Preferences.class.getName();
+}

=== added file 'src/org/openlp/android/activity/SearchableActivity.java'
--- src/org/openlp/android/activity/SearchableActivity.java	1970-01-01 00:00:00 +0000
+++ src/org/openlp/android/activity/SearchableActivity.java	2011-05-30 10:27:27 +0000
@@ -0,0 +1,170 @@
+/******************************************************************************
+ * OpenLP - Open Source Lyrics Projection                                      *
+ * --------------------------------------------------------------------------- *
+ * Copyright (c) 2008-2011 Raoul Snyman                                        *
+ * Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael      *
+ * Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler,        *
+ * Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout,      *
+ * Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund             *
+ * --------------------------------------------------------------------------- *
+ * This program 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; version 2 of the License.                              *
+ *                                                                             *
+ * This program 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 this program; if not, write to the Free Software Foundation, Inc., 59  *
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA                          *
+ *******************************************************************************/
+package org.openlp.android.activity;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.app.SearchManager;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ExpandableListView;
+import android.widget.Toast;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.openlp.android.R;
+import org.openlp.android.api.Api;
+import org.openlp.android.utility.GroupExpandableListAdapter;
+import org.openlp.android.utility.JSONHandler;
+import org.openlp.android.utility.WebCallAsyncTask;
+import org.openlp.android.utility.WebCallReturningAsyncTask;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class SearchableActivity extends Activity implements Api {
+    private Activity context;
+    private ExpandableListView listView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.search);
+        context = this;
+        listView = (ExpandableListView) findViewById(R.id.list);
+
+        listView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
+            @SuppressWarnings({"unchecked"})
+            @Override
+            public boolean onChildClick(ExpandableListView expandableListView, View view, int parent,
+                                        int childPosition, long l) {
+                Map<String, JSONArray> child = (Map<String, JSONArray>) listView.getExpandableListAdapter()
+                        .getChild(parent, childPosition);
+                Object key = listView.getExpandableListAdapter().getGroup(parent);
+                JSONArray value = child.get(key.toString());
+
+                try {
+                    new WebCallAsyncTask(context, String.format(SEARCH_PLUGIN_LIVE, key))
+                            .execute(JSONHandler.createRequestJSON("id", value.get(0).toString()));
+                }
+                catch (Exception e) {
+                    Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();
+                }
+                return false;
+            }
+        });
+
+        Intent intent = getIntent();
+        if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
+            String query = intent.getStringExtra(SearchManager.QUERY);
+            doSearch(query);
+        }
+    }
+
+    public void doSearch(String search) {
+        new SearchAsync().execute(search);
+    }
+
+    class SearchAsync extends AsyncTask<String, Void, SearchResults> {
+        ProgressDialog progressDialog;
+        String query;
+
+        @Override
+        protected SearchResults doInBackground(String... strings) {
+            query = strings[0];
+            List<String> groups = new ArrayList<String>();
+            List<List<Map<String, JSONArray>>> children = new ArrayList<List<Map<String, JSONArray>>>();
+
+            AsyncTask call = new WebCallReturningAsyncTask(context).execute(SEARCHABLE_PLUGINS);
+
+            try {
+                JSONArray array = new JSONObject(call.get().toString()).getJSONObject("results").getJSONArray("items");
+
+                for (int i = 0; i < array.length(); i++) {
+                    String pluginString = array.get(i).toString();
+                    groups.add(pluginString);
+
+                    AsyncTask pluginResults = new WebCallReturningAsyncTask(context,
+                            String.format(SEARCH_PLUGIN_FORMATTED, pluginString))
+                            .execute(JSONHandler.createRequestJSON("text", query));
+
+                    JSONArray resultArray = new JSONObject(pluginResults.get().toString()).getJSONObject("results")
+                            .getJSONArray("items");
+                    List<Map<String, JSONArray>> list = new ArrayList<Map<String, JSONArray>>();
+                    for (int j = 0; j < resultArray.length(); j++) {
+                        Map<String, JSONArray> item = new HashMap<String, JSONArray>();
+                        item.put(pluginString, (JSONArray) resultArray.get(j));
+                        list.add(item);
+                    }
+                    children.add(list);
+                }
+            }
+            catch (Exception e) {
+                Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();
+            }
+
+            SearchResults results = new SearchResults();
+            results.setGroups(groups);
+            results.setChildren(children);
+            return results;
+        }
+
+        @Override
+        protected void onPreExecute() {
+            super.onPreExecute();
+            progressDialog = ProgressDialog.show(context, null, getString(R.string.searching));
+        }
+
+        @Override
+        protected void onPostExecute(SearchResults results) {
+            super.onPostExecute(results);
+            listView.setAdapter(new GroupExpandableListAdapter(context, results.getGroups(), results.getChildren()));
+            progressDialog.dismiss();
+            Toast.makeText(context, String.format(getString(R.string.showingResults), query), Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    class SearchResults {
+        List<String> groups;
+        List<List<Map<String, JSONArray>>> children;
+
+        public List<String> getGroups() {
+            return groups;
+        }
+
+        public void setGroups(List<String> groups) {
+            this.groups = groups;
+        }
+
+        public List<List<Map<String, JSONArray>>> getChildren() {
+            return children;
+        }
+
+        public void setChildren(List<List<Map<String, JSONArray>>> children) {
+            this.children = children;
+        }
+    }
+}

=== added file 'src/org/openlp/android/activity/Service.java'
--- src/org/openlp/android/activity/Service.java	1970-01-01 00:00:00 +0000
+++ src/org/openlp/android/activity/Service.java	2011-05-30 10:27:27 +0000
@@ -0,0 +1,228 @@
+/******************************************************************************
+ * OpenLP - Open Source Lyrics Projection                                      *
+ * --------------------------------------------------------------------------- *
+ * Copyright (c) 2008-2011 Raoul Snyman                                        *
+ * Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael      *
+ * Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler,        *
+ * Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout,      *
+ * Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund             *
+ * --------------------------------------------------------------------------- *
+ * This program 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; version 2 of the License.                              *
+ *                                                                             *
+ * This program 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 this program; if not, write to the Free Software Foundation, Inc., 59  *
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA                          *
+ *******************************************************************************/
+package org.openlp.android.activity;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.Toast;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.openlp.android.R;
+import org.openlp.android.api.Api;
+import org.openlp.android.data.Slide;
+import org.openlp.android.utility.JSONHandler;
+import org.openlp.android.utility.OpenLPHttpClient;
+import org.openlp.android.utility.SlideAdapter;
+import org.openlp.android.utility.WebCallAsyncTask;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Activity for managing service objects.
+ */
+public class Service extends Activity implements Api {
+    private final String LOG_TAG = Service.class.getName();
+    private final Activity context = this;
+
+    private ListView listView;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(LOG_TAG, "onCreate");
+
+        setContentView(R.layout.service);
+        listView = (ListView) findViewById(R.id.list);
+        findViewById(R.id.prev).setOnClickListener(new Button.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                try {
+                    new WebCallAsyncTask(context).execute(SERVICE_PREVIOUS);
+                }
+                catch (Exception e) {
+                    Toast.makeText(getApplicationContext(),
+                            String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage()),
+                            Toast.LENGTH_SHORT).show();
+                }
+            }
+        });
+
+        findViewById(R.id.next).setOnClickListener(new Button.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                try {
+                    new WebCallAsyncTask(context).execute(SERVICE_NEXT);
+                }
+                catch (Exception e) {
+                    Toast.makeText(getApplicationContext(),
+                            String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage()),
+                            Toast.LENGTH_SHORT).show();
+                }
+            }
+        });
+
+        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
+                String alert;
+                try {
+                    alert = JSONHandler.createRequestJSON("id", Integer.toString(i));
+                    new WebCallAsyncTask(context, SERVICE_SET).execute(alert);
+                    alert = null;
+                }
+                catch (JSONHandler.JSONHandlerException e) {
+                    alert = String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage());
+                }
+
+                if (alert != null) {
+                    Toast.makeText(getApplicationContext(), alert, Toast.LENGTH_LONG).show();
+                }
+            }
+        });
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        Log.d(LOG_TAG, "Resume");
+
+        new FetchServiceItemsTask(this).execute(SERVICE_LIST);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.menu, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.preferences:
+                startActivity(new Intent(this, Preferences.class));
+                return true;
+            case R.id.menuSearch:
+                onSearchRequested();
+                return true;
+            default:
+                return super.onOptionsItemSelected(item);
+        }
+    }
+
+    /**
+     * Asynchronous task to fetch the service items.
+     */
+    class FetchServiceItemsTask extends AsyncTask<String, Void, Slide[]> {
+        Service serviceActivity;
+        ProgressDialog progressDialog;
+        String error;
+
+        FetchServiceItemsTask(Service serviceActivity) {
+            this.serviceActivity = serviceActivity;
+        }
+
+        @Override
+        protected void onPreExecute() {
+            super.onPreExecute();
+            progressDialog = ProgressDialog.show(Service.this, getString(R.string.loading),
+                    getString(R.string.loadingServiceItems));
+        }
+
+        @Override
+        protected Slide[] doInBackground(String... strings) {
+            OpenLPHttpClient httpClient = new OpenLPHttpClient(getApplicationContext());
+            HttpResponse response = null;
+            String returnString = "";
+
+            Log.d(LOG_TAG, "Processing:" + Arrays.asList(strings));
+            try {
+                httpClient.setUrl(strings[0]);
+                if (httpClient.getUrl().getHost().trim().length() <= 0) {
+                    startActivity(new Intent(serviceActivity, Preferences.class));
+                }
+                else {
+                    response = httpClient.execute();
+                }
+
+                if (response != null && response.getStatusLine().getStatusCode() == 200) {
+                    HttpEntity entity = response.getEntity();
+
+                    if (entity != null) {
+                        List<Slide> serviceItemList = JSONHandler.parseServiceItemResponseJSON(entity);
+                        Log.i(LOG_TAG, String.format("Service Items: %s", serviceItemList));
+                        return serviceItemList.toArray(new Slide[]{});
+                    }
+                }
+                else {
+                    returnString = String.format("%s %s", getString(R.string.unable), response);
+                }
+            }
+            catch (Exception e) {
+                try {
+                    throw new FetchItemsException(e);
+                }
+                catch (FetchItemsException e1) {
+                    returnString = String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage());
+                }
+            }
+
+            if (returnString.trim().length() > 0) {
+                error = returnString;
+                Log.e(LOG_TAG, returnString);
+            }
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Slide[] slides) {
+            super.onPostExecute(slides);
+            if (slides == null) {
+                slides = new Slide[]{};
+            }
+            listView.setAdapter(new SlideAdapter(context, Arrays.asList(slides), false));
+            progressDialog.dismiss();
+            if (error != null && error.trim().length() > 0) {
+                Toast.makeText(context, error, Toast.LENGTH_LONG).show();
+            }
+        }
+
+        class FetchItemsException extends Exception {
+            FetchItemsException(Throwable throwable) {
+                super(throwable);
+            }
+        }
+    }
+}
\ No newline at end of file

=== added file 'src/org/openlp/android/activity/Slide.java'
--- src/org/openlp/android/activity/Slide.java	1970-01-01 00:00:00 +0000
+++ src/org/openlp/android/activity/Slide.java	2011-05-30 10:27:27 +0000
@@ -0,0 +1,223 @@
+/******************************************************************************
+ * OpenLP - Open Source Lyrics Projection                                      *
+ * --------------------------------------------------------------------------- *
+ * Copyright (c) 2008-2011 Raoul Snyman                                        *
+ * Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael      *
+ * Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler,        *
+ * Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout,      *
+ * Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund             *
+ * --------------------------------------------------------------------------- *
+ * This program 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; version 2 of the License.                              *
+ *                                                                             *
+ * This program 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 this program; if not, write to the Free Software Foundation, Inc., 59  *
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA                          *
+ *******************************************************************************/
+package org.openlp.android.activity;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.Toast;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.openlp.android.R;
+import org.openlp.android.api.Api;
+import org.openlp.android.utility.JSONHandler;
+import org.openlp.android.utility.OpenLPHttpClient;
+import org.openlp.android.utility.SlideAdapter;
+import org.openlp.android.utility.WebCallAsyncTask;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class Slide extends Activity implements Api {
+    private final Activity context = this;
+    private final String LOG_TAG = Slide.class.getName();
+    private ListView slideList;
+
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.d(LOG_TAG, "onCreate");
+        setContentView(R.layout.slide);
+
+        slideList = (ListView) findViewById(R.id.list);
+
+        findViewById(R.id.prev).setOnClickListener(new Button.OnClickListener() {
+            public void onClick(View v) {
+                try {
+                    new WebCallAsyncTask(context).execute(LIVE_PREVIOUS);
+                }
+                catch (Exception e) {
+                    Toast.makeText(getApplicationContext(),
+                            String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage()),
+                            Toast.LENGTH_SHORT).show();
+                    Log.e(LOG_TAG, e.toString(), e);
+                }
+            }
+        });
+
+        findViewById(R.id.next).setOnClickListener(new Button.OnClickListener() {
+            public void onClick(View v) {
+                try {
+                    new WebCallAsyncTask(context).execute(LIVE_NEXT);
+                }
+                catch (Exception e) {
+                    Toast.makeText(getApplicationContext(),
+                            String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage()),
+                            Toast.LENGTH_SHORT).show();
+                    Log.e(LOG_TAG, e.toString(), e);
+                }
+            }
+        });
+
+        slideList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
+                String alert;
+                try {
+                    alert = JSONHandler.createRequestJSON("id", Integer.toString(i));
+                    new WebCallAsyncTask(context, LIVE_SET).execute(alert);
+                    alert = null;
+                }
+                catch (Exception e) {
+                    try {
+                        throw new JSONHandler.JSONHandlerException(e);
+                    }
+                    catch (JSONHandler.JSONHandlerException e1) {
+                        alert = String.format("%s: %s", e1.getClass().getSimpleName(), e1.getMessage());
+                    }
+                }
+
+                if (alert != null) {
+                    Toast.makeText(getApplicationContext(), alert, Toast.LENGTH_LONG).show();
+                }
+            }
+        });
+
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        Log.d(LOG_TAG, "Resume");
+
+        new FetchSlideItemsTask(this).execute(LIVE_TEXT);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.menu, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.preferences:
+                startActivity(new Intent(this, Preferences.class));
+                return true;
+            default:
+                return super.onOptionsItemSelected(item);
+        }
+    }
+
+    class FetchSlideItemsTask extends AsyncTask<String, Void, org.openlp.android.data.Slide[]> {
+        Slide slideActivity;
+        ProgressDialog progressDialog;
+        String error;
+
+        FetchSlideItemsTask(Slide slideActivity) {
+            this.slideActivity = slideActivity;
+        }
+
+        @Override
+        protected void onPreExecute() {
+            super.onPreExecute();
+            progressDialog = ProgressDialog.show(Slide.this, getString(R.string.loading), getString(R.string.loadingSlideItems));
+        }
+
+        @Override
+        protected org.openlp.android.data.Slide[] doInBackground(String... strings) {
+            OpenLPHttpClient httpClient = new OpenLPHttpClient(getApplicationContext());
+            HttpResponse response = null;
+            String returnString = "";
+
+            Log.d(LOG_TAG, "Processing:" + Arrays.asList(strings));
+            try {
+                httpClient.setUrl(strings[0]);
+                if (httpClient.getUrl().getHost().trim().length() <= 0) {
+                    startActivity(new Intent(slideActivity, Preferences.class));
+                }
+                else {
+                    response = httpClient.execute();
+                }
+
+                if (response != null && response.getStatusLine().getStatusCode() == 200) {
+                    HttpEntity entity = response.getEntity();
+
+                    if (entity != null) {
+                        List<org.openlp.android.data.Slide> slideItemList = JSONHandler.parseSlideItemResponseJSON(entity);
+                        Log.d(LOG_TAG, String.format("slides: %s", slideItemList));
+                        return slideItemList.toArray(new org.openlp.android.data.Slide[]{});
+                    }
+                }
+                else {
+                    returnString = String.format("%s %s", getString(R.string.unable), response);
+                }
+            }
+            catch (Exception ex) {
+                try {
+                    throw new FetchItemsException(ex);
+                }
+                catch (FetchItemsException e) {
+                    returnString = String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage());
+                }
+            }
+
+            if (returnString.trim().length() > 0) {
+                error = returnString;
+                Log.e(LOG_TAG, returnString);
+            }
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(org.openlp.android.data.Slide[] slides) {
+            super.onPostExecute(slides);
+            if (slides == null) {
+                slides = new org.openlp.android.data.Slide[]{};
+            }
+            slideList.setAdapter(new SlideAdapter(context, Arrays.asList(slides)));
+            progressDialog.dismiss();
+
+            if (error != null && error.trim().length() > 0) {
+                Toast.makeText(context, error, Toast.LENGTH_LONG).show();
+            }
+        }
+
+        class FetchItemsException extends Exception {
+            FetchItemsException(Throwable throwable) {
+                super(throwable);
+            }
+        }
+    }
+}

=== modified file 'src/org/openlp/android/api/Api.java'
--- src/org/openlp/android/api/Api.java	2011-05-07 20:01:46 +0000
+++ src/org/openlp/android/api/Api.java	2011-05-30 10:27:27 +0000
@@ -95,14 +95,23 @@
     public final String LIVE_NEXT = "/api/controller/live/next";
     public final String LIVE_PREVIOUS = "/api/controller/live/previous";
     public final String LIVE_TEXT = "/api/controller/live/text";
+    public final String LIVE_SET = "/api/controller/live/set?data=";
 
     public final String SERVICE_NEXT = "/api/service/next";
     public final String SERVICE_PREVIOUS = "/api/service/previous";
     public final String SERVICE_LIST = "/api/service/list";
+    public final String SERVICE_SET = "/api/service/set?data=";
 
     public final String DISPLAY_HIDE = "/api/display/hide";
     public final String DISPLAY_SHOW = "/api/display/show";
 
     public final String ALERT = "/api/alert?data=";
 
+    public final String SEARCHABLE_PLUGINS = "/api/plugin/search";
+    /**
+     * This is a special string that uses the String.format() method.
+     * See {@link String#format(String, Object...)}
+     */
+    public final String SEARCH_PLUGIN_FORMATTED = "/api/%s/search?data=";
+    public final String SEARCH_PLUGIN_LIVE = "/api/%s/live?data=";
 }

=== added directory 'src/org/openlp/android/data'
=== added file 'src/org/openlp/android/data/Slide.java'
--- src/org/openlp/android/data/Slide.java	1970-01-01 00:00:00 +0000
+++ src/org/openlp/android/data/Slide.java	2011-05-30 10:27:27 +0000
@@ -0,0 +1,72 @@
+/******************************************************************************
+ * OpenLP - Open Source Lyrics Projection                                      *
+ * --------------------------------------------------------------------------- *
+ * Copyright (c) 2008-2011 Raoul Snyman                                        *
+ * Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael      *
+ * Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler,        *
+ * Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout,      *
+ * Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund             *
+ * --------------------------------------------------------------------------- *
+ * This program 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; version 2 of the License.                              *
+ *                                                                             *
+ * This program 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 this program; if not, write to the Free Software Foundation, Inc., 59  *
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA                          *
+ *******************************************************************************/
+package org.openlp.android.data;
+
+public class Slide {
+    private String text;
+    private boolean selected;
+    private String tag;
+    private String html;
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    public boolean isSelected() {
+        return selected;
+    }
+
+    public void setSelected(boolean selected) {
+        this.selected = selected;
+    }
+
+    public String getTag() {
+        return tag;
+    }
+
+    public void setTag(String tag) {
+        this.tag = tag;
+    }
+
+    public String getHtml() {
+        return html;
+    }
+
+    public void setHtml(String html) {
+        this.html = html;
+    }
+
+    @Override
+    public String toString() {
+        return "SlidePOJO{" +
+                "text='" + text + '\'' +
+                ", selected=" + selected +
+                ", tag='" + tag + '\'' +
+                ", html='" + html + '\'' +
+                '}';
+    }
+}

=== added file 'src/org/openlp/android/utility/GroupExpandableListAdapter.java'
--- src/org/openlp/android/utility/GroupExpandableListAdapter.java	1970-01-01 00:00:00 +0000
+++ src/org/openlp/android/utility/GroupExpandableListAdapter.java	2011-05-30 10:27:27 +0000
@@ -0,0 +1,124 @@
+/******************************************************************************
+ * OpenLP - Open Source Lyrics Projection                                      *
+ * --------------------------------------------------------------------------- *
+ * Copyright (c) 2008-2011 Raoul Snyman                                        *
+ * Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael      *
+ * Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler,        *
+ * Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout,      *
+ * Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund             *
+ * --------------------------------------------------------------------------- *
+ * This program 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; version 2 of the License.                              *
+ *                                                                             *
+ * This program 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 this program; if not, write to the Free Software Foundation, Inc., 59  *
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA                          *
+ *******************************************************************************/
+package org.openlp.android.utility;
+
+import android.app.Activity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.TextView;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.openlp.android.R;
+
+import java.util.List;
+import java.util.Map;
+
+public class GroupExpandableListAdapter extends BaseExpandableListAdapter {
+    List<String> groups;
+    List<List<Map<String, JSONArray>>> children;
+    LayoutInflater inflater;
+    Activity context;
+
+    public GroupExpandableListAdapter(Activity context, List<String> groups,
+                                      List<List<Map<String, JSONArray>>> children) {
+        this.context = context;
+        this.groups = groups;
+        this.children = children;
+        inflater = context.getLayoutInflater();
+    }
+
+    @Override
+    public int getGroupCount() {
+        return groups.size();
+    }
+
+    @Override
+    public int getChildrenCount(int position) {
+        return children.get(position).size();
+    }
+
+    @Override
+    public Object getGroup(int position) {
+        return groups.get(position);
+    }
+
+    @Override
+    public Object getChild(int rootPosition, int childPosition) {
+        return children.get(rootPosition).get(childPosition);
+    }
+
+    @Override
+    public long getGroupId(int position) {
+        return groups.indexOf(groups.get(position));
+    }
+
+    @Override
+    public long getChildId(int i, int i1) {
+        List<Map<String, JSONArray>> child = children.get(i);
+        return child.indexOf(child.get(i1));
+    }
+
+    @Override
+    public boolean hasStableIds() {
+        return false;
+    }
+
+    @Override
+    public View getGroupView(int position, boolean b, View view, ViewGroup viewGroup) {
+        if (view == null) {
+            view = inflater.inflate(R.layout.group_parent, null);
+            view.setClickable(false);
+        }
+        TextView textView = (TextView) view.findViewById(R.id.groupParentText);
+        TextView numberView = (TextView) view.findViewById(R.id.parentChildCount);
+        numberView.setText(String.format("%s", children.get(position).size()));
+        textView.setText(groups.get(position));
+        return view;
+    }
+
+    @Override
+    public View getChildView(int groupPosition, int childPosition, boolean b, View view, ViewGroup viewGroup) {
+        if (view == null) {
+            view = inflater.inflate(R.layout.group_child, null);
+            view.setClickable(false);
+        }
+        TextView childView = (TextView) view.findViewById(R.id.groupChildText);
+        List<Map<String, JSONArray>> childItem = children.get(groupPosition);
+        Map<String, JSONArray> mapItem = childItem.get(childPosition);
+        JSONArray jsonItem = mapItem.get(groups.get(groupPosition));
+        try {
+            childView.setText(String.format("%s", jsonItem.get(1)));
+        }
+        catch (JSONException e) {
+            e.printStackTrace();
+        }
+        return view;
+    }
+
+    @Override
+    public boolean isChildSelectable(int i, int i1) {
+        return true;
+    }
+}

=== added file 'src/org/openlp/android/utility/JSONHandler.java'
--- src/org/openlp/android/utility/JSONHandler.java	1970-01-01 00:00:00 +0000
+++ src/org/openlp/android/utility/JSONHandler.java	2011-05-30 10:27:27 +0000
@@ -0,0 +1,131 @@
+/******************************************************************************
+ * OpenLP - Open Source Lyrics Projection                                      *
+ * --------------------------------------------------------------------------- *
+ * Copyright (c) 2008-2011 Raoul Snyman                                        *
+ * Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael      *
+ * Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler,        *
+ * Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout,      *
+ * Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund             *
+ * --------------------------------------------------------------------------- *
+ * This program 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; version 2 of the License.                              *
+ *                                                                             *
+ * This program 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 this program; if not, write to the Free Software Foundation, Inc., 59  *
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA                          *
+ *******************************************************************************/
+package org.openlp.android.utility;
+
+import android.util.Log;
+import org.apache.http.HttpEntity;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONStringer;
+import org.openlp.android.data.Slide;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JSONHandler {
+    private static String LOG_TAG = JSONHandler.class.getName();
+
+    public static String createRequestJSON(String key, String value) throws JSONHandlerException {
+        try {
+            String responseJSON;
+            JSONObject jo = new JSONObject();
+            jo.put(key, value);
+            responseJSON = new JSONStringer().object().key("request").value(jo).endObject().toString();
+            responseJSON = URLEncoder.encode(responseJSON, "UTF-8");
+            return responseJSON;
+        }
+        catch (JSONException e) {
+            throw new JSONHandlerException(e);
+        }
+        catch (UnsupportedEncodingException e) {
+            throw new JSONHandlerException(e);
+        }
+    }
+
+    public static List<Slide> parseServiceItemResponseJSON(HttpEntity entity) throws JSONHandlerException {
+        try {
+            List<Slide> serviceItemList = new ArrayList<Slide>();
+            InputStream inputStream = entity.getContent();
+            String result = StringHelper.convertStreamToString(inputStream);
+            Log.i(LOG_TAG, result);
+
+            JSONObject jObject = new JSONObject(result);
+            JSONObject results = jObject.getJSONObject("results");
+            JSONArray items = results.getJSONArray("items");
+
+            for (int i = 0; i < items.length(); i++) {
+                JSONObject item = items.getJSONObject(i);
+                Slide slide = new Slide();
+                slide.setTag("");
+                slide.setText(item.getString("title"));
+                slide.setSelected(item.getBoolean("selected"));
+                slide.setHtml("");
+                serviceItemList.add(slide);
+            }
+
+            inputStream.close();
+            return serviceItemList;
+        }
+        catch (IOException e) {
+            throw new JSONHandlerException(e);
+        }
+        catch (JSONException e) {
+            throw new JSONHandlerException(e);
+        }
+
+    }
+
+    public static List<Slide> parseSlideItemResponseJSON(HttpEntity entity) throws JSONHandlerException {
+        try {
+            List<Slide> serviceItemList = new ArrayList<Slide>();
+            InputStream inputStream = entity.getContent();
+            String result = StringHelper.convertStreamToString(inputStream);
+            Log.i(LOG_TAG, result);
+
+            JSONObject jObject = new JSONObject(result);
+            JSONObject results = jObject.getJSONObject("results");
+            JSONArray items = results.getJSONArray("slides");
+
+            for (int i = 0; i < items.length(); i++) {
+                JSONObject item = items.getJSONObject(i);
+                Slide slide = new Slide();
+                slide.setText(item.getString("text"));
+                slide.setTag(item.getString("tag"));
+                slide.setSelected(item.getBoolean("selected"));
+                slide.setHtml(item.getString("html"));
+                serviceItemList.add(slide);
+            }
+
+            inputStream.close();
+            return serviceItemList;
+        }
+        catch (IOException e) {
+            throw new JSONHandlerException(e);
+        }
+        catch (JSONException e) {
+            throw new JSONHandlerException(e);
+        }
+
+    }
+
+    public static class JSONHandlerException extends Exception {
+        public JSONHandlerException(Throwable throwable) {
+            super(throwable);
+        }
+    }
+}

=== added file 'src/org/openlp/android/utility/SlideAdapter.java'
--- src/org/openlp/android/utility/SlideAdapter.java	1970-01-01 00:00:00 +0000
+++ src/org/openlp/android/utility/SlideAdapter.java	2011-05-30 10:27:27 +0000
@@ -0,0 +1,103 @@
+/******************************************************************************
+ * OpenLP - Open Source Lyrics Projection                                      *
+ * --------------------------------------------------------------------------- *
+ * Copyright (c) 2008-2011 Raoul Snyman                                        *
+ * Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael      *
+ * Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler,        *
+ * Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout,      *
+ * Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund             *
+ * --------------------------------------------------------------------------- *
+ * This program 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; version 2 of the License.                              *
+ *                                                                             *
+ * This program 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 this program; if not, write to the Free Software Foundation, Inc., 59  *
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA                          *
+ *******************************************************************************/
+package org.openlp.android.utility;
+
+import android.app.Activity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+import org.openlp.android.R;
+import org.openlp.android.data.Slide;
+
+import java.util.List;
+
+public class SlideAdapter extends BaseAdapter {
+    List<Slide> items;
+    Activity context;
+    LayoutInflater inflater;
+    boolean useTagDisplay = true;
+
+    public SlideAdapter(Activity context, List<Slide> items) {
+        this.context = context;
+        this.items = items;
+        inflater = context.getLayoutInflater();
+    }
+
+    public SlideAdapter(Activity context, List<Slide> items, boolean useTagDisplay) {
+        this.context = context;
+        this.items = items;
+        this.useTagDisplay = useTagDisplay;
+        inflater = context.getLayoutInflater();
+    }
+
+    @Override
+    public int getCount() {
+        return items.size();
+    }
+
+    @Override
+    public Object getItem(int i) {
+        return items.get(i);
+    }
+
+    @Override
+    public long getItemId(int i) {
+        return items.indexOf(items.get(i));
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        ViewHolder holder;
+        View view = convertView;
+
+        Slide item = items.get(position);
+
+        if (view == null) {
+            view = inflater.inflate(R.layout.slide_list_item, null);
+            holder = new ViewHolder();
+
+            holder.rowMarker = (TextView) view.findViewById(R.id.rowItemMarker);
+            holder.rowItem = (TextView) view.findViewById(R.id.rowItemText);
+
+            view.setTag(holder);
+        }
+        else {
+            holder = (ViewHolder) view.getTag();
+        }
+
+        holder.rowMarker.setText(item.getTag());
+        holder.rowItem.setText(item.getText());
+
+        if (useTagDisplay) {
+            holder.rowMarker.setMinWidth(40);
+        }
+        return view;
+    }
+
+    static class ViewHolder {
+        TextView rowMarker;
+        TextView rowItem;
+    }
+}

=== modified file 'src/org/openlp/android/utility/WebCallAsyncTask.java'
--- src/org/openlp/android/utility/WebCallAsyncTask.java	2011-05-08 11:41:49 +0000
+++ src/org/openlp/android/utility/WebCallAsyncTask.java	2011-05-30 10:27:27 +0000
@@ -29,9 +29,6 @@
 import android.widget.Toast;
 import org.openlp.android.R;
 
-import java.io.IOException;
-import java.net.URISyntaxException;
-
 /**
  * Call URL's using this task, which provides visual feedback.
  */
@@ -40,11 +37,17 @@
     private Context context;
     private ProgressDialog progressDialog;
     private String error;
+    private String apiPart;
 
     public WebCallAsyncTask(Context context) {
         this.context = context;
     }
 
+    public WebCallAsyncTask(Context context, String apiPart) {
+        this.context = context;
+        this.apiPart = apiPart;
+    }
+
     @Override
     protected void onPreExecute() {
         super.onPreExecute();
@@ -55,18 +58,19 @@
     protected Void doInBackground(String... apiCall) {
         OpenLPHttpClient httpClient = new OpenLPHttpClient(context);
         try {
-            httpClient.setUrl(apiCall[0]);
+            if (apiPart == null) {
+                httpClient.setUrl(apiCall[0]);
+            }
+            else {
+                httpClient.setUrl(String.format("%s%s", apiPart, apiCall[0]));
+            }
 
             Log.d(LOG_TAG, "Executing request: " + httpClient.getUrl().toString());
             if (httpClient.getUrl().getHost() != null) {
                 httpClient.execute();
             }
         }
-        catch (URISyntaxException e) {
-            Log.e(LOG_TAG, e.toString());
-            error = String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage());
-        }
-        catch (IOException e) {
+        catch (Exception e) {
             Log.e(LOG_TAG, e.toString());
             error = String.format("%s: %s", e.getClass().getSimpleName(), e.getMessage());
         }

=== added file 'src/org/openlp/android/utility/WebCallReturningAsyncTask.java'
--- src/org/openlp/android/utility/WebCallReturningAsyncTask.java	1970-01-01 00:00:00 +0000
+++ src/org/openlp/android/utility/WebCallReturningAsyncTask.java	2011-05-30 10:27:27 +0000
@@ -0,0 +1,107 @@
+/******************************************************************************
+ * OpenLP - Open Source Lyrics Projection                                      *
+ * --------------------------------------------------------------------------- *
+ * Copyright (c) 2008-2011 Raoul Snyman                                        *
+ * Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael      *
+ * Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler,        *
+ * Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout,      *
+ * Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund             *
+ * --------------------------------------------------------------------------- *
+ * This program 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; version 2 of the License.                              *
+ *                                                                             *
+ * This program 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 this program; if not, write to the Free Software Foundation, Inc., 59  *
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA                          *
+ *******************************************************************************/
+package org.openlp.android.utility;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.util.Log;
+import android.widget.Toast;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.openlp.android.activity.Preferences;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+public class WebCallReturningAsyncTask extends AsyncTask<String, Void, String> {
+    private final String LOG_TAG = this.getClass().getName();
+    private Activity context;
+    private String apiPart;
+
+
+    public WebCallReturningAsyncTask(Activity context) {
+        this.context = context;
+    }
+
+    public WebCallReturningAsyncTask(Activity context, String apiPart) {
+        this.context = context;
+        this.apiPart = apiPart;
+    }
+
+
+    @Override
+    protected String doInBackground(String... apiCall) {
+        OpenLPHttpClient httpClient = new OpenLPHttpClient(context);
+        HttpResponse response = null;
+
+        try {
+            if (apiPart == null) {
+                httpClient.setUrl(apiCall[0]);
+            }
+            else {
+                httpClient.setUrl(String.format("%s%s", apiPart, apiCall[0]));
+            }
+
+            if (httpClient.getUrl().getHost().trim().length() <= 0) {
+                context.startActivity(new Intent(context, Preferences.class));
+            }
+            else {
+                response = httpClient.execute();
+            }
+
+            if (response != null && response.getStatusLine().getStatusCode() == 200) {
+                HttpEntity entity = response.getEntity();
+
+                if (entity != null) {
+                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
+                    StringBuilder stringBuilder = new StringBuilder();
+
+                    String line = bufferedReader.readLine();
+                    while (line != null) {
+                        stringBuilder.append(line);
+                        line = bufferedReader.readLine();
+                    }
+
+                    Log.i(LOG_TAG, String.format("entity: %s", stringBuilder.toString()));
+                    bufferedReader.close();
+                    return stringBuilder.toString();
+                }
+            }
+        }
+        catch (Exception e) {
+            Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();
+        }
+        return null;
+    }
+
+    @Override
+    protected void onPreExecute() {
+        super.onPreExecute();
+    }
+
+    @Override
+    protected void onPostExecute(String s) {
+        super.onPostExecute(s);
+    }
+}


Follow ups