← 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)

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

Major UI/backend overhaul to achieve view swiping, including background service changes to improve user experience.
- ViewPager for swiping views
- Changed asynchronous tasks to use Service and IntentService, which allows for less user-visible interference during server calls.

Known issues:
- layout issue with view pager tile when placed at the top: overlaps with main UI and space at the bottom.
- The app might FC(force-close) on selecting the option to change the ping interval.
  + workaround: clear the application's data under applications in android's settings menu.
-- 
https://code.launchpad.net/~johanmynhardt/openlp/android/+merge/104847
Your team OpenLP Android Developers is subscribed to branch lp:openlp/android.
=== modified file 'AndroidManifest.xml'
--- AndroidManifest.xml	2012-04-14 18:09:56 +0000
+++ AndroidManifest.xml	2012-05-05 18:00:28 +0000
@@ -31,15 +31,12 @@
                     android:resource="@xml/searchable"/>
         </activity>
 
-		<service android:name=".service.PingService"/>
-
-        <activity android:name=".activity.Misc"/>
-        <activity android:name=".activity.Preferences" 
-        	android:label="@string/preferences"/>
-        <activity android:name=".activity.Slide"/>
-        <activity android:name=".activity.Service"/>
-        <activity android:name=".activity.Alert"/>
-        <activity android:name=".activity.StageView"/>                 
+		<activity android:name=".activity.Preferences" android:label="@string/preferences"/>
+		<activity android:name=".activity.PagerActivity"/>
+
+		<service android:name=".service.PingIntent"/>
+		<service android:name=".service.ApiCallIntent"/>
+
         <meta-data
                 android:name="android.app.default_searchable"
                 android:value=".activity.SearchableActivity"/>

=== added file 'libs/android-support-v4.jar'
Binary files libs/android-support-v4.jar	1970-01-01 00:00:00 +0000 and libs/android-support-v4.jar	2012-05-05 18:00:28 +0000 differ
=== modified file 'res/layout/main.xml'
--- res/layout/main.xml	2012-03-04 19:48:07 +0000
+++ res/layout/main.xml	2012-05-05 18:00:28 +0000
@@ -1,73 +1,73 @@
 <?xml version="1.0" encoding="utf-8"?>
 <TableLayout xmlns:android="http://schemas.android.com/apk/res/android";
-             android:layout_width="fill_parent"
-             android:layout_height="fill_parent"
-             android:gravity="fill"
-             android:orientation="horizontal"
-             android:layout_weight="0"
-        >
-    <TableRow
-            android:layout_weight="1"
-            android:layout_gravity="fill">
-        <ImageView
-                android:padding="20pt"
-                android:layout_span="2"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="fill"
-                android:layout_weight="1"
-                android:src="@drawable/openlp_splash_screen"
-                android:contentDescription="@string/contentDescription"
-                />
+			 android:layout_width="fill_parent"
+			 android:layout_height="fill_parent"
+			 android:gravity="fill"
+			 android:orientation="horizontal"
+			 android:layout_weight="0"
+		>
+	<TableRow
+			android:layout_weight="1"
+			android:layout_gravity="fill">
+		<ImageView
+				android:padding="20pt"
+				android:layout_span="2"
+				android:layout_width="wrap_content"
+				android:layout_height="wrap_content"
+				android:layout_gravity="fill"
+				android:layout_weight="1"
+				android:src="@drawable/openlp_splash_screen"
+				android:contentDescription="@string/contentDescription"
+				/>
 
-    </TableRow>
-    <TableRow
-            android:layout_weight="0"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content">
-        <Button
-                android:id="@+id/buttonService"
-                android:text="@string/tabService"
-                style="@style/ButtonStyle"
-                android:drawableLeft="@drawable/service"
-                />
-        <Button
-                android:id="@+id/buttonLive"
-                android:text="@string/tabLive"
-                style="@style/ButtonStyle"
-                android:drawableLeft="@drawable/live"
-                />
-    </TableRow>
-    <TableRow
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content">
-        <Button
-                android:id="@+id/buttonDisplay"
-                android:text="@string/tabDisplay"
-                style="@style/ButtonStyle"
-                android:drawableLeft="@drawable/blank"
-                />
-        <Button
-                android:id="@+id/buttonStage"
-                android:text="@string/tabStage"
-                style="@style/ButtonStyle"
-                android:drawableLeft="@drawable/stage"
-                />
-    </TableRow>
-    <TableRow
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content">
-        <Button
-                android:id="@+id/buttonAlert"
-                android:text="@string/tabAlert"
-                style="@style/ButtonStyle"
-                android:drawableLeft="@drawable/alert"
-                />
-        <Button
-                android:id="@+id/buttonSearch"
-                android:text="@string/buttonSearchText"
-                style="@style/ButtonStyle"
-                android:drawableLeft="@drawable/search"
-                />
-    </TableRow>
+	</TableRow>
+	<TableRow
+			android:layout_weight="0"
+			android:layout_width="fill_parent"
+			android:layout_height="wrap_content">
+		<Button
+				android:id="@+id/buttonService"
+				android:text="@string/tabService"
+				style="@style/ButtonStyle"
+				android:drawableLeft="@drawable/service"
+				/>
+		<Button
+				android:id="@+id/buttonLive"
+				android:text="@string/tabLive"
+				style="@style/ButtonStyle"
+				android:drawableLeft="@drawable/live"
+				/>
+	</TableRow>
+	<TableRow
+			android:layout_width="fill_parent"
+			android:layout_height="wrap_content">
+		<Button
+				android:id="@+id/buttonDisplay"
+				android:text="@string/tabDisplay"
+				style="@style/ButtonStyle"
+				android:drawableLeft="@drawable/blank"
+				/>
+		<Button
+				android:id="@+id/buttonStage"
+				android:text="@string/tabStage"
+				style="@style/ButtonStyle"
+				android:drawableLeft="@drawable/stage"
+				/>
+	</TableRow>
+	<TableRow
+			android:layout_width="fill_parent"
+			android:layout_height="wrap_content">
+		<Button
+				android:id="@+id/buttonAlert"
+				android:text="@string/tabAlert"
+				style="@style/ButtonStyle"
+				android:drawableLeft="@drawable/alert"
+				/>
+		<Button
+				android:id="@+id/buttonSearch"
+				android:text="@string/buttonSearchText"
+				style="@style/ButtonStyle"
+				android:drawableLeft="@drawable/search"
+				/>
+	</TableRow>
 </TableLayout>

=== modified file 'res/layout/slide_service.xml'
--- res/layout/slide_service.xml	2012-03-03 11:00:27 +0000
+++ res/layout/slide_service.xml	2012-05-05 18:00:28 +0000
@@ -3,13 +3,6 @@
         xmlns:android="http://schemas.android.com/apk/res/android";
         style="@style/SlideServiceTableLayout"
         >
-    <TextView
-            android:id="@+id/slide_service_title"
-            style="@style/SlideServiceTextViewTitle"
-            />
-    <View
-            style="@style/SlideServiceViewHorizontalRule"
-            />
     <ListView
             android:id="@+id/list"
             style="@style/SlideServiceListView"

=== added file 'res/layout/view_pager_layout.xml'
--- res/layout/view_pager_layout.xml	1970-01-01 00:00:00 +0000
+++ res/layout/view_pager_layout.xml	2012-05-05 18:00:28 +0000
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v4.view.ViewPager
+		xmlns:android="http://schemas.android.com/apk/res/android";
+		android:layout_width="match_parent"
+		android:layout_height="match_parent"
+		android:id="@+id/myViewPager"
+		>
+	<android.support.v4.view.PagerTitleStrip
+			android:layout_width="fill_parent"
+			android:layout_height="fill_parent"
+			android:layout_gravity="bottom"/>
+</android.support.v4.view.ViewPager>

=== added file 'res/values-nl/strings.xml'
--- res/values-nl/strings.xml	1970-01-01 00:00:00 +0000
+++ res/values-nl/strings.xml	2012-05-05 18:00:28 +0000
@@ -0,0 +1,55 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+  <string name="app_name">OpenLP</string>
+  <string name="settings">Instellingen</string>
+  <string name="preferences">Voorkeuren</string>
+  <string name="prev">Vorige</string>
+  <string name="next">Volgende</string>
+  <string name="blank">Scherm-uit</string>
+  <string name="unblank">Scherm-uit Uit</string>
+  <string name="tabAlert">Waarschuwing</string>
+  <string name="alert">Waarschuwing:</string>
+  <string name="alertHint">Voer waarschuwingsbericht in</string>
+  <string name="alertTextNull">Voer een bericht in om te versturen.</string>
+  <string name="send">Verstuur</string>
+  <string name="tabLive">Live</string>
+  <string name="tabStage">Podium</string>
+  <string name="tabDisplay">Weergave</string>
+  <string name="tabService">Liturgie</string>
+  <string name="preferenceCategoryTitleServer">Server</string>
+  <string name="url">Server</string>
+  <string name="urlHint">Hostname of IP</string>
+  <string name="port">Poort</string>
+  <string name="enableCustomTimeouts"> Aangepaste timeouts toestaan</string>
+  <string name="textSizeType">Lettergrootte</string>
+  <string name="textSizeSummary">Verander Lettergrootte Liturgie</string>
+  <string name="displayType">Scherm-uit soort </string>
+  <string name="displayBlankType">Scherm-uit soort </string>
+  <string name="displayBlankSummary">Selecteer noodzakelijk Scherm-uit soort </string>
+  <string name="displayScreen">Scherm</string>
+  <string name="displayTheme">Thema</string>
+  <string name="displayDesktop">Desktop</string>
+  <string name="displayBlankOn">Weergave herstellen van</string>
+  <string name="displayBlankOff">Scherm-uit Weergave naar</string>
+  <string name="customTimeoutsSummary">Inschakelen om timeout instellingen aan te passen</string>
+  <string name="customTimeout">Aangepaste timeout</string>
+  <string name="socketTimeout">Socket Timeout</string>
+  <string name="socketTimeoutSummary">Selecteer een waarde (milliseconden)</string>
+  <string name="connectionTimeout">Verbinding Timeout</string>
+  <string name="connectionTimedout">Verbinding timed out</string>
+  <string name="connectionTimeoutSummary">Selecteer een waarde (milliseconden)</string>
+  <string name="unable">Kan de pagina niet laden -</string>
+  <string name="connectionFailed">Verbinding mislukt</string>
+  <string name="jsonfail">Bericht opmaakfout</string>
+  <string name="loading">Verbinden…</string>
+  <string name="searching">Zoeken…</string>
+  <string name="loadingFailed">Fout - laden mislukt</string>
+  <string name="loadingServiceItems">Liturgie items Laden…</string>
+  <string name="loadingSlideItems">Dia items Laden…</string>
+  <string name="loadingStatusInfo">Status info Laden…</string>
+  <string name="searchHint">Doorzoek OpenLP</string>
+  <string name="searchResults">Doorzoek resultaten</string>
+  <string name="showingResults">Resultaten voor  \'%s\'</string>
+  <string name="contentDescription">Opstartscherm</string>
+  <string name="buttonSearchText">Zoek</string>
+</resources>

=== removed file 'res/values-nl/strings.xml'
--- res/values-nl/strings.xml	2012-03-23 16:53:04 +0000
+++ res/values-nl/strings.xml	1970-01-01 00:00:00 +0000
@@ -1,55 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<resources>
-  <string name="app_name">OpenLP</string>
-  <string name="settings">Instellingen</string>
-  <string name="preferences">Voorkeuren</string>
-  <string name="prev">Vorige</string>
-  <string name="next">Volgende</string>
-  <string name="blank">Scherm-uit</string>
-  <string name="unblank">Scherm-uit Uit</string>
-  <string name="tabAlert">Waarschuwing</string>
-  <string name="alert">Waarschuwing:</string>
-  <string name="alertHint">Voer waarschuwingsbericht in</string>
-  <string name="alertTextNull">Voer een bericht in om te versturen.</string>
-  <string name="send">Verstuur</string>
-  <string name="tabLive">Live</string>
-  <string name="tabStage">Podium</string>
-  <string name="tabDisplay">Weergave</string>
-  <string name="tabService">Liturgie</string>
-  <string name="preferenceCategoryTitleServer">Server</string>
-  <string name="url">Server</string>
-  <string name="urlHint">Hostname of IP</string>
-  <string name="port">Poort</string>
-  <string name="enableCustomTimeouts"> Aangepaste timeouts toestaan</string>
-  <string name="textSizeType">Lettergrootte</string>
-  <string name="textSizeSummary">Verander Lettergrootte Liturgie</string>
-  <string name="displayType">Scherm-uit soort </string>
-  <string name="displayBlankType">Scherm-uit soort </string>
-  <string name="displayBlankSummary">Selecteer noodzakelijk Scherm-uit soort </string>
-  <string name="displayScreen">Scherm</string>
-  <string name="displayTheme">Thema</string>
-  <string name="displayDesktop">Desktop</string>
-  <string name="displayBlankOn">Weergave herstellen van</string>
-  <string name="displayBlankOff">Scherm-uit Weergave naar</string>
-  <string name="customTimeoutsSummary">Inschakelen om timeout instellingen aan te passen</string>
-  <string name="customTimeout">Aangepaste timeout</string>
-  <string name="socketTimeout">Socket Timeout</string>
-  <string name="socketTimeoutSummary">Selecteer een waarde (milliseconden)</string>
-  <string name="connectionTimeout">Verbinding Timeout</string>
-  <string name="connectionTimedout">Verbinding timed out</string>
-  <string name="connectionTimeoutSummary">Selecteer een waarde (milliseconden)</string>
-  <string name="unable">Kan de pagina niet laden -</string>
-  <string name="connectionFailed">Verbinding mislukt</string>
-  <string name="jsonfail">Bericht opmaakfout</string>
-  <string name="loading">Verbinden…</string>
-  <string name="searching">Zoeken…</string>
-  <string name="loadingFailed">Fout - laden mislukt</string>
-  <string name="loadingServiceItems">Liturgie items Laden…</string>
-  <string name="loadingSlideItems">Dia items Laden…</string>
-  <string name="loadingStatusInfo">Status info Laden…</string>
-  <string name="searchHint">Doorzoek OpenLP</string>
-  <string name="searchResults">Doorzoek resultaten</string>
-  <string name="showingResults">Resultaten voor  \'%s\'</string>
-  <string name="contentDescription">Opstartscherm</string>
-  <string name="buttonSearchText">Zoek</string>
-</resources>

=== modified file 'res/values/backgroundTimeouts.xml'
--- res/values/backgroundTimeouts.xml	2012-04-22 09:16:42 +0000
+++ res/values/backgroundTimeouts.xml	2012-05-05 18:00:28 +0000
@@ -1,6 +1,8 @@
 <resources>
-    <string-array name="backgroundTimeEntries">
-     	<item>@string/none</item>
+	<string-array name="backgroundTimeEntries">
+		<item>@string/none</item>
+		<item>1</item>
+		<item>2</item>
 		<item>5</item>
 		<item>10</item>
 		<item>15</item>
@@ -8,11 +10,13 @@
 		<item>30</item>
 	</string-array>
 	<string-array name="backgroundTimeValues">
-		<item>0</item>	    
-		<item>1800</item>
-		<item>3600</item>
-		<item>540</item>
-		<item>7200</item>
-		<item>10800</item>
+		<item>0</item>
+		<item>1000</item>
+		<item>2000</item>
+		<item>5000</item>
+		<item>10000</item>
+		<item>15000</item>
+		<item>20000</item>
+		<item>30000</item>
 	</string-array>
-</resources>
\ No newline at end of file
+</resources>

=== modified file 'res/values/defaultValues.xml'
--- res/values/defaultValues.xml	2012-04-15 06:47:49 +0000
+++ res/values/defaultValues.xml	2012-05-05 18:00:28 +0000
@@ -7,6 +7,6 @@
     <!-- INTEGER -->
     <integer name="socketTimeoutDefaultValue">3000</integer>
     <integer name="connectionTimeoutDefaultValue">3000</integer>
-    <integer name="backgroundRefreshDefaultValue">3600</integer>    
+    <string name="backgroundRefreshDefaultValue">3600</string>
     <integer name="textSizeDefaultValue">14</integer>    
-</resources>
\ No newline at end of file
+</resources>

=== modified file 'res/values/strings.xml'
--- res/values/strings.xml	2012-04-22 09:16:42 +0000
+++ res/values/strings.xml	2012-05-05 18:00:28 +0000
@@ -56,4 +56,11 @@
     <string name="showingResults">Showing Results for \'%s\'</string>
     <string name="contentDescription">Splash Screen</string>
     <string name="buttonSearchText">Search</string>
+	<string name="dialogTitleItemOptions">Item Options:</string>
+	<string name="dialogNegativeSendLive">Send Live</string>
+	<string name="dialogPositiveAddToService">Add to Service</string>
+	<string name="couldNotHandlePollResponse">Could not handle Poll response</string>
+	<string name="requestFailed">Request Failed</string>
+	<string name="couldNotHandleLiveItems">Could not handle live items</string>
+	<string name="couldNotHandleServiceItems">Could not handle service items</string>
 </resources>

=== modified file 'res/xml/preferences.xml'
--- res/xml/preferences.xml	2012-04-15 18:53:30 +0000
+++ res/xml/preferences.xml	2012-05-05 18:00:28 +0000
@@ -44,7 +44,7 @@
                 android:summary="@string/backgroundTaskSummary"
                 android:entries="@array/backgroundTimeEntries"
                 android:entryValues="@array/backgroundTimeValues"
-                android:defaultValue="@integer/backgroundRefreshDefaultValue"/>
+                android:defaultValue="@string/backgroundRefreshDefaultValue"/>
     </PreferenceCategory>          
     <PreferenceCategory
             android:title="@string/customTimeout">

=== modified file 'src/org/openlp/android/OpenLP.java'
--- src/org/openlp/android/OpenLP.java	2012-04-22 09:16:42 +0000
+++ src/org/openlp/android/OpenLP.java	2012-05-05 18:00:28 +0000
@@ -20,17 +20,17 @@
  *******************************************************************************/
 package org.openlp.android;
 
+import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.View;
-import org.openlp.android.activity.Alert;
 import org.openlp.android.activity.DefaultActivity;
-import org.openlp.android.activity.Misc;
+import org.openlp.android.activity.PagerActivity;
 import org.openlp.android.activity.Preferences;
-import org.openlp.android.activity.Service;
-import org.openlp.android.activity.Slide;
-import org.openlp.android.activity.StageView;
+import org.openlp.android.utility.OpenLPController;
+
+import static org.openlp.android.utility.OpenLPController.getPageForButton;
 
 /**
  * OpenLP-Android initialisation point.
@@ -45,83 +45,57 @@
 		super.onCreate(savedInstanceState);
 		setContentView(R.layout.main);
 
-		if (prefs.getString(getString(R.string.keyHost), "NONE").equals("NONE")
-				|| prefs.getString(getString(R.string.keyHost), null).equals(
-						null)) {
+		doPreferenceCheck();
+		setOnClickListenerPagerForButtons();
+	}
+
+	private View.OnClickListener onClickListenerPager =
+			new View.OnClickListener() {
+				@Override
+				public void onClick(View view) {
+					Intent pager = new Intent(OpenLP.this, PagerActivity.class);
+					pager.putExtra(OpenLPController.PAGE_KEY, getPageForButton(view));
+					startActivity(pager);
+				}
+			};
+
+	private void doPreferenceCheck() {
+		if (getSharedPreferences(getString(R.string.keySharedPreferences),
+				Context.MODE_PRIVATE).getString(getString(R.string.keyHost),
+				"NONE").equals("NONE")
+				|| getSharedPreferences(
+				getString(R.string.keySharedPreferences),
+				Context.MODE_PRIVATE).getString(
+				getString(R.string.keyHost), null).equals(null)) {
 			Log.d(LOG_TAG,
 					"URL preference not set. Starting preference activity...");
 			Intent preferenceIntent = new Intent(this, Preferences.class);
 			startActivity(preferenceIntent);
 		}
-
-		findViewById(R.id.buttonService).setOnClickListener(
-				onClickListenerService);
-		findViewById(R.id.buttonLive).setOnClickListener(onClickListenerLive);
-		findViewById(R.id.buttonDisplay).setOnClickListener(
-				onClickListenerDisplay);
-		findViewById(R.id.buttonAlert).setOnClickListener(onClickListenerAlert);
-		findViewById(R.id.buttonStage).setOnClickListener(onClickListenerStage);
-		findViewById(R.id.buttonSearch).setOnClickListener(
-				onClickListenerSearch);
-	}
-
-	@Override
-	public void onResume() {
-		super.onResume();
-	}
-
-	@Override
-	public void onPause() {
-		super.onPause();
-	}
-
-	@Override
-	public void onDestroy() {
-		super.onDestroy();
-		stopService(pingIntent);
-	}
-
-	private View.OnClickListener onClickListenerService = new View.OnClickListener() {
-		@Override
-		public void onClick(View view) {
-			startActivity(new Intent(OpenLP.this, Service.class));
-		}
-	};
-
-	private View.OnClickListener onClickListenerLive = new View.OnClickListener() {
-		@Override
-		public void onClick(View view) {
-			startActivity(new Intent(OpenLP.this, Slide.class));
-		}
-	};
-
-	private View.OnClickListener onClickListenerDisplay = new View.OnClickListener() {
-		@Override
-		public void onClick(View view) {
-			startActivity(new Intent(OpenLP.this, Misc.class));
-		}
-	};
-
-	private View.OnClickListener onClickListenerAlert = new View.OnClickListener() {
-		@Override
-		public void onClick(View view) {
-			startActivity(new Intent(OpenLP.this, Alert.class));
-		}
-	};
-
-	private View.OnClickListener onClickListenerStage = new View.OnClickListener() {
-		@Override
-		public void onClick(View view) {
-			startActivity(new Intent(OpenLP.this, StageView.class));
-		}
-	};
-
-	private View.OnClickListener onClickListenerSearch = new View.OnClickListener() {
-		@Override
-		public void onClick(View view) {
-			onSearchRequested();
-		}
-	};
+	}
+
+	private void setOnClickListenerPagerForButtons() {
+		findViewById(R.id.buttonService)
+				.setOnClickListener(onClickListenerPager);
+		findViewById(R.id.buttonLive)
+				.setOnClickListener(onClickListenerPager);
+		findViewById(R.id.buttonDisplay)
+				.setOnClickListener(onClickListenerPager);
+		findViewById(R.id.buttonAlert)
+				.setOnClickListener(onClickListenerPager);
+		findViewById(R.id.buttonStage)
+				.setOnClickListener(onClickListenerPager);
+		findViewById(R.id.buttonSearch)
+				.setOnClickListener(onClickListenerSearch);
+	}
+
+	private View.OnClickListener onClickListenerSearch =
+			new View.OnClickListener() {
+				@Override
+				public void onClick(View view) {
+					onSearchRequested();
+				}
+			};
 
 	private final String LOG_TAG = OpenLP.class.getName();
 }

=== removed file 'src/org/openlp/android/activity/Alert.java'
--- src/org/openlp/android/activity/Alert.java	2012-03-13 19:02:12 +0000
+++ src/org/openlp/android/activity/Alert.java	1970-01-01 00:00:00 +0000
@@ -1,90 +0,0 @@
-/******************************************************************************
- * OpenLP - Open Source Lyrics Projection                                      *
- * --------------------------------------------------------------------------- *
- * Copyright (c) 2011-2012 Raoul Snyman                                        *
- * Portions copyright (c) 2011-2012 Tim Bentley, Johan Mynhardt, Samuel        *
- * Sjöbergsson                                                                 *
- * --------------------------------------------------------------------------- *
- * 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 java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.Toast;
-import org.openlp.android.R;
-import org.openlp.android.utility.JSONHandler;
-import org.openlp.android.utility.WebCallAsyncTask;
-
-public class Alert extends DefaultActivity {
-	private final Context context = this;
-
-	@Override
-	public void onCreate(Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
-
-		Log.i(LOG_TAG, "onCreate");
-		setContentView(R.layout.alert);
-
-		findViewById(R.id.send).setOnClickListener(mSend);
-	}
-
-	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) {
-					try {
-						alert = JSONHandler.createRequestJSON("text", 
-							URLEncoder.encode(edittext
-							.getText().toString(), "utf-8"));
-						new WebCallAsyncTask(context, ALERT).execute(alert);						
-					}
-					catch (UnsupportedEncodingException e) {
-						Toast.makeText(context, R.string.jsonfail,
-								Toast.LENGTH_LONG).show();
-						Log.e(LOG_TAG, e.toString());
-					}
-				}
-				else {
-					Toast.makeText(getBaseContext(),
-						getString(R.string.alertTextNull),
-						Toast.LENGTH_SHORT).show();
-				}
-			}
-			catch (JSONHandler.JSONHandlerException e) {
-				Toast.makeText(context, R.string.jsonfail,
-					Toast.LENGTH_LONG).show();
-				Log.e(LOG_TAG, e.toString());
-			}
-		}
-	};
-
-	@Override
-	protected void onResume() {
-		super.onResume();
-		Log.d(LOG_TAG, "Resume");
-	}
-
-	private final String LOG_TAG = Alert.class.getName();
-}

=== modified file 'src/org/openlp/android/activity/DefaultActivity.java'
--- src/org/openlp/android/activity/DefaultActivity.java	2012-04-22 09:16:42 +0000
+++ src/org/openlp/android/activity/DefaultActivity.java	2012-05-05 18:00:28 +0000
@@ -21,32 +21,15 @@
 package org.openlp.android.activity;
 
 import android.app.Activity;
-import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Bundle;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import org.openlp.android.R;
 import org.openlp.android.api.Api;
-import org.openlp.android.service.PingService;
+
 
 public abstract class DefaultActivity extends Activity implements Api {
-
-	public SharedPreferences prefs;
-	public Intent pingIntent;
-
-	@Override
-	public void onCreate(Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
-
-		prefs = getApplicationContext().getSharedPreferences(
-				getApplicationContext().getString(R.string.keySharedPreferences),
-				Context.MODE_PRIVATE);
-		pingIntent = new Intent(this, PingService.class);		
-	}
-
 	@Override
 	public boolean onCreateOptionsMenu(Menu menu) {
 		MenuInflater inflater = getMenuInflater();

=== removed file 'src/org/openlp/android/activity/Misc.java'
--- src/org/openlp/android/activity/Misc.java	2012-04-22 09:16:42 +0000
+++ src/org/openlp/android/activity/Misc.java	1970-01-01 00:00:00 +0000
@@ -1,186 +0,0 @@
-/******************************************************************************
- * OpenLP - Open Source Lyrics Projection                                      *
- * --------------------------------------------------------------------------- *
- * Copyright (c) 2011-2012 Raoul Snyman                                        *
- * Portions copyright (c) 2011-2012 Tim Bentley, Johan Mynhardt, Samuel        *
- * Sjöbergsson                                                                 *
- * --------------------------------------------------------------------------- *
- * 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 org.openlp.android.R;
-import org.openlp.android.service.PingService;
-import org.openlp.android.utility.WebCallAsyncTask;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.widget.Toast;
-import android.widget.ToggleButton;
-
-public class Misc extends DefaultActivity {
-	private final Context context = this;
-	Misc misc = this;
-	String displayType;
-
-	@Override
-	public void onCreate(Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
-
-		Log.i(LOG_TAG, "onCreate");
-		setContentView(R.layout.misc);
-
-		displayType = prefs.getString(
-			getApplicationContext().getString(R.string.keyDisplayBlankType),
-			getApplicationContext().getString(R.string.displayTypeValue));
-		Log.d(LOG_TAG, "Pref Display Type = " + displayType);
-
-		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 {
-						Log.d(LOG_TAG, "onCLick Display Type = " + displayType);
-						if (displayType
-								.equals(getString(R.string.displayScreen))) {
-							Log.d(LOG_TAG, "Blank matched");
-							new WebCallAsyncTask(context)
-									.execute(DISPLAY_HIDE_SCREEN);
-						}
-						else if (displayType
-								.equals(getString(R.string.displayTheme))) {
-							Log.d(LOG_TAG, "Theme matched");
-							new WebCallAsyncTask(context)
-									.execute(DISPLAY_HIDE_THEME);
-						}
-						else {
-							Log.d(LOG_TAG, "Desktop matched");
-							new WebCallAsyncTask(context)
-									.execute(DISPLAY_HIDE_DESKTOP);
-						}
-					}
-				}
-				catch (Exception e) {
-					Toast.makeText(context, R.string.loadingFailed,
-							Toast.LENGTH_LONG).show();
-					Log.e(LOG_TAG, e.toString());
-				}
-			}
-		});
-	}
-
-	@Override
-	protected void onResume() {
-		super.onResume();
-		Log.d(LOG_TAG, "Resume");
-		startService(pingIntent);
-		registerReceiver(broadcastReceiver, new IntentFilter(
-				PingService.BROADCAST_ACTION));
-	}
-
-	@Override
-	public void onPause() {
-		super.onPause();
-		unregisterReceiver(broadcastReceiver);
-		stopService(pingIntent);
-	}
-
-	private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
-		@Override
-		public void onReceive(Context context, Intent intent) {
-			Log.d(LOG_TAG, "onReceive");
-			updateUI(intent);
-		}
-	};
-
-	private void updateUI(Intent intent) {
-		Log.d(LOG_TAG, "updateUI");
-		Boolean isBlank = intent.getBooleanExtra("blank", false);
-		Boolean isTheme = intent.getBooleanExtra("theme", false);
-		Boolean isDisplayHidden = intent.getBooleanExtra("display", false);
-		Boolean inError = intent.getBooleanExtra("inError", true);
-		final ToggleButton toggleButton = 
-			(ToggleButton) findViewById(R.id.toggleDisplayButton);
-		if (inError) {
-			toggleButton.setEnabled(false);
-		}
-		else {
-			String onText = "";
-			String offText = "";
-
-			displayType = prefs.getString(
-				getApplicationContext().getString(R.string.keyDisplayBlankType),
-				getApplicationContext().getString(R.string.displayTypeValue));
-			Log.d(LOG_TAG, "onPostExecute Display Type = " + displayType + " "
-					+ isDisplayHidden);
-			/*
-			 * Set display blanking to the preferences value
-			 */
-			if (displayType.equals(getString(R.string.displayScreen))) {
-				Log.d(LOG_TAG, "Blank called");
-				onText = context.getString(R.string.displayScreen);
-				offText = context.getString(R.string.displayScreen);
-			}
-			else if (displayType.equals(getString(R.string.displayTheme))) {
-				Log.d(LOG_TAG, "Theme called");
-				onText = context.getString(R.string.displayTheme);
-				offText = context.getString(R.string.displayTheme);
-			}
-			else {
-				Log.d(LOG_TAG, "Desktop called");
-				onText = context.getString(R.string.displayDesktop);
-				offText = context.getString(R.string.displayDesktop);
-			}
-			/*
-			 * Set display blanked to the off value to that of the screen
-			 */
-			if (isDisplayHidden) {
-				if (isBlank) {
-					Log.d(LOG_TAG, "Hidden Blank called");
-					onText = context.getString(R.string.displayScreen);
-				}
-				else if (isTheme) {
-					Log.d(LOG_TAG, "Hidden Theme called");
-					onText = context.getString(R.string.displayTheme);
-				}
-				else {
-					Log.d(LOG_TAG, "Hidden Desktop called");
-					onText = context.getString(R.string.displayDesktop);
-				}
-			}
-			toggleButton.setTextOn(context.getString(R.string.displayBlankOn)
-					+ " " + onText);
-			toggleButton.setTextOff(context.getString(R.string.displayBlankOff)
-					+ " " + offText);
-			toggleButton.setEnabled(true);
-			toggleButton.setChecked(false);
-			if (isDisplayHidden) {
-				toggleButton.setChecked(true);
-			}
-		}
-	}
-
-	private final String LOG_TAG = Misc.class.getName();
-}

=== modified file 'src/org/openlp/android/activity/OpenLPNavigate.java'
--- src/org/openlp/android/activity/OpenLPNavigate.java	2012-03-04 10:56:34 +0000
+++ src/org/openlp/android/activity/OpenLPNavigate.java	2012-05-05 18:00:28 +0000
@@ -20,16 +20,26 @@
  *******************************************************************************/
 package org.openlp.android.activity;
 
+import org.openlp.android.api.Api;
+
 /**
- * Interface shared by {@link Service} and {@link Slide} to navigate and set data.
+ * Interface shared by  and to navigate and set data.
  */
-public interface OpenLPNavigate {
+public interface OpenLPNavigate extends Api {
+
+	public final String NAVIGATE_PREVIOUS = "previous";
+	public final String NAVIGATE_NEXT = "next";
+	public final String HIDE_SCREEN = "blank";
+	public final String HIDE_THEME = "theme";
+	public final String HIDE_DESKTOP = "desktop";
+	public final String DISPLAY_SHOW = "show";
+
 	/**
 	 * Call to the service changing service/slides.
 	 *
 	 * @param direction A direction constant from {@link org.openlp.android.api.Api}.
 	 *                  Eg.: {@link org.openlp.android.api.Api#LIVE_NEXT}
-	 *                  or {@link org.openlp.android.api.Api#SERVICE_NEXT}.
+	 *                  or {@link org.openlp.android.api.Api#SERVICE_BASE} + previous/next.
 	 */
 	void navigate(String direction);
 
@@ -42,4 +52,13 @@
 	 * @param id      Id of the item selected.
 	 */
 	void setData(String apiPart, int id);
+
+	void setDisplay(String displayRequest);
+	void poll();
+
+	/**
+	 * Replaces what we previously had as FetchItemsTask which basically ran in the UI
+	 * @param apiPart Items to fetch, eg.: {@link Api#LIVE_TEXT} or {@link Api#SERVICE_LIST}
+	 */
+	void fetchItems(String apiPart);
 }

=== added file 'src/org/openlp/android/activity/PagerActivity.java'
--- src/org/openlp/android/activity/PagerActivity.java	1970-01-01 00:00:00 +0000
+++ src/org/openlp/android/activity/PagerActivity.java	2012-05-05 18:00:28 +0000
@@ -0,0 +1,66 @@
+/******************************************************************************
+ * OpenLP - Open Source Lyrics Projection                                      *
+ * --------------------------------------------------------------------------- *
+ * Copyright (c) 2011-2012 Raoul Snyman                                        *
+ * Portions copyright (c) 2011-2012 Tim Bentley, Johan Mynhardt, Samuel        *
+ * Sjöbergsson                                                                 *
+ * --------------------------------------------------------------------------- *
+ * 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.content.Intent;
+import android.os.Bundle;
+import android.support.v4.view.ViewPager;
+import android.util.Log;
+import org.openlp.android.R;
+import org.openlp.android.utility.OpenLPController;
+
+public class PagerActivity extends DefaultActivity {
+	private ViewPager pager;
+	private OpenLPController controller;
+
+	@Override
+	protected void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		Log.i(LOG_TAG, "onCreate");
+
+		setContentView(R.layout.view_pager_layout);
+
+		controller = new OpenLPController(this);
+		pager = (ViewPager) findViewById(R.id.myViewPager);
+		pager.setAdapter(controller);
+
+		Intent intent = getIntent();
+		int selectedPage = intent.getIntExtra(OpenLPController.PAGE_KEY, 0);
+		Log.v(LOG_TAG, "Setting Page number: " + selectedPage);
+		pager.setOnPageChangeListener(controller.onPageChangeListener);
+		pager.setCurrentItem(selectedPage, true);
+		controller.onPageChangeListener.onPageSelected(selectedPage);
+	}
+
+	public void setCurrentPage(int page) {
+		pager.setCurrentItem(page, true);
+	}
+
+	@Override
+	protected void onDestroy() {
+		super.onDestroy();
+		Log.v(LOG_TAG, "Destroying pagerActivity...");
+		unregisterReceiver(controller.apiCallReceiver);
+		stopService(controller.pingIntent);
+	}
+
+	private final String LOG_TAG = PagerActivity.class.getName();
+}

=== added file 'src/org/openlp/android/activity/Search.java'
--- src/org/openlp/android/activity/Search.java	1970-01-01 00:00:00 +0000
+++ src/org/openlp/android/activity/Search.java	2012-05-05 18:00:28 +0000
@@ -0,0 +1,27 @@
+/******************************************************************************
+ * OpenLP - Open Source Lyrics Projection                                      *
+ * --------------------------------------------------------------------------- *
+ * Copyright (c) 2011-2012 Raoul Snyman                                        *
+ * Portions copyright (c) 2011-2012 Tim Bentley, Johan Mynhardt, Samuel        *
+ * Sjöbergsson                                                                 *
+ * --------------------------------------------------------------------------- *
+ * 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;
+
+public interface Search {
+	public final String ACTION = Search.class.getName().concat(".Action");
+
+	public void searchPluginCall(String apiBase, String apiData);
+}

=== added file 'src/org/openlp/android/activity/SearchService.java'
--- src/org/openlp/android/activity/SearchService.java	1970-01-01 00:00:00 +0000
+++ src/org/openlp/android/activity/SearchService.java	2012-05-05 18:00:28 +0000
@@ -0,0 +1,33 @@
+/******************************************************************************
+ * OpenLP - Open Source Lyrics Projection                                      *
+ * --------------------------------------------------------------------------- *
+ * Copyright (c) 2011-2012 Raoul Snyman                                        *
+ * Portions copyright (c) 2011-2012 Tim Bentley, Johan Mynhardt, Samuel        *
+ * Sjöbergsson                                                                 *
+ * --------------------------------------------------------------------------- *
+ * 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.content.Intent;
+import org.openlp.android.utility.JSONHandler;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+public interface SearchService {
+	public Intent getSearchPluginIntent(String apiBase, String apiData)
+		throws JSONHandler.JSONHandlerException, IOException,
+		URISyntaxException;
+}

=== modified file 'src/org/openlp/android/activity/SearchableActivity.java'
--- src/org/openlp/android/activity/SearchableActivity.java	2012-03-13 19:02:12 +0000
+++ src/org/openlp/android/activity/SearchableActivity.java	2012-05-05 18:00:28 +0000
@@ -25,8 +25,11 @@
 import android.app.Dialog;
 import android.app.ProgressDialog;
 import android.app.SearchManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.util.Log;
@@ -38,123 +41,131 @@
 import org.json.JSONObject;
 import org.openlp.android.R;
 import org.openlp.android.api.Api;
+import org.openlp.android.service.ApiCallIntent;
 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.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
 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;
 	private final int DIALOG_ITEM_OPTIONS = 0;
 	private Object dialogKey;
 	private JSONArray dialogValue;
 
+	private Search controller = new Search() {
+		@Override
+		public void searchPluginCall(String apiBase, String apiData) {
+			Intent apiCallIntent =
+				new Intent(SearchableActivity.this, ApiCallIntent.class);
+			apiCallIntent.putExtra(apiBase, apiData);
+			startService(apiCallIntent);
+		}
+	};
+
+	BroadcastReceiver apiCallReceiver = new BroadcastReceiver() {
+		@Override
+		public void onReceive(Context context, Intent intent) {
+			Log.d(LOG_TAG, String.format(
+				"Search broadcast received: context(%s), intent(%s, %s)",
+				context, intent, intent.getExtras()));
+		}
+	};
+
 	@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);
-					dialogKey = null;
-					dialogValue = null;
-					dialogKey = listView.getExpandableListAdapter()
-						.getGroup(parent);
-					dialogValue = child.get(dialogKey.toString());
-					showDialog(DIALOG_ITEM_OPTIONS);
-					return false;
-				}
-			});
+		listView.setOnChildClickListener(onChildClickListener);
+
+		IntentFilter apiCallFilter = new IntentFilter(Search.ACTION);
+		apiCallFilter.addCategory(Intent.CATEGORY_DEFAULT);
+
+		registerReceiver(apiCallReceiver, apiCallFilter);
 
 		Intent intent = getIntent();
 		if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
-			String query = ""; 
-			try {
-				query = URLEncoder.encode(intent.getStringExtra(SearchManager.QUERY), "utf-8");
-			}
-			catch (UnsupportedEncodingException e) {
-				Toast.makeText(context, R.string.jsonfail,
-						Toast.LENGTH_LONG).show();
-				Log.e(LOG_TAG, e.toString());
-			}
+			String query = intent.getStringExtra(SearchManager.QUERY);
 			doSearch(query);
 		}
 	}
 
+	private ExpandableListView.OnChildClickListener onChildClickListener =
+		new ExpandableListView.OnChildClickListener() {
+			@Override
+			public boolean onChildClick(ExpandableListView expandableListView,
+				View view, int parent, int childPosition, long l) {
+				@SuppressWarnings("unchecked") Map<String, JSONArray> child =
+					(Map<String, JSONArray>) listView.getExpandableListAdapter()
+						.getChild(parent, childPosition);
+				dialogKey = null;
+				dialogValue = null;
+				dialogKey =
+					listView.getExpandableListAdapter().getGroup(parent);
+				dialogValue = child.get(dialogKey.toString());
+				showDialog(DIALOG_ITEM_OPTIONS);
+				return false;
+			}
+		};
+
 	@Override
 	protected Dialog onCreateDialog(int id) {
 		switch (id) {
 			case DIALOG_ITEM_OPTIONS:
 				AlertDialog.Builder dialogBuilder =
-					new AlertDialog.Builder(context);
-				dialogBuilder.setTitle("Item Options:");
-				dialogBuilder.setNegativeButton("Send Live",
-					new DialogInterface.OnClickListener() {
-						@Override
-						public void onClick(DialogInterface dialogInterface,
-							int i) {
-							try {
-								new WebCallAsyncTask(context, String.format(
-									SEARCH_PLUGIN_LIVE, dialogKey))
-									.execute(JSONHandler
-										.createRequestJSON(
-											"id",
-											dialogValue.get(0)
-												.toString()));
-							}
-							catch (Exception e) {
-								Toast.makeText(context, e.getMessage(),
-									Toast.LENGTH_LONG).show();
-							}
-						}
-					});
-				dialogBuilder.setPositiveButton("Add to Service",
-					new DialogInterface.OnClickListener() {
-						@Override
-						public void onClick(DialogInterface dialogInterface,
-							int i) {
-							try {
-								new WebCallAsyncTask(context, String.format(
-									SEARCH_PLUGIN_ADD, dialogKey))
-									.execute(JSONHandler
-										.createRequestJSON(
-											"id",
-											dialogValue.get(0)
-												.toString()));
-							}
-							catch (Exception e) {
-								Toast.makeText(context, e.getMessage(),
-									Toast.LENGTH_LONG).show();
-							}
-							dialogInterface.cancel();
-						}
-					});
+					new AlertDialog.Builder(SearchableActivity.this);
+				dialogBuilder
+					.setTitle(getString(R.string.dialogTitleItemOptions));
+				dialogBuilder.setNegativeButton(
+					getString(R.string.dialogNegativeSendLive),
+					onClickListenerDialogSendLive);
+				dialogBuilder.setPositiveButton(
+					getString(R.string.dialogPositiveAddToService),
+					onClickListenerDialogAddToService);
 				return dialogBuilder.create();
 			default:
 				return null;
 		}
 	}
 
+	DialogInterface.OnClickListener onClickListenerDialogSendLive =
+		new DialogInterface.OnClickListener() {
+			@Override
+			public void onClick(DialogInterface dialogInterface, int i) {
+				controller.searchPluginCall(
+					String.format(SEARCH_PLUGIN_LIVE, dialogKey),
+					getDialogValue());
+			}
+		};
+
+	DialogInterface.OnClickListener onClickListenerDialogAddToService =
+		new DialogInterface.OnClickListener() {
+			@Override
+			public void onClick(DialogInterface dialogInterface, int i) {
+				controller.searchPluginCall(
+					String.format(SEARCH_PLUGIN_ADD, dialogKey),
+					getDialogValue());
+				dialogInterface.cancel();
+			}
+		};
+
+	private String getDialogValue() {
+		try {
+			return String.valueOf(dialogValue.get(0));
+		}
+		catch (JSONException e) {
+			Log.e(LOG_TAG, "Could not get dialogValue: " + e.getMessage());
+			Toast.makeText(SearchableActivity.this, e.getMessage(),
+				Toast.LENGTH_LONG).show();
+			return null;
+		}
+	}
+
 	@Override
 	protected void onPrepareDialog(int id, Dialog dialog) {
 		try {
@@ -169,6 +180,12 @@
 		new SearchAsync().execute(search);
 	}
 
+	@Override
+	protected void onDestroy() {
+		super.onDestroy();
+		unregisterReceiver(apiCallReceiver);
+	}
+
 	class SearchAsync extends AsyncTask<String, Void, SearchResults> {
 		ProgressDialog progressDialog;
 		String query;
@@ -179,37 +196,35 @@
 			List<String> groups = new ArrayList<String>();
 			List<List<Map<String, JSONArray>>> children =
 				new ArrayList<List<Map<String, JSONArray>>>();
-
 			AsyncTask<String, Void, String> call =
-				new WebCallReturningAsyncTask(
-					context).execute(SEARCHABLE_PLUGINS);
+				new WebCallReturningAsyncTask(SearchableActivity.this)
+					.execute(SEARCHABLE_PLUGINS);
 
 			try {
-				JSONArray array = new JSONObject(call.get().toString())
-					.getJSONObject("results").getJSONArray("items");
+				JSONArray array =
+					new JSONObject(call.get()).getJSONObject("results")
+						.getJSONArray("items");
 
 				for (int i = 0; i < array.length(); i++) {
-					String pluginString = ((JSONArray) array.get(i)).get(0)
-						.toString();
+					String pluginString =
+						((JSONArray) array.get(i)).get(0).toString();
 					groups.add(pluginString);
 
 					JSONArray resultArray = null;
 
 					AsyncTask<String, Void, String> pluginResults =
-						new WebCallReturningAsyncTask(
-							context,
-							String.format(SEARCH_PLUGIN_FORMATTED,
-								pluginString)).execute(JSONHandler
-							.createRequestJSON("text", query));
+						new WebCallReturningAsyncTask(SearchableActivity.this,
+							String
+								.format(SEARCH_PLUGIN_FORMATTED, pluginString))
+							.execute(
+								JSONHandler.createRequestJSON("text", query));
 
 					List<Map<String, JSONArray>> list =
 						new ArrayList<Map<String, JSONArray>>();
-					if (pluginResults.get() != null
-						&& pluginResults.get().toString().trim().length() >
-						0) {
-						resultArray = new JSONObject(pluginResults.get()
-							.toString()).getJSONObject("results")
-							.getJSONArray("items");
+					if (pluginResults.get() != null &&
+						pluginResults.get().trim().length() > 0) {
+						resultArray = new JSONObject(pluginResults.get())
+							.getJSONObject("results").getJSONArray("items");
 						for (int j = 0; j < resultArray.length(); j++) {
 							Map<String, JSONArray> item =
 								new HashMap<String, JSONArray>();
@@ -223,8 +238,8 @@
 			}
 			catch (Exception e) {
 				Log.e(LOG_TAG, e.toString());
-				Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG)
-					.show();
+				Toast.makeText(SearchableActivity.this, e.getMessage(),
+					Toast.LENGTH_LONG).show();
 			}
 
 			SearchResults results = new SearchResults();
@@ -236,17 +251,18 @@
 		@Override
 		protected void onPreExecute() {
 			super.onPreExecute();
-			progressDialog = ProgressDialog.show(context, null,
+			progressDialog = ProgressDialog.show(SearchableActivity.this, null,
 				getString(R.string.searching));
 		}
 
 		@Override
 		protected void onPostExecute(SearchResults results) {
 			super.onPostExecute(results);
-			listView.setAdapter(new GroupExpandableListAdapter(context, results
-				.getGroups(), results.getChildren()));
+			listView.setAdapter(
+				new GroupExpandableListAdapter(SearchableActivity.this,
+					results.getGroups(), results.getChildren()));
 			progressDialog.dismiss();
-			Toast.makeText(context,
+			Toast.makeText(SearchableActivity.this,
 				String.format(getString(R.string.showingResults), query),
 				Toast.LENGTH_SHORT).show();
 		}
@@ -273,5 +289,5 @@
 		}
 	}
 
-	private final String LOG_TAG = this.getClass().getSimpleName();
+	private final String LOG_TAG = this.getClass().getName();
 }

=== removed file 'src/org/openlp/android/activity/Service.java'
--- src/org/openlp/android/activity/Service.java	2012-04-22 09:16:42 +0000
+++ src/org/openlp/android/activity/Service.java	1970-01-01 00:00:00 +0000
@@ -1,248 +0,0 @@
-/******************************************************************************
- * OpenLP - Open Source Lyrics Projection                                      *
- * --------------------------------------------------------------------------- *
- * Copyright (c) 2011-2012 Raoul Snyman                                        *
- * Portions copyright (c) 2011-2012 Tim Bentley, Johan Mynhardt, Samuel        *
- * Sjöbergsson                                                                 *
- * --------------------------------------------------------------------------- *
- * 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 java.util.Arrays;
-import java.util.List;
-
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.openlp.android.R;
-import org.openlp.android.data.SlideItem;
-import org.openlp.android.service.PingService;
-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 android.app.Activity;
-import android.app.ProgressDialog;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.Button;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-/**
- * Activity for managing service objects.
- */
-public class Service extends DefaultActivity implements OpenLPNavigate {
-	private final Activity context = this;
-	private ListView listView;
-	private int currentService = 0;
-
-	@Override
-	public void onCreate(Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
-		Log.i(LOG_TAG, "onCreate");
-
-		setContentView(R.layout.slide_service);
-		listView = (ListView) findViewById(R.id.list);
-		findViewById(R.id.prev).setOnClickListener(onClickListenerNavigate);
-		findViewById(R.id.next).setOnClickListener(onClickListenerNavigate);
-		listView.setOnItemClickListener(onItemClickListenerService);
-		listView.setOnItemLongClickListener(adapterViewOnItemLongClickListener);
-		((TextView) findViewById(R.id.slide_service_title))
-			.setText(R.string.tabService);
-	}
-
-	private Button.OnClickListener onClickListenerNavigate = new Button.OnClickListener() {
-		@Override
-		public void onClick(View v) {
-			if (v.getId() == R.id.prev) {
-				navigate(SERVICE_PREVIOUS);
-			}
-			else if (v.getId() == R.id.next) {
-				navigate(SERVICE_NEXT);
-			}
-		}
-	};
-
-	private ListView.OnItemClickListener onItemClickListenerService = new AdapterView.OnItemClickListener() {
-		@Override
-		public void onItemClick(AdapterView<?> adapterView, View view, int i,
-				long l) {
-			setData(SERVICE_SET, i);
-		}
-	};
-
-	private AdapterView.OnItemLongClickListener adapterViewOnItemLongClickListener = new AdapterView.OnItemLongClickListener() {
-		@Override
-		public boolean onItemLongClick(AdapterView<?> adapterView, View view,
-				int i, long l) {
-			setData(SERVICE_SET, i);
-			startActivity(new Intent(context, Slide.class));
-			return true;
-		}
-	};
-
-	public void navigate(String direction) {
-		try {
-			new WebCallAsyncTask(context).execute(direction);
-		}
-		catch (Exception e) {
-			Toast.makeText(
-					getApplicationContext(),
-					String.format("%s: %s", e.getClass().getSimpleName(),
-							e.getMessage()), Toast.LENGTH_SHORT).show();
-		}
-	}
-
-	public void setData(String apiPart, int id) {
-		try {
-			String response = JSONHandler.createRequestJSON("id",
-					Integer.toString(id));
-			new WebCallAsyncTask(context, apiPart).execute(response);
-		}
-		catch (JSONHandler.JSONHandlerException e) {
-			Toast.makeText(
-					getApplicationContext(),
-					String.format("%s: %s", e.getClass().getSimpleName(),
-							e.getMessage()), Toast.LENGTH_LONG).show();
-		}
-	}
-
-	@Override
-	protected void onResume() {
-		super.onResume();
-		Log.d(LOG_TAG, "Resume");
-		startService(pingIntent);
-		registerReceiver(broadcastReceiver, new IntentFilter(
-				PingService.BROADCAST_ACTION));
-	}
-
-	@Override
-	public void onPause() {
-		super.onPause();
-		unregisterReceiver(broadcastReceiver);
-		stopService(pingIntent);
-	}
-
-	private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
-		@Override
-		public void onReceive(Context context, Intent intent) {
-			Log.d(LOG_TAG, "onReceive");
-			updateUI(intent);
-		}
-	};
-
-	private void updateUI(Intent intent) {
-		Log.d(LOG_TAG, "updateUI");
-		int service = intent.getIntExtra("service", -1);
-		/*
-		 * Service has been updated so lets refresh it
-		 */
-		Log.d(LOG_TAG, "Service update " + service + " " + currentService);		
-		if (currentService != service) {
-			currentService = service;
-			new FetchServiceItemsTask(this).execute(SERVICE_LIST);
-		}
-	}
-
-	/**
-	 * Asynchronous task to fetch the service items.
-	 */
-	class FetchServiceItemsTask extends AsyncTask<String, Void, SlideItem[]> {
-		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 SlideItem[] doInBackground(String... strings) {
-			OpenLPHttpClient httpClient = new OpenLPHttpClient(
-				getApplicationContext());
-			HttpResponse response = null;
-			error = "";
-
-			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<SlideItem> serviceItemList = JSONHandler
-							.parseServiceItemResponseJSON(entity);
-						Log.i(LOG_TAG, String.format("Service Items: %s",
-							serviceItemList));
-						return serviceItemList.toArray(new SlideItem[] {});
-					}
-				}
-				else {
-					error = String.format("%s %s", getString(R.string.unable),
-						response);
-				}
-			}
-			catch (Exception e) {
-				error = String.format("%s: %s", e.getClass().getSimpleName(),
-						e.getMessage());
-			}
-			return null;
-		}
-
-		@Override
-		protected void onPostExecute(SlideItem[] slides) {
-			super.onPostExecute(slides);
-			if (slides == null) {
-				slides = new SlideItem[] {};
-			}
-			listView.setAdapter(new SlideAdapter(context,
-					Arrays.asList(slides), false));
-			progressDialog.dismiss();
-
-			if (error != null && error.trim().length() > 0) {
-				Toast.makeText(context, R.string.loadingFailed,
-					Toast.LENGTH_LONG).show();
-				Log.e(LOG_TAG, error);
-			}
-		}
-	}
-
-	private final String LOG_TAG = Service.class.getName();
-}

=== removed file 'src/org/openlp/android/activity/Slide.java'
--- src/org/openlp/android/activity/Slide.java	2012-04-21 09:07:07 +0000
+++ src/org/openlp/android/activity/Slide.java	1970-01-01 00:00:00 +0000
@@ -1,249 +0,0 @@
-/******************************************************************************
- * OpenLP - Open Source Lyrics Projection                                      *
- * --------------------------------------------------------------------------- *
- * Copyright (c) 2011-2012 Raoul Snyman                                        *
- * Portions copyright (c) 2011-2012 Tim Bentley, Johan Mynhardt, Samuel        *
- * Sjöbergsson                                                                 *
- * --------------------------------------------------------------------------- *
- * 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.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.Button;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.openlp.android.R;
-import org.openlp.android.service.PingService;
-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 DefaultActivity implements OpenLPNavigate {
-	private final Activity context = this;
-	private ListView slideList;
-	private String currentItem = "";
-	private int currentSlide = -1;
-
-	public void onCreate(Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
-		Log.d(LOG_TAG, "onCreate");
-		setContentView(R.layout.slide_service);
-
-		slideList = (ListView) findViewById(R.id.list);
-		findViewById(R.id.prev).setOnClickListener(onClickListenerNavigate);
-		findViewById(R.id.next).setOnClickListener(onClickListenerNavigate);
-		slideList.setOnItemClickListener(onItemClickListenerSetLive);
-		((TextView) findViewById(R.id.slide_service_title))
-			.setText(R.string.tabLive);
-	}
-
-	private AdapterView.OnItemClickListener onItemClickListenerSetLive =
-		new AdapterView.OnItemClickListener() {
-			@Override
-			public void onItemClick(AdapterView<?> adapterView, View view,
-				int i, long l) {
-				setData(LIVE_SET, i);
-			}
-		};
-
-	private Button.OnClickListener onClickListenerNavigate =
-		new Button.OnClickListener() {
-			public void onClick(View v) {
-				if (v.getId() == R.id.next) {
-					navigate(LIVE_NEXT);
-				}
-				else if (v.getId() == R.id.prev) {
-					navigate(LIVE_PREVIOUS);
-				}
-			}
-		};
-
-	@Override
-	public void navigate(String direction) {
-		try {
-			new WebCallAsyncTask(context).execute(direction);
-		}
-		catch (Exception e) {
-			Toast.makeText(context, R.string.loadingFailed,
-				Toast.LENGTH_LONG).show();
-			Log.e(LOG_TAG, String.format("%s: %s", e.getClass()
-				.getSimpleName(), e.getMessage()), e);
-		}
-	}
-
-	@Override
-	public void setData(String apiPart, int id) {
-		try {
-			String response = JSONHandler.createRequestJSON("id",
-				Integer.toString(id));
-			new WebCallAsyncTask(context, apiPart).execute(response);
-		}
-		catch (Exception e) {
-			try {
-				throw new JSONHandler.JSONHandlerException(e);
-			}
-			catch (JSONHandler.JSONHandlerException e1) {
-				Toast.makeText(getApplicationContext(),
-					String.format("%s: %s", e1.getClass()
-						.getSimpleName(), e1.getMessage()),
-					Toast.LENGTH_LONG).show();
-			}
-		}
-	}
-	
-	@Override
-	protected void onResume() {
-		super.onResume();
-		Log.d(LOG_TAG, "Resume");
-		startService(pingIntent);
-		registerReceiver(broadcastReceiver, new IntentFilter(
-				PingService.BROADCAST_ACTION));
-	}
-
-	@Override
-	public void onPause() {
-		super.onPause();
-		unregisterReceiver(broadcastReceiver);
-		stopService(pingIntent);
-	}
-
-	private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
-		@Override
-		public void onReceive(Context context, Intent intent) {
-			Log.d(LOG_TAG, "onReceive");
-			updateUI(intent);
-		}
-	};	
-	
-	private void updateUI(Intent intent) {
-		Log.d(LOG_TAG, "updateUI");
-		String item = intent.getStringExtra("item");
-		int slide = intent.getIntExtra("slide",0);		
-		/*
-		 * Service Item or Slide has been updated so lets refresh it
-		 */
-		if (currentItem != item && currentSlide != slide) {
-			currentItem = item;
-			currentSlide = slide;
-			Log.d(LOG_TAG, "Updateing display for " + item + " " + slide);			
-			new FetchSlideItemsTask(this, slide).execute(LIVE_TEXT);
-		}
-	}	
-
-	class FetchSlideItemsTask extends
-		AsyncTask<String, Void, org.openlp.android.data.SlideItem[]> {
-		Slide slideActivity;
-		ProgressDialog progressDialog;
-		String error;
-		private int currentSlide;
-
-		FetchSlideItemsTask(Slide slideActivity, int slide) {
-			this.slideActivity = slideActivity;
-			this.currentSlide = slide;
-		}
-
-		@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.SlideItem[] doInBackground(
-			String... strings) {
-			OpenLPHttpClient httpClient = new OpenLPHttpClient(
-				getApplicationContext());
-			HttpResponse response = null;
-			error = "";
-
-			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.SlideItem> slideItemList =
-							JSONHandler
-								.parseSlideItemResponseJSON(entity);
-						Log.d(LOG_TAG,
-							String.format("slides: %s", slideItemList));
-						return slideItemList
-							.toArray(
-								new org.openlp.android.data.SlideItem[]{});
-					}
-				}
-				else {
-					error = String.format("%s %s",
-						getString(R.string.unable), response);
-				}
-			}
-			catch (Exception ex) {
-				Log.d(LOG_TAG, String.format("%s: %s", ex.getClass()
-					.getSimpleName(), ex.getMessage()));
-				error = String.format("%s",
-					getString(R.string.connectionTimedout));
-			}
-			return null;
-		}
-
-		@Override
-		protected void onPostExecute(
-			org.openlp.android.data.SlideItem[] slides) {
-			super.onPostExecute(slides);
-			if (slides == null) {
-				slides = new org.openlp.android.data.SlideItem[]{};
-			}
-			slideList.setAdapter(new SlideAdapter(context, Arrays
-				.asList(slides), currentSlide ));
-			progressDialog.dismiss();
-
-			if (error != null && error.trim().length() > 0) {
-				Toast.makeText(context, R.string.loadingFailed,
-					Toast.LENGTH_LONG).show();
-				Log.e(LOG_TAG, error);
-			}
-		}
-	}
-
-	private final String LOG_TAG = Slide.class.getName();
-}

=== removed file 'src/org/openlp/android/activity/StageView.java'
--- src/org/openlp/android/activity/StageView.java	2012-03-04 17:20:44 +0000
+++ src/org/openlp/android/activity/StageView.java	1970-01-01 00:00:00 +0000
@@ -1,70 +0,0 @@
-/******************************************************************************
- * OpenLP - Open Source Lyrics Projection                                      *
- * --------------------------------------------------------------------------- *
- * Copyright (c) 2011-2012 Raoul Snyman                                        *
- * Portions copyright (c) 2011-2012 Tim Bentley, Johan Mynhardt, Samuel        *
- * Sjöbergsson                                                                 *
- * --------------------------------------------------------------------------- *
- * 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.content.Context;
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.util.Log;
-import android.webkit.WebSettings;
-import android.webkit.WebView;
-import org.openlp.android.R;
-
-public class StageView extends DefaultActivity {
-	private final Context context = this;
-	private SharedPreferences preferences;
-
-	public void onCreate(Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
-		Log.d(LOG_TAG, "onCreate");
-		setContentView(R.layout.stageview);
-		preferences = getApplicationContext().getSharedPreferences(
-			getApplicationContext()
-				.getString(R.string.keySharedPreferences),
-			Context.MODE_PRIVATE);
-	}
-
-	@Override
-	protected void onResume() {
-		super.onResume();
-		Log.d(LOG_TAG, "Resume");
-		WebView myWebView = (WebView) findViewById(R.id.stageview);
-		WebSettings webSettings = myWebView.getSettings();
-		webSettings.setJavaScriptEnabled(true);
-		webSettings.setBuiltInZoomControls(true);
-		webSettings.setLoadWithOverviewMode(true);
-		webSettings.setUseWideViewPort(true);
-		myWebView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
-		myWebView.setScrollbarFadingEnabled(true);
-		String urlBase =
-			String.format("http://%s:%s/stage";, preferences.getString(
-				context.getString(R.string.keyHost),
-				context.getString(R.string.hostDefaultValue)),
-				preferences
-					.getString(context.getString(R.string.keyPort),
-						context.getString(
-							R.string.portDefaultValue)));
-		Log.d(LOG_TAG, urlBase);
-		myWebView.loadUrl(urlBase);
-	}
-
-	private final String LOG_TAG = StageView.class.getName();
-}

=== modified file 'src/org/openlp/android/api/Api.java'
--- src/org/openlp/android/api/Api.java	2012-03-04 10:56:34 +0000
+++ src/org/openlp/android/api/Api.java	2012-05-05 18:00:28 +0000
@@ -91,20 +91,20 @@
 
 public interface Api {
 
+	public final String LIVE_BASE = "/api/controller/live/";
 	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";
+	/** Use with {@link #SERVICE_BASE} + (previous|next) */
+	public final String SERVICE_BASE = "/api/service/";
+	// todo: cleanup, use {@link #SERVICE_BASE}
 	public final String SERVICE_LIST = "/api/service/list";
 	public final String SERVICE_SET = "/api/service/set?data=";
 
-	public final String DISPLAY_HIDE_SCREEN = "/api/display/blank";
-	public final String DISPLAY_HIDE_THEME = "/api/display/theme";
-	public final String DISPLAY_HIDE_DESKTOP = "/api/display/desktop";
-	public final String DISPLAY_SHOW = "/api/display/show";
+	/** Use with {@link #DISPLAY_BASE} + (blank|theme|desktop|show) */
+	public final String DISPLAY_BASE = "/api/display/";
 	public final String POLL_STATUS = "/api/poll";
 
 	public final String ALERT = "/api/alert?data=";
@@ -115,6 +115,8 @@
 	 * {@link String#format(String, Object...)}
 	 */
 	public final String SEARCH_PLUGIN_FORMATTED = "/api/%s/search?data=";
+	/** Match intent extra key with regex since multiple plugins can be inserted */
+	public final String SEARCH_PLUGIN_ADD = "/api/%s/add?data=";
+	/** Match intent extra key with regex since multiple plugins can be inserted */
 	public final String SEARCH_PLUGIN_LIVE = "/api/%s/live?data=";
-	public final String SEARCH_PLUGIN_ADD = "/api/%s/add?data=";
 }

=== modified file 'src/org/openlp/android/data/Poll.java'
--- src/org/openlp/android/data/Poll.java	2012-04-22 08:41:40 +0000
+++ src/org/openlp/android/data/Poll.java	2012-05-05 18:00:28 +0000
@@ -25,7 +25,7 @@
 
 	private int slide;
 	private String item;
-	private int service = -1;	
+	private int service = -1;
 	private boolean twelveHourDisplay = false;
 	private boolean blankedDisplayed = false;
 	private boolean themeDisplayed = false;
@@ -96,11 +96,26 @@
 	public void setDisplayHidden(boolean displayHidden) {
 		this.displayHidden = displayHidden;
 	}
+
 	public int getService() {
 		return service;
 	}
 
 	public void setService(int service) {
 		this.service = service;
-	}	
+	}
+
+	@Override
+	public String toString() {
+		return "Poll{" +
+				"slide=" + slide +
+				", item='" + item + '\'' +
+				", service=" + service +
+				", twelveHourDisplay=" + twelveHourDisplay +
+				", blankedDisplayed=" + blankedDisplayed +
+				", themeDisplayed=" + themeDisplayed +
+				", desktopDisplayed=" + desktopDisplayed +
+				", displayHidden=" + displayHidden +
+				'}';
+	}
 }

=== added file 'src/org/openlp/android/service/ApiCallIntent.java'
--- src/org/openlp/android/service/ApiCallIntent.java	1970-01-01 00:00:00 +0000
+++ src/org/openlp/android/service/ApiCallIntent.java	2012-05-05 18:00:28 +0000
@@ -0,0 +1,302 @@
+/******************************************************************************
+ * OpenLP - Open Source Lyrics Projection                                      *
+ * --------------------------------------------------------------------------- *
+ * Copyright (c) 2011-2012 Raoul Snyman                                        *
+ * Portions copyright (c) 2011-2012 Tim Bentley, Johan Mynhardt, Samuel        *
+ * Sjöbergsson                                                                 *
+ * --------------------------------------------------------------------------- *
+ * 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.service;
+
+import android.app.IntentService;
+import android.content.Intent;
+import android.util.Log;
+import org.openlp.android.activity.Search;
+import org.openlp.android.activity.SearchService;
+import org.openlp.android.api.Api;
+import org.openlp.android.utility.JSONHandler;
+import org.openlp.android.utility.OpenLPHttpClient;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+
+/**
+ * The purpose of this class is to provide a service that is separate from the UI, and assist in fetching informatin from
+ * a server.
+ */
+public class ApiCallIntent extends IntentService implements SearchService {
+	public static final String API_CALL_RECEIVE =
+		ApiCallIntent.class.getName().concat(".Receive");
+
+	public ApiCallIntent() {
+		super(ApiCallIntent.class.getName());
+	}
+
+	/**
+	 * apiPart is one of the strings set in {@link org.openlp.android.api.Api}
+	 * apiCall is when the requested apiPart takes input
+	 *
+	 * @param intent The intent requesting data
+	 */
+	@Override
+	protected void onHandleIntent(Intent intent) {
+		Log.v(LOG_TAG, "Executing WebCallIntent from: " + intent);
+		Intent apiCallReceiverIntent = null;
+
+		try {
+			if (intent.hasExtra(Api.ALERT)) {
+				String alertData = JSONHandler.createRequestJSON("text",
+					intent.getStringExtra(Api.ALERT));
+				apiCallReceiverIntent = executeAlert(alertData);
+			}
+			else if (intent.hasExtra(Api.SERVICE_BASE)) {
+				apiCallReceiverIntent = executeNavigation(Api.SERVICE_BASE,
+					intent.getStringExtra(Api.SERVICE_BASE));
+			}
+			else if (intent.hasExtra(Api.LIVE_BASE)) {
+				apiCallReceiverIntent = executeNavigation(Api.LIVE_BASE,
+					intent.getStringExtra(Api.LIVE_BASE));
+			}
+			else if (intent.hasExtra(Api.DISPLAY_BASE)) {
+				apiCallReceiverIntent = executeNavigation(Api.DISPLAY_BASE,
+					intent.getStringExtra(Api.DISPLAY_BASE));
+			}
+			else if (intent.hasExtra(Api.POLL_STATUS)) {
+				apiCallReceiverIntent = executePoll();
+			}
+			else if (intent.hasExtra(Api.LIVE_TEXT)) {
+				apiCallReceiverIntent = executeItemFetch(Api.LIVE_TEXT);
+			}
+			else if (intent.hasExtra(Api.SERVICE_LIST)) {
+				apiCallReceiverIntent = executeItemFetch(Api.SERVICE_LIST);
+			}
+			else if (intent.hasExtra(Api.LIVE_SET)) {
+				apiCallReceiverIntent = executeSetData(Api.LIVE_SET,
+					intent.getIntExtra(Api.LIVE_SET, 0));
+			}
+			else if (intent.hasExtra(Api.SERVICE_SET)) {
+				apiCallReceiverIntent = executeSetData(Api.SERVICE_SET,
+					intent.getIntExtra(Api.SERVICE_SET, 0));
+			}
+
+			if (apiCallReceiverIntent == null) {
+
+				// match wildcard intent keys
+				for (String key : intent.getExtras().keySet()) {
+					if (key.matches("/api/\\w+\\W?\\w+/live\\?data=") ||
+						key.matches("/api/\\w+\\W?\\w+/add\\?data=")) {
+						apiCallReceiverIntent = getSearchPluginIntent(key,
+							intent.getStringExtra(key));
+						break;
+					}
+				}
+
+				if (apiCallReceiverIntent == null) {
+					String nyiMessage =
+						"Not yet Implemented for calls with these extras: " +
+							intent.getExtras();
+					Log.w(LOG_TAG, nyiMessage);
+					apiCallReceiverIntent = new WebCallReceiverIntent(
+						new WebCallReceiverIntentError(nyiMessage));
+				}
+			}
+		}
+		catch (Exception e) {
+			Log.e(LOG_TAG, e.toString());
+			apiCallReceiverIntent = new WebCallReceiverIntent(
+				new WebCallReceiverIntentError(String
+					.format("%s: %s", e.getClass().getSimpleName(),
+						e.getMessage())));
+			Log.w(LOG_TAG,
+				"Sending Broadcast with error: " + apiCallReceiverIntent);
+		}
+		sendBroadcast(apiCallReceiverIntent);
+	}
+
+	@Override
+	public Intent getSearchPluginIntent(String apiBase, String apiData)
+		throws JSONHandler.JSONHandlerException, IOException,
+		URISyntaxException {
+		Intent apiCallSearchReceiverIntent =
+			new WebCallReceiverIntent(apiBase, apiData);
+		apiCallSearchReceiverIntent
+			.setAction(Search.ACTION); //override default receiver action
+		String jsonRequest = JSONHandler.createRequestJSON("id", apiData);
+
+		OpenLPHttpClient httpClient =
+			getApiConfiguredClient(apiBase, jsonRequest);
+		String result = httpClient.handleExecute();
+
+		if (result == null) {
+			apiCallSearchReceiverIntent = new WebCallReceiverIntent(
+				new WebCallReceiverIntentError(
+					"Unexpected null result during searchPluginCall."));
+			apiCallSearchReceiverIntent.setAction(Search.ACTION);
+		}
+		else {
+			Log.d(LOG_TAG, "searchPluginCall result: " + result);
+		}
+
+		return apiCallSearchReceiverIntent;
+	}
+
+	private Intent executeAlert(String alertData)
+		throws IOException, URISyntaxException {
+		Intent apiCallReceiverIntent =
+			new WebCallReceiverIntent(Api.ALERT, alertData);
+		OpenLPHttpClient httpClient =
+			getApiConfiguredClient(Api.ALERT, alertData);
+
+		String result = httpClient.handleExecute();
+		if (result == null) {
+			apiCallReceiverIntent = new WebCallReceiverIntent(
+				new WebCallReceiverIntentError(
+					"Unexpected null result during alert call."));
+		}
+		else {
+			Log.d(LOG_TAG, "Alert result: " + result);
+		}
+
+		return apiCallReceiverIntent;
+	}
+
+	private Intent executeNavigation(final String apiBase, final String apiData)
+		throws IOException, URISyntaxException {
+		Intent apiCallReceiverIntent =
+			new WebCallReceiverIntent(apiBase, apiData);
+		OpenLPHttpClient httpClient = getApiConfiguredClient(apiBase, apiData);
+
+		String result = httpClient.handleExecute();
+		if (result == null) {
+			apiCallReceiverIntent = new WebCallReceiverIntent(
+				new WebCallReceiverIntentError(
+					"Unexpected null result during navigation."));
+		}
+		else {
+			Log.d(LOG_TAG, "Navigation result: " + result);
+		}
+
+		return apiCallReceiverIntent;
+	}
+
+	private Intent executeSetData(String apiBase, int position)
+		throws JSONHandler.JSONHandlerException, IOException,
+		URISyntaxException {
+		String request =
+			JSONHandler.createRequestJSON("id", Integer.toString(position));
+		Intent apiCallreceivereIntent =
+			new WebCallReceiverIntent(apiBase, request);
+		OpenLPHttpClient httpClient = getApiConfiguredClient(apiBase, request);
+
+		String result = httpClient.handleExecute();
+		if (result == null) {
+			String message = String.format(
+				"Unexpected null result while setting list data. apiBase(%s), position(%s)",
+				apiBase, position);
+			Log.e(LOG_TAG, message);
+			apiCallreceivereIntent = new WebCallReceiverIntent(
+				new WebCallReceiverIntentError(
+					"Unexpected null result while setting list data."));
+		}
+		else {
+			Log.d(LOG_TAG, "setData result: " + result);
+		}
+
+		return apiCallreceivereIntent;
+	}
+
+	private Intent executePoll() throws IOException, URISyntaxException,
+		JSONHandler.JSONHandlerException {
+		Intent apiCallReceiverIntent =
+			new WebCallReceiverIntent(Api.POLL_STATUS, "");
+		OpenLPHttpClient httpClient = getApiConfiguredClient(Api.POLL_STATUS);
+
+		String pollJson = httpClient.handleExecute();
+		if (pollJson == null) {
+			apiCallReceiverIntent = new WebCallReceiverIntent(
+				new WebCallReceiverIntentError(
+					"Unexpected null result during status poll."));
+		}
+		else {
+			Log.d(LOG_TAG, "Poll result: " + pollJson);
+			apiCallReceiverIntent.putExtra("pollJson", pollJson);
+		}
+		return apiCallReceiverIntent;
+	}
+
+	private Intent executeItemFetch(final String apiBase)
+		throws IOException, URISyntaxException {
+		Intent apiCallReceiverIntent = new WebCallReceiverIntent(apiBase, "");
+		OpenLPHttpClient httpClient = getApiConfiguredClient(apiBase);
+
+		String itemsJson = httpClient.handleExecute();
+		if (itemsJson == null) {
+			apiCallReceiverIntent = new WebCallReceiverIntent(
+				new WebCallReceiverIntentError(
+					"Unexpected null result while fetching itmes."));
+		}
+		else {
+			Log.d(LOG_TAG, "Items JSON: " + itemsJson);
+			apiCallReceiverIntent.putExtra("itemsJson", itemsJson);
+		}
+		return apiCallReceiverIntent;
+	}
+
+	private OpenLPHttpClient getApiConfiguredClient(String apiBase,
+		String partData) throws MalformedURLException, URISyntaxException {
+		OpenLPHttpClient httpClient =
+			new OpenLPHttpClient(getApplicationContext());
+		httpClient.setUrl(String.format("%s%s", apiBase, partData));
+		return httpClient;
+	}
+
+	private OpenLPHttpClient getApiConfiguredClient(String apiBase)
+		throws MalformedURLException, URISyntaxException {
+		return getApiConfiguredClient(apiBase, "");
+	}
+
+	private final String LOG_TAG = ApiCallIntent.class.getName();
+
+	class WebCallReceiverIntent extends Intent {
+		WebCallReceiverIntent() {
+			setDefaults();
+		}
+
+		WebCallReceiverIntent(WebCallReceiverIntentError error) {
+			setDefaults();
+			WebCallReceiverIntent.this.putExtra("error", error.message);
+		}
+
+		WebCallReceiverIntent(String apiBase, String apiData) {
+			setDefaults();
+			WebCallReceiverIntent.this.putExtra("apiBase", apiBase);
+			WebCallReceiverIntent.this.putExtra("apiData", apiData);
+		}
+
+		private void setDefaults() {
+			WebCallReceiverIntent.this.setAction(API_CALL_RECEIVE);
+			WebCallReceiverIntent.this.addCategory(Intent.CATEGORY_DEFAULT);
+		}
+	}
+
+	class WebCallReceiverIntentError {
+		final String message;
+
+		WebCallReceiverIntentError(String error) {
+			this.message = error;
+		}
+	}
+}

=== added file 'src/org/openlp/android/service/PingIntent.java'
--- src/org/openlp/android/service/PingIntent.java	1970-01-01 00:00:00 +0000
+++ src/org/openlp/android/service/PingIntent.java	2012-05-05 18:00:28 +0000
@@ -0,0 +1,91 @@
+/******************************************************************************
+ * OpenLP - Open Source Lyrics Projection                                      *
+ * --------------------------------------------------------------------------- *
+ * Copyright (c) 2011-2012 Raoul Snyman                                        *
+ * Portions copyright (c) 2011-2012 Tim Bentley, Johan Mynhardt, Samuel        *
+ * Sjöbergsson                                                                 *
+ * --------------------------------------------------------------------------- *
+ * 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.service;
+
+import android.app.Service;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+import org.openlp.android.R;
+import org.openlp.android.api.Api;
+
+public class PingIntent extends Service {
+
+	private Handler handler = new Handler();
+	private Integer delay;
+
+	public PingIntent() {
+		super();
+		Log.v(LOG_TAG, "Instantiating PingIntent...");
+
+	}
+
+	@Override
+	public IBinder onBind(Intent intent) {
+		return null;
+	}
+
+	@Override
+	public void onStart(Intent intent, int startId) {
+		String PREFERENCES_KEY =
+			getApplicationContext().getString(R.string.keySharedPreferences);
+		SharedPreferences preferences = getApplicationContext()
+			.getSharedPreferences(PREFERENCES_KEY, MODE_PRIVATE);
+		Resources resources = getApplicationContext().getResources();
+		delay = Integer.valueOf(
+			resources.getString(R.string.backgroundRefreshDefaultValue));
+		Log.v(LOG_TAG, "Starting PingIntent...");
+		super.onStart(intent, startId);
+		delay = Integer.parseInt(preferences
+			.getString(resources.getString(R.string.keyBackgroundService),
+				delay.toString()));
+
+		Log.d(LOG_TAG, "PingIntent delay: " + delay);
+		handler.removeCallbacks(r);
+		handler.postDelayed(r, delay);
+	}
+
+	Runnable r = new Runnable() {
+
+		@Override
+		public void run() {
+			Intent apiCallPingIntent =
+				new Intent(getApplicationContext(), ApiCallIntent.class);
+			apiCallPingIntent.putExtra(Api.POLL_STATUS, "");
+			startService(apiCallPingIntent);
+			if (delay > 0) {
+				handler.postDelayed(r, delay);
+			}
+		}
+	};
+
+	@Override
+	public void onDestroy() {
+		Log.v(LOG_TAG, "Stopping PingIntent...");
+		handler.removeCallbacks(r);
+		super.onDestroy();
+	}
+
+	private static final String LOG_TAG = PingIntent.class.getName();
+}

=== removed file 'src/org/openlp/android/service/PingService.java'
--- src/org/openlp/android/service/PingService.java	2012-04-22 09:16:42 +0000
+++ src/org/openlp/android/service/PingService.java	1970-01-01 00:00:00 +0000
@@ -1,164 +0,0 @@
-/******************************************************************************
- * OpenLP - Open Source Lyrics Projection                                      *
- * --------------------------------------------------------------------------- *
- * Copyright (c) 2011-2012 Raoul Snyman                                        *
- * Portions copyright (c) 2011-2012 Tim Bentley, Johan Mynhardt, Samuel        *
- * Sjöbergsson                                                                 *
- * --------------------------------------------------------------------------- *
- * 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.service;
-
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.openlp.android.R;
-import org.openlp.android.data.Poll;
-import org.openlp.android.utility.JSONHandler;
-import org.openlp.android.utility.OpenLPHttpClient;
-import org.openlp.android.api.Api;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Handler;
-import android.os.IBinder;
-import android.util.Log;
-
-import android.app.Service;
-
-/**
- * Provides a background service started by activities to Poll the status 
- * service to get the latest state of OpenLP.
- * Each Activity will be responsible for handling the returned data,
- * If the poll fails it will stop the timer to prevent it from running
- * when it is not required.
- */
-public class PingService extends Service implements Api {
-
-	public static final String BROADCAST_ACTION = "org.openlp.PingService.Poll";
-	public SharedPreferences prefs;
-	private final Handler handler = new Handler();
-	private Intent intent;
-	private Long delay;
-	private Boolean runOnce = false;
-
-	private Context ctx;
-
-	public IBinder onBind(Intent arg0) {
-		return null;
-	}
-
-	@Override
-	public void onCreate() {
-		Log.d(LOG_TAG, "onCreate called");
-		super.onCreate();
-		prefs = getApplicationContext().getSharedPreferences(
-				getApplicationContext()
-						.getString(R.string.keySharedPreferences),
-				Context.MODE_PRIVATE);
-		ctx = this;
-		intent = new Intent(BROADCAST_ACTION);		
-	}
-	
-	@Override
-	public void onStart(Intent intent, int startId){
-		Log.d(LOG_TAG, "onStart called");		
-		handler.removeCallbacks(startPoll);
-		int def = ctx.getResources().getInteger(
-				R.integer.backgroundRefreshDefaultValue);
-		delay = Long.parseLong(prefs.getString(
-				getString(R.string.keyBackgroundService),
-				String.valueOf(def)));
-		/*
-		 * Allow for a no polling but the need to run one loop to update UI.
-		 */
-		runOnce = false;
-		if (delay == 0){
-			delay = 1L;
-			runOnce = true; 
-		}
-		Log.d(LOG_TAG, "Config" + runOnce + " " + delay);
-		handler.postDelayed(startPoll, delay);
-	}
-	
-	private Runnable startPoll = new Runnable() {
-		public void run() {
-			Log.d(LOG_TAG, "Task fired");
-			OpenLPHttpClient httpClient = new OpenLPHttpClient(
-					getApplicationContext());
-			HttpResponse response = null;
-			String error = "";
-
-			try {
-				httpClient.setUrl(POLL_STATUS);
-				if (httpClient.getUrl().getHost().trim().length() <= 0) {
-					intent.putExtra("inError", true);						
-					sendBroadcast(intent);
-					handler.postDelayed(this, 1);
-					handler.removeCallbacks(startPoll);
-				}
-				else {
-					response = httpClient.execute();
-				}
-				if (response != null
-						&& response.getStatusLine().getStatusCode() == 200) {
-					HttpEntity entity = response.getEntity();
-
-					if (entity != null) {
-						Poll poll = JSONHandler.parsePollResponseJSON(entity);
-						intent.putExtra("service", poll.getService());
-						intent.putExtra("item", poll.getItem());						
-						intent.putExtra("slide", poll.getSlide());
-						intent.putExtra("blank", poll.isBlankedDisplayed());
-						intent.putExtra("theme", poll.isThemeDisplayed());
-						intent.putExtra("desktop", poll.isDesktopDisplayed());
-						intent.putExtra("display", poll.isDisplayHidden());
-						intent.putExtra("inError", false);						
-						sendBroadcast(intent);
-						handler.postDelayed(this, delay);
-					}
-				}
-				else {
-					error = String.format("%s %s", getString(R.string.unable),
-							response);
-				}
-			}
-			catch (Exception e) {
-				error = String.format("%s: %s", e.getClass().getSimpleName(),
-						e.getMessage());
-			}
-			if (error != null && error.trim().length() > 0) {
-				Log.d(LOG_TAG, "HTTP request error " + error);
-				intent.putExtra("inError", true);						
-				sendBroadcast(intent);
-				handler.postDelayed(this, 1);
-				handler.removeCallbacks(startPoll);
-			}
-			/*
-			 * Stop once we have run the process.
-			 */
-			if (runOnce){
-				handler.removeCallbacks(startPoll);				
-			}
-		}
-	};
-
-	public void onDestroy() {
-		super.onDestroy();
-		Log.d(LOG_TAG, "onDestroy");
-		handler.removeCallbacks(startPoll);
-	}
-
-	private final String LOG_TAG = PingService.class.getName();
-}
\ No newline at end of file

=== modified file 'src/org/openlp/android/utility/JSONHandler.java'
--- src/org/openlp/android/utility/JSONHandler.java	2012-04-22 08:41:40 +0000
+++ src/org/openlp/android/utility/JSONHandler.java	2012-05-05 18:00:28 +0000
@@ -61,12 +61,24 @@
 		HttpEntity entity)
 		throws JSONHandlerException {
 		try {
-			List<SlideItem> serviceItemList = new ArrayList<SlideItem>();
+			List<SlideItem> serviceItemList;
 			InputStream inputStream = entity.getContent();
 			String result = StringHelper.convertStreamToString(inputStream);
-			Log.i(LOG_TAG, result);
+			Log.v(LOG_TAG, result);
+			serviceItemList = getServiceItemsFromString(result);
+			inputStream.close();
+			return serviceItemList;
+		}
+		catch (IOException e) {
+			throw new JSONHandlerException(e);
+		}
+	}
 
-			JSONObject jObject = new JSONObject(result);
+	public static List<SlideItem> getServiceItemsFromString(String itemsJson)
+		throws JSONHandlerException {
+		try {
+			List<SlideItem> serviceItemList = new ArrayList<SlideItem>();
+			JSONObject jObject = new JSONObject(itemsJson);
 			JSONObject results = jObject.getJSONObject("results");
 			JSONArray items = results.getJSONArray("items");
 
@@ -79,31 +91,37 @@
 				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<SlideItem> parseSlideItemResponseJSON(HttpEntity entity)
 		throws JSONHandlerException {
 		try {
-			List<SlideItem> serviceItemList = new ArrayList<SlideItem>();
+			List<SlideItem> serviceItemList;
 			InputStream inputStream = entity.getContent();
 			String result = StringHelper.convertStreamToString(inputStream);
-			Log.i(LOG_TAG, result);
+			Log.v(LOG_TAG, result);
+			serviceItemList = getSlideItemsFromString(result);
+			inputStream.close();
+			return serviceItemList;
+		}
+		catch (IOException e) {
+			throw new JSONHandlerException(e);
+		}
+	}
 
-			JSONObject jObject = new JSONObject(result);
+	public static List<SlideItem> getSlideItemsFromString(String itemJson)
+		throws JSONHandlerException {
+		try {
+			JSONObject jObject = new JSONObject(itemJson);
 			JSONObject results = jObject.getJSONObject("results");
 			JSONArray items = results.getJSONArray("slides");
 
+			List<SlideItem> serviceItemList = new ArrayList<SlideItem>();
 			for (int i = 0; i < items.length(); i++) {
 				JSONObject item = items.getJSONObject(i);
 				SlideItem slide = new SlideItem();
@@ -113,57 +131,40 @@
 				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 Poll parsePollResponseJSON(HttpEntity entity)
+	public static Poll getPollFromString(String pollJson)
 		throws JSONHandlerException {
+		Log.v(LOG_TAG, String.format("parsePollResponseJSON: " + pollJson));
 		try {
-			InputStream inputStream = entity.getContent();
-			String result = StringHelper.convertStreamToString(inputStream);
-			Log.i(LOG_TAG, result);
-
-			JSONObject jObject = new JSONObject(result);
+			JSONObject jObject = new JSONObject(pollJson);
 			JSONObject results = jObject.getJSONObject("results");
 			Poll poll = new Poll();
 			poll.setSlide(results.getInt("slide"));
-			try {
-				poll.setService(results.getInt("service"));
-			}
-				catch (JSONException e) {
-					Log.d(LOG_TAG, "Missing service in JSON Openlp too Old");					
-					poll.setService(-1);					
-			}			
 			poll.setItem(results.getString("item"));
 			poll.setTwelveHourDisplay(results.getBoolean("twelve"));
 			poll.setBlankedDisplayed(results.getBoolean("blank"));
 			poll.setThemeDisplayed(results.getBoolean("theme"));
 			poll.setDesktopDisplayed(results.getBoolean("display"));
-			Log.d(LOG_TAG, poll.toString());
-			inputStream.close();
+			if (!results.has("service")) {
+				Log.w(LOG_TAG,
+					"Current OpenLP too old. Missing \"service\" (OpenLP < 1941)");
+			}
+			poll.setService(
+				results.has("service") ? results.getInt("service") : -1);
 			return poll;
 		}
-		catch (IOException e) {
-			throw new JSONHandlerException(e);
-		}
 		catch (JSONException e) {
 			throw new JSONHandlerException(e);
 		}
 	}
 
 	public static class JSONHandlerException extends Exception {
-		/**
-		 *
-		 */
 		private static final long serialVersionUID = -6772307308404816615L;
 
 		public JSONHandlerException(Throwable throwable) {

=== added file 'src/org/openlp/android/utility/OpenLPController.java'
--- src/org/openlp/android/utility/OpenLPController.java	1970-01-01 00:00:00 +0000
+++ src/org/openlp/android/utility/OpenLPController.java	2012-05-05 18:00:28 +0000
@@ -0,0 +1,619 @@
+/******************************************************************************
+ * OpenLP - Open Source Lyrics Projection                                      *
+ * --------------------------------------------------------------------------- *
+ * Copyright (c) 2011-2012 Raoul Snyman                                        *
+ * Portions copyright (c) 2011-2012 Tim Bentley, Johan Mynhardt, Samuel        *
+ * Sjöbergsson                                                                 *
+ * --------------------------------------------------------------------------- *
+ * 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.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ListView;
+import android.widget.Toast;
+import android.widget.ToggleButton;
+import org.openlp.android.R;
+import org.openlp.android.activity.OpenLPNavigate;
+import org.openlp.android.activity.PagerActivity;
+import org.openlp.android.api.Api;
+import org.openlp.android.data.Poll;
+import org.openlp.android.data.SlideItem;
+import org.openlp.android.service.ApiCallIntent;
+import org.openlp.android.service.PingIntent;
+
+import java.util.List;
+
+public class OpenLPController extends PagerAdapter {
+
+	private static Activity context;
+	private ListView listViewService;
+	private ListView listViewLive;
+	private int currentService = -1;
+	private int currentLive = -1;
+	private String itemId = "";
+
+	private static int currentPage = 0;
+
+	public static final String PAGE_KEY = "openlp.pageKey";
+	public static final int PAGE_SERVICE = 0;
+	public static final int PAGE_LIVE = 1;
+	public static final int PAGE_DISPLAY = 2;
+	public static final int PAGE_STAGE = 3;
+	public static final int PAGE_ALERT = 4;
+	public static final int PAGE_SEARCH = 5;
+
+	private String displayType;
+	private WebView webView;
+	private SharedPreferences preferences;
+
+	public Intent pingIntent;
+	public Intent apiCallIntent;
+
+	public OpenLPController(Activity context) {
+		OpenLPController.context = context;
+		preferences = context.getApplicationContext().getSharedPreferences(
+			context.getString(R.string.keySharedPreferences),
+			Context.MODE_PRIVATE);
+		pingIntent = new Intent(context, PingIntent.class);
+		apiCallIntent = new Intent(context, ApiCallIntent.class);
+
+		IntentFilter apiCallFilter =
+			new IntentFilter(ApiCallIntent.API_CALL_RECEIVE);
+		apiCallFilter.addCategory(Intent.CATEGORY_DEFAULT);
+		context.registerReceiver(apiCallReceiver, apiCallFilter);
+	}
+
+	public final BroadcastReceiver apiCallReceiver = new BroadcastReceiver() {
+		@Override
+		public void onReceive(Context broadcastedContext, Intent intent) {
+			Log.v(LOG_TAG, String
+				.format("%s broadcast received: intent(%s), context(%s)",
+					ApiCallIntent.API_CALL_RECEIVE, intent,
+					broadcastedContext));
+			if (intent.hasExtra("error")) {
+				context.stopService(pingIntent);
+				Log.w(LOG_TAG, "Stopping PingIntent. Got erroneous intent: " +
+					intent.getStringExtra("error"));
+				Toast.makeText(broadcastedContext, String
+					.format("%s: %s", context.getString(R.string.requestFailed),
+						intent.getStringExtra("error")), Toast.LENGTH_LONG)
+					.show();
+			}
+			else {
+				if (intent.hasExtra("apiBase")) {
+					String apiBase = intent.getStringExtra("apiBase");
+					if (apiBase.equals(Api.ALERT)) {
+						Toast.makeText(broadcastedContext, "Alert sent!",
+							Toast.LENGTH_SHORT).show();
+					}
+					if (apiBase.equals(Api.DISPLAY_BASE)) {
+						controller.poll();
+					}
+					if (apiBase.equals(Api.POLL_STATUS)) {
+						handlePollResponse(intent.getStringExtra("pollJson"));
+					}
+					if (apiBase.equals(Api.LIVE_TEXT)) {
+						handleLiveItemsResponse(
+							intent.getStringExtra("itemsJson"));
+					}
+					if (apiBase.equals(Api.SERVICE_LIST)) {
+						handleServiceItemsResponse(
+							intent.getStringExtra("itemsJson"));
+					}
+				}
+			}
+		}
+	};
+
+	private void handlePollResponse(final String pollResponse) {
+		try {
+
+			Poll poll = JSONHandler.getPollFromString(pollResponse);
+			if (poll != null) {
+				if (currentPage == PAGE_DISPLAY) {
+					setDisplayFromPoll(poll);
+				}
+				else {
+					setServiceOrLiveFromPoll(poll);
+				}
+			}
+			else {
+				Log.w(LOG_TAG, "Parsed poll is null. This is not expected!");
+			}
+		}
+		catch (JSONHandler.JSONHandlerException e) {
+			Log.e(LOG_TAG, e.toString());
+			Toast.makeText(context, String.format("%s: %s",
+				context.getString(R.string.couldNotHandlePollResponse),
+				e.getMessage()), Toast.LENGTH_LONG).show();
+		}
+	}
+
+	private void setDisplayFromPoll(Poll poll) {
+		final ToggleButton toggleButton = (ToggleButton)
+			context.findViewById(R.id.toggleDisplayButton);
+		if (poll == null) {
+			toggleButton.setEnabled(false);
+		}
+		else {
+			String onText;
+			String offText;
+
+			displayType = getDisplayType();
+			Log.d(LOG_TAG, "onPostExecute Display Type = " + displayType
+				+ " " + poll.isDisplayHidden());
+			if (displayType.equals(context.getString(R.string.displayScreen))) {
+				Log.v(LOG_TAG, "Blank called");
+				onText = context.getString(R.string.displayScreen);
+				offText = context.getString(R.string.displayScreen);
+			}
+			else if (displayType
+				.equals(context.getString(R.string.displayTheme))) {
+				Log.v(LOG_TAG, "Theme called");
+				onText = context.getString(R.string.displayTheme);
+				offText = context.getString(R.string.displayTheme);
+			}
+			else {
+				Log.v(LOG_TAG, "Desktop called");
+				onText = context.getString(R.string.displayDesktop);
+				offText = context.getString(R.string.displayDesktop);
+			}
+			/*
+				Set display blanked to the off value to that of the screen
+			*/
+			if (poll.isDisplayHidden()) {
+				if (poll.isBlankedDisplayed()) {
+					Log.v(LOG_TAG, "Hidden Blank called");
+					onText = context.getString(R.string.displayScreen);
+				}
+				else if (poll.isThemeDisplayed()) {
+					Log.v(LOG_TAG, "Hidden Theme called");
+					onText = context.getString(R.string.displayTheme);
+				}
+				else {
+					Log.v(LOG_TAG, "Hidden Desktop called");
+					onText = context.getString(R.string.displayDesktop);
+				}
+			}
+			toggleButton.setTextOn(
+				context.getString(R.string.displayBlankOn) + " " + onText);
+			toggleButton.setTextOff(
+				context.getString(R.string.displayBlankOff) + " " + offText);
+			toggleButton.setEnabled(true);
+			toggleButton.setChecked(false);
+			if (poll.isDisplayHidden()) {
+				toggleButton.setChecked(true);
+			}
+		}
+	}
+
+	private void setServiceOrLiveFromPoll(Poll poll) {
+		if (currentPage == PAGE_LIVE) {
+			if (!itemId.equals(poll.getItem()) ||
+				currentLive != poll.getSlide()) {
+				Log.v(LOG_TAG, "Slide Changed. Polling update...");
+				currentLive = poll.getSlide();
+				itemId = poll.getItem();
+				controller.fetchItems(Api.LIVE_TEXT);
+			}
+		}
+		else if (currentPage == PAGE_SERVICE) {
+			if (currentService < poll.getService()) {
+				Log.v(LOG_TAG, "Service Changed. Polling update...");
+				currentService = poll.getService();
+				controller.fetchItems(Api.SERVICE_LIST);
+			}
+		}
+	}
+
+	private void handleLiveItemsResponse(final String itemsJson) {
+		try {
+			List<SlideItem> liveItems =
+				JSONHandler.getSlideItemsFromString(itemsJson);
+			listViewLive
+				.setAdapter(new SlideAdapter(context, liveItems, currentLive));
+		}
+		catch (JSONHandler.JSONHandlerException e) {
+			Log.e(LOG_TAG, e.toString());
+			Toast.makeText(context, String.format("%s: %s",
+				context.getString(R.string.couldNotHandleLiveItems),
+				e.getMessage()), Toast.LENGTH_LONG).show();
+		}
+	}
+
+	private void handleServiceItemsResponse(final String itemsJson) {
+		try {
+			List<SlideItem> serviceItems =
+				JSONHandler.getServiceItemsFromString(itemsJson);
+			listViewService.setAdapter(
+				new SlideAdapter(context, serviceItems, currentService));
+		}
+		catch (JSONHandler.JSONHandlerException e) {
+			Log.e(LOG_TAG, e.toString());
+			Toast.makeText(context, String.format("%s: %s",
+				context.getString(R.string.couldNotHandleServiceItems),
+				e.getMessage()), Toast.LENGTH_LONG).show();
+		}
+	}
+
+	private OpenLPNavigate controller = new OpenLPNavigate() {
+		@Override
+		public void navigate(final String navigationRequest) {
+			Intent navigationIntent = new Intent(context, ApiCallIntent.class);
+			if (currentPage == PAGE_SERVICE) {
+				navigationIntent.putExtra(Api.SERVICE_BASE, navigationRequest);
+			}
+			if (currentPage == PAGE_LIVE) {
+				navigationIntent.putExtra(Api.LIVE_BASE, navigationRequest);
+			}
+			context.startService(navigationIntent);
+		}
+
+		@Override
+		public void setData(String apiPart, int id) {
+			Intent setDataIntent = new Intent(context, ApiCallIntent.class);
+			setDataIntent.putExtra(apiPart, id);
+			context.startService(setDataIntent);
+		}
+
+		@Override
+		public void setDisplay(String displayRequest) {
+			Log.d(LOG_TAG, String
+				.format("Setting Display: displayRequest(%s)", displayRequest));
+			Intent displayIntent = new Intent(context, ApiCallIntent.class);
+			displayIntent.putExtra(Api.DISPLAY_BASE, displayRequest);
+			context.startService(displayIntent);
+		}
+
+		@Override
+		public void poll() {
+			Intent pollIntent = new Intent(context, ApiCallIntent.class);
+			pollIntent.putExtra(Api.POLL_STATUS, "");
+			context.startService(pollIntent);
+		}
+
+		@Override
+		public void fetchItems(String apiPart) {
+			Intent fetchItemsIntent = new Intent(context, ApiCallIntent.class);
+			fetchItemsIntent.putExtra(apiPart, "");
+			context.startService(fetchItemsIntent);
+		}
+	};
+
+	@Override
+	public Object instantiateItem(final ViewGroup container, int position) {
+		LayoutInflater inflater = (LayoutInflater) container.getContext()
+			.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+		int res = 0;
+		try {
+			res = getLayoutForPosition(position);
+		}
+		catch (NoLayoutForPositionException e) {
+			Log.e(LOG_TAG, e.getMessage());
+		}
+
+		View view = inflater.inflate(res, null);
+
+		if (position == PAGE_SERVICE || position == PAGE_LIVE) {
+			if (position == PAGE_SERVICE) {
+				listViewService = (ListView) view.findViewById(R.id.list);
+				listViewService
+					.setOnItemClickListener(onItemClickListenerService);
+				listViewService.setOnItemLongClickListener(
+					adapterViewOnItemLongClickListener);
+			}
+
+			if (position == PAGE_LIVE) {
+				listViewLive = (ListView) view.findViewById(R.id.list);
+				listViewLive.setOnItemClickListener(onItemClickListenerSetLive);
+			}
+
+			view.findViewById(R.id.prev)
+				.setOnClickListener(onClickListenerNavigate);
+			view.findViewById(R.id.next)
+				.setOnClickListener(onClickListenerNavigate);
+		}
+
+		if (position == PAGE_STAGE) {
+			webView = getWebViewFromView(view);
+		}
+		if (position == PAGE_ALERT) {
+			view.findViewById(R.id.send).setOnClickListener(mSend);
+		}
+		if (position == PAGE_DISPLAY) {
+			view.findViewById(R.id.toggleDisplayButton)
+				.setOnClickListener(onClickListenerToggleDisplay);
+		}
+
+		container.addView(view, 0);
+		return view;
+	}
+
+	private WebView getWebViewFromView(View view) {
+		WebView myWebView = (WebView) view.findViewById(R.id.stageview);
+		WebSettings webSettings = myWebView.getSettings();
+		webSettings.setJavaScriptEnabled(true);
+		webSettings.setBuiltInZoomControls(true);
+		webSettings.setLoadWithOverviewMode(true);
+		webSettings.setUseWideViewPort(true);
+		myWebView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
+		myWebView.setScrollbarFadingEnabled(true);
+
+		myWebView.loadUrl(getUrlBase());
+
+		return myWebView;
+	}
+
+	public static int getPageForButton(View view) {
+		final int buttonId = view.getId();
+		if (buttonId == R.id.buttonAlert) {
+			return PAGE_ALERT;
+		}
+		if (buttonId == R.id.buttonDisplay) {
+			return PAGE_DISPLAY;
+		}
+		if (buttonId == R.id.buttonLive) {
+			return PAGE_LIVE;
+		}
+		if (buttonId == R.id.buttonSearch) {
+			return PAGE_SEARCH;
+		}
+		if (buttonId == R.id.buttonService) {
+			return PAGE_SERVICE;
+		}
+		if (buttonId == R.id.buttonStage) {
+			return PAGE_STAGE;
+		}
+
+		Log.e(OpenLPController.class.getName(),
+			"No Button connected to the requested view, with id: " + buttonId);
+		return 0;
+	}
+
+	public ViewPager.OnPageChangeListener onPageChangeListener =
+		new ViewPager.OnPageChangeListener() {
+			@Override
+			public void onPageScrolled(int i, float v, int i1) {
+			}
+
+			@Override
+			public void onPageSelected(int selectedPage) {
+				Log.d(LOG_TAG, String
+					.format("Selected Page: position(%s) title(%s)",
+						selectedPage, getPageTitle(selectedPage)));
+				currentPage = selectedPage;
+
+				if (currentPage == PAGE_SERVICE || currentPage == PAGE_LIVE) {
+					context.startService(pingIntent);
+					if (currentPage == PAGE_SERVICE) {
+						controller.fetchItems(Api.SERVICE_LIST);
+					}
+					if (currentPage == PAGE_LIVE) {
+						controller.fetchItems(Api.LIVE_TEXT);
+					}
+				}
+				else {
+					context.stopService(pingIntent);
+				}
+
+				if (currentPage == PAGE_STAGE) {
+					if (webView == null) {
+						webView = getWebViewFromView(context.getLayoutInflater()
+							.inflate(R.layout.stageview, null));
+					}
+				}
+
+				if (currentPage == PAGE_DISPLAY) {
+					controller.poll();
+				}
+			}
+
+			@Override
+			public void onPageScrollStateChanged(int i) {
+			}
+		};
+
+
+	@Override
+	public CharSequence getPageTitle(int position) {
+		String page = "none";
+		switch (position) {
+			case PAGE_SERVICE:
+				page = context.getString(R.string.tabService);
+				break;
+			case PAGE_LIVE:
+				page = context.getString(R.string.tabLive);
+				break;
+			case PAGE_DISPLAY:
+				page = context.getString(R.string.tabDisplay);
+				break;
+			case PAGE_STAGE:
+				page = context.getString(R.string.tabStage);
+				break;
+			case PAGE_ALERT:
+				page = context.getString(R.string.tabAlert);
+				break;
+			case PAGE_SEARCH:
+				page = context.getString(R.string.buttonSearchText);
+				break;
+		}
+		return page;
+	}
+
+	private int getLayoutForPosition(int position)
+		throws NoLayoutForPositionException {
+		switch (position) {
+			case PAGE_SERVICE:
+				return R.layout.slide_service;
+			case PAGE_LIVE:
+				return R.layout.slide_service;
+			case PAGE_DISPLAY:
+				return R.layout.misc;
+			case PAGE_STAGE:
+				return R.layout.stageview;
+			case PAGE_ALERT:
+				return R.layout.alert;
+			case PAGE_SEARCH:
+				return R.layout.search;
+			default:
+				throw new NoLayoutForPositionException(
+					"No Layout for position (" + position + ")");
+		}
+	}
+
+	private AdapterView.OnItemClickListener onItemClickListenerSetLive =
+		new AdapterView.OnItemClickListener() {
+			@Override
+			public void onItemClick(AdapterView<?> adapterView, View view,
+				int i, long l) {
+				controller.setData(Api.LIVE_SET, i);
+			}
+		};
+
+	private AdapterView.OnItemLongClickListener
+		adapterViewOnItemLongClickListener =
+		new AdapterView.OnItemLongClickListener() {
+			@Override
+			public boolean onItemLongClick(AdapterView<?> adapterView,
+				View view, int i, long l) {
+				controller.setData(Api.SERVICE_SET, i);
+				((PagerActivity) context).setCurrentPage(PAGE_LIVE);
+				return true;
+			}
+		};
+
+	private ListView.OnItemClickListener onItemClickListenerService =
+		new AdapterView.OnItemClickListener() {
+			@Override
+			public void onItemClick(AdapterView<?> adapterView, View view,
+				int i, long l) {
+				controller.setData(Api.SERVICE_SET, i);
+			}
+		};
+
+	private Button.OnClickListener onClickListenerNavigate =
+		new Button.OnClickListener() {
+			@Override
+			public void onClick(View v) {
+				if (v.getId() == R.id.prev) {
+					controller.navigate(OpenLPNavigate.NAVIGATE_PREVIOUS);
+				}
+				else if (v.getId() == R.id.next) {
+					controller.navigate(OpenLPNavigate.NAVIGATE_NEXT);
+				}
+			}
+		};
+
+	/**
+	 * Alert Button Listener
+	 */
+	private Button.OnClickListener mSend = new Button.OnClickListener() {
+		@Override
+		public void onClick(View v) {
+			EditText edittext = (EditText) context.findViewById(R.id.alert);
+
+			if (edittext.getText().toString().trim().length() > 0) {
+				apiCallIntent
+					.putExtra(Api.ALERT, edittext.getText().toString());
+				context.startService(apiCallIntent);
+			}
+			else {
+				Toast.makeText(context,
+					context.getString(R.string.alertTextNull),
+					Toast.LENGTH_SHORT).show();
+			}
+		}
+	};
+
+	public View.OnClickListener onClickListenerToggleDisplay =
+		new View.OnClickListener() {
+			@Override
+			public void onClick(View view) {
+				ToggleButton toggleButton = (ToggleButton) view;
+
+				String displayType = getDisplayType();
+				if (!toggleButton.isChecked()) {
+					controller.setDisplay(OpenLPNavigate.DISPLAY_SHOW);
+				}
+				else {
+					if (displayType
+						.equals(context.getString(R.string.displayScreen))) {
+						controller.setDisplay(OpenLPNavigate.HIDE_SCREEN);
+					}
+					else if (displayType
+						.equals(context.getString(R.string.displayTheme))) {
+						controller.setDisplay(OpenLPNavigate.HIDE_THEME);
+					}
+					else {
+						controller.setDisplay(OpenLPNavigate.HIDE_DESKTOP);
+					}
+				}
+			}
+		};
+
+	@Override
+	public int getCount() {
+		return 5;
+	}
+
+	@Override
+	public boolean isViewFromObject(View view, Object o) {
+		return view == o;
+	}
+
+	@Override
+	public void destroyItem(ViewGroup container, int position, Object object) {
+		container.removeView((View) object);
+	}
+
+	private static final String LOG_TAG = OpenLPController.class.getName();
+
+	private String getDisplayType() {
+		displayType = preferences
+			.getString(context.getString(R.string.keyDisplayBlankType),
+				context.getString(R.string.displayTypeValue));
+		return displayType;
+	}
+
+	private String getUrlBase() {
+		return String.format("http://%s:%s/stage";,
+			preferences.getString(context.getString(R.string.keyHost),
+				context.getString(R.string.hostDefaultValue)),
+			preferences.getString(context.getString(R.string.keyPort),
+				context.getString(R.string.portDefaultValue)));
+	}
+
+	class NoLayoutForPositionException extends Exception {
+		NoLayoutForPositionException(String detailMessage) {
+			super(detailMessage);
+		}
+	}
+}

=== modified file 'src/org/openlp/android/utility/OpenLPHttpClient.java'
--- src/org/openlp/android/utility/OpenLPHttpClient.java	2012-03-04 17:20:44 +0000
+++ src/org/openlp/android/utility/OpenLPHttpClient.java	2012-05-05 18:00:28 +0000
@@ -20,9 +20,21 @@
  *******************************************************************************/
 package org.openlp.android.utility;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLDecoder;
+
+import javax.net.ssl.HttpsURLConnection;
+
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.util.Log;
+import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.impl.client.DefaultHttpClient;
@@ -31,14 +43,10 @@
 import org.apache.http.params.HttpParams;
 import org.openlp.android.R;
 
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URISyntaxException;
-import java.net.URL;
-
 /**
  * Personalised HttpClient to be used throughout OpenLP with customisable
  * parameters.
+ * todo: accomodate https
  */
 public class OpenLPHttpClient extends DefaultHttpClient {
 
@@ -48,39 +56,39 @@
 
 	public OpenLPHttpClient(Context context) {
 		SharedPreferences preferences = context.getSharedPreferences(
-			context.getString(R.string.keySharedPreferences),
-			Context.MODE_PRIVATE);
+				context.getString(R.string.keySharedPreferences),
+				Context.MODE_PRIVATE);
 
-		Log.d(LOG_TAG, preferences.getAll().toString());
+		Log.v(LOG_TAG, String.format("Preferences: %s", preferences.getAll().toString()));
 		HttpParams httpParams = new BasicHttpParams();
 
 		urlBase = String.format("http://%s:%s";, preferences.getString(
-			context.getString(R.string.keyHost),
-			context.getString(R.string.hostDefaultValue)), preferences
-			.getString(context.getString(R.string.keyPort),
-				context.getString(R.string.portDefaultValue)));
+				context.getString(R.string.keyHost),
+				context.getString(R.string.hostDefaultValue)), preferences
+				.getString(context.getString(R.string.keyPort),
+						context.getString(R.string.portDefaultValue)));
 
 		int connectionTimeout = context.getResources().getInteger(
-			R.integer.connectionTimeoutDefaultValue);
+				R.integer.connectionTimeoutDefaultValue);
 		int socketTimeout = context.getResources().getInteger(
-			R.integer.socketTimeoutDefaultValue);
+				R.integer.socketTimeoutDefaultValue);
 
 		if (preferences.getBoolean(
-			context.getString(R.string.keyEnableCustomTimeout), false)) {
+				context.getString(R.string.keyEnableCustomTimeout), false)) {
 			Log.d(LOG_TAG, String.format("Retrieving values for %s and %s",
-				context.getString(R.string.keyConnectionTimeout),
-				context.getString(R.string.keySocketTimeout)));
+					context.getString(R.string.keyConnectionTimeout),
+					context.getString(R.string.keySocketTimeout)));
 			connectionTimeout = Integer.parseInt(preferences.getString(
-				context.getString(R.string.keyConnectionTimeout),
-				String.valueOf(context.getResources().getInteger(
-					R.integer.connectionTimeoutDefaultValue))));
+					context.getString(R.string.keyConnectionTimeout),
+					String.valueOf(context.getResources().getInteger(
+							R.integer.connectionTimeoutDefaultValue))));
 			socketTimeout = Integer.parseInt(preferences.getString(
-				context.getString(R.string.keySocketTimeout),
-				String.valueOf(context.getResources().getInteger(
-					R.integer.socketTimeoutDefaultValue))));
+					context.getString(R.string.keySocketTimeout),
+					String.valueOf(context.getResources().getInteger(
+							R.integer.socketTimeoutDefaultValue))));
 		}
 		HttpConnectionParams
-			.setConnectionTimeout(httpParams, connectionTimeout);
+				.setConnectionTimeout(httpParams, connectionTimeout);
 		HttpConnectionParams.setSoTimeout(httpParams, socketTimeout);
 		setParams(httpParams);
 		httpGet = new HttpGet();
@@ -91,9 +99,13 @@
 	}
 
 	public void setUrl(String apiPart) throws URISyntaxException,
-		MalformedURLException {
+			MalformedURLException {
 		url = new URL(urlBase.concat(apiPart));
-		Log.d(LOG_TAG, "URL set to: " + url);
+		try {
+			Log.d(LOG_TAG, "URL set to: " + URLDecoder.decode(url.toString(), "UTF-8"));
+		} catch (UnsupportedEncodingException e) {
+			Log.e(LOG_TAG, "Unable to decode url: " + e.getMessage());
+		}
 		httpGet.setURI(getUrl().toURI());
 	}
 
@@ -101,5 +113,39 @@
 		return super.execute(httpGet);
 	}
 
+	public String handleExecute() throws IOException {
+		HttpResponse response = this.execute();
+
+		if (response.getStatusLine().getStatusCode() == 200) {
+			BufferedReader bufferedReader;
+			HttpEntity entity = response.getEntity();
+
+			if (entity != null) {
+				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.v(LOG_TAG, String.format("entity: %s",stringBuilder.toString()));
+				bufferedReader.close();
+				return stringBuilder.toString();
+			}
+		}
+		return null;
+	}
+
+	public HttpEntity handleAndReturnEntity() throws IOException {
+		HttpResponse response = this.execute();
+
+		if (response.getStatusLine().getStatusCode() == 200) {
+			return response.getEntity();
+		}
+		return null;
+	}
+
 	private final String LOG_TAG = OpenLPHttpClient.class.getName();
 }

=== modified file 'src/org/openlp/android/utility/SlideAdapter.java'
--- src/org/openlp/android/utility/SlideAdapter.java	2012-04-22 09:16:42 +0000
+++ src/org/openlp/android/utility/SlideAdapter.java	2012-05-05 18:00:28 +0000
@@ -20,11 +20,12 @@
  *******************************************************************************/
 package org.openlp.android.utility;
 
+import java.util.List;
+
 import android.app.Activity;
 import android.content.Context;
 import android.content.SharedPreferences;
-import android.graphics.Typeface;
-import android.util.Log;
+import android.graphics.Color;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -33,8 +34,6 @@
 import org.openlp.android.R;
 import org.openlp.android.data.SlideItem;
 
-import java.util.List;
-
 public class SlideAdapter extends BaseAdapter {
 	List<SlideItem> items;
 	Activity context;
@@ -88,9 +87,6 @@
 		View view = convertView;
 
 		SlideItem item = items.get(position);
-		
-		Log.d(LOG_TAG, "getView " + position + " " + item.getText() + " " 
-				+ this.currentSlide);		
 
 		if (view == null) {
 			view = inflater.inflate(R.layout.slide_list_item, null);
@@ -104,6 +100,7 @@
 		else {
 			holder = (ViewHolder) view.getTag();
 		}
+
 		int size = Integer.parseInt(prefs.getString(
 			context.getString(R.string.keyTextSize),
 			String.valueOf(context.getResources().getInteger(
@@ -116,16 +113,13 @@
 		if (useTagDisplay) {
 			holder.rowMarker.setMinWidth(40);
 		}
-		if (position == this.currentSlide){
-			Log.d(LOG_TAG, "getView compare " + position + " "  
-					+ this.currentSlide);			
-			holder.rowItem.setTypeface(null, Typeface.BOLD);
+		if (position == this.currentSlide) {
+			holder.rowItem.setTextColor(Color.WHITE);
+			view.setBackgroundColor(Color.argb(99, 200, 200, 200));
+		} else {
+			holder.rowItem.setTextColor(Color.LTGRAY);
+			view.setBackgroundColor(colors[position % colors.length]);
 		}
-		else{
-			holder.rowItem.setTypeface(null, Typeface.NORMAL);			
-		}			
-		int colorPos = position % colors.length;
-		view.setBackgroundColor(colors[colorPos]);
 
 		return view;
 	}
@@ -134,5 +128,6 @@
 		TextView rowMarker;
 		TextView rowItem;
 	}
-	private final String LOG_TAG = SlideAdapter.class.getName();	
+
+	private final String LOG_TAG = SlideAdapter.class.getName();
 }

=== removed file 'src/org/openlp/android/utility/WebCallAsyncTask.java'
--- src/org/openlp/android/utility/WebCallAsyncTask.java	2012-03-04 17:20:44 +0000
+++ src/org/openlp/android/utility/WebCallAsyncTask.java	1970-01-01 00:00:00 +0000
@@ -1,90 +0,0 @@
-/******************************************************************************
- * OpenLP - Open Source Lyrics Projection                                      *
- * --------------------------------------------------------------------------- *
- * Copyright (c) 2011-2012 Raoul Snyman                                        *
- * Portions copyright (c) 2011-2012 Tim Bentley, Johan Mynhardt, Samuel        *
- * Sjöbergsson                                                                 *
- * --------------------------------------------------------------------------- *
- * 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.ProgressDialog;
-import android.content.Context;
-import android.os.AsyncTask;
-import android.util.Log;
-import android.widget.Toast;
-import org.openlp.android.R;
-
-/**
- * Call URL's using this task, which provides visual feedback.
- */
-public class WebCallAsyncTask extends AsyncTask<String, Void, Void> {
-	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();
-		progressDialog = ProgressDialog.show(context, "",
-			context.getString(R.string.loading));
-	}
-
-	@Override
-	protected Void doInBackground(String... apiCall) {
-		OpenLPHttpClient httpClient = new OpenLPHttpClient(context);
-		try {
-			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 (Exception e) {
-			Log.e(LOG_TAG, e.toString());
-			error = String.format("%s: %s", e.getClass().getSimpleName(),
-				e.getMessage());
-		}
-		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();
-		}
-	}
-
-	private final String LOG_TAG = WebCallAsyncTask.class.getName();
-}


Follow ups