[Libreoffice-commits] .: 4 commits - android/sdremote sd/source

Andrzej J.R. Hunt ajrhunt at kemper.freedesktop.org
Mon Aug 13 12:42:18 PDT 2012


 android/sdremote/AndroidManifest.xml                                                       |   12 
 android/sdremote/res/layout/activity_pairing.xml                                           |   36 +
 android/sdremote/res/layout/activity_selector_sublayout_server.xml                         |    5 
 android/sdremote/res/values/strings.xml                                                    |    3 
 android/sdremote/src/org/libreoffice/impressremote/PairingActivity.java                    |   66 ++
 android/sdremote/src/org/libreoffice/impressremote/SelectorActivity.java                   |  317 +++++----
 android/sdremote/src/org/libreoffice/impressremote/TestClient.java                         |  196 ------
 android/sdremote/src/org/libreoffice/impressremote/communication/Client.java               |  135 ++--
 android/sdremote/src/org/libreoffice/impressremote/communication/CommunicationService.java |  320 +++++-----
 android/sdremote/src/org/libreoffice/impressremote/communication/NetworkClient.java        |  106 ++-
 android/sdremote/src/org/libreoffice/impressremote/communication/Server.java               |   43 +
 android/sdremote/src/org/libreoffice/impressremote/communication/ServerFinder.java         |    6 
 android/sdremote/src/org/libreoffice/impressremote/communication/TestClient.java           |  197 ++++++
 sd/source/ui/dlg/RemoteDialog.cxx                                                          |   18 
 sd/source/ui/dlg/RemoteDialogClientBox.cxx                                                 |   40 -
 sd/source/ui/dlg/RemoteDialogClientBox.hxx                                                 |   13 
 sd/source/ui/remotecontrol/BufferedStreamSocket.cxx                                        |   30 
 sd/source/ui/remotecontrol/Server.cxx                                                      |    2 
 18 files changed, 923 insertions(+), 622 deletions(-)

New commits:
commit 837a125ba6d31fb4cbc654e982884430464e8bba
Author: Andrzej J.R. Hunt <andrzej at ahunt.org>
Date:   Mon Aug 13 21:39:12 2012 +0200

    Final fixes for pairing.
    
    Change-Id: Ib66f227062f9c263dbc3dfefaf70525c73bdedc9

diff --git a/android/sdremote/AndroidManifest.xml b/android/sdremote/AndroidManifest.xml
index 4364b24..1004193 100644
--- a/android/sdremote/AndroidManifest.xml
+++ b/android/sdremote/AndroidManifest.xml
@@ -20,6 +20,15 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity
+            android:name=".communication.TestClient"
+            android:label="Remote--Direct" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
 
         <service android:name=".communication.CommunicationService" >
         </service>
diff --git a/android/sdremote/src/org/libreoffice/impressremote/TestClient.java b/android/sdremote/src/org/libreoffice/impressremote/TestClient.java
deleted file mode 100644
index 2bc6e90..0000000
--- a/android/sdremote/src/org/libreoffice/impressremote/TestClient.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * This file is part of the LibreOffice project.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-package org.libreoffice.impressremote;
-
-import org.libreoffice.impressremote.communication.CommunicationService;
-
-import android.app.Activity;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.graphics.Bitmap;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-public class TestClient extends Activity {
-
-    private boolean mCurrentPreviewImageMissing = false;
-
-    private boolean mIsBound = false;
-
-    private CommunicationService mCommunicationService;
-
-    final Messenger mMessenger = new Messenger(new MessageHandler());
-
-    /** Called when the activity is first created. */
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.testlayout);
-        setupUI();
-
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        doBindService();
-
-    }
-
-    // FIXME: move all necessary code to CommunicationService.onUnbind
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-    }
-
-    @Override
-    public void onBackPressed() {
-        // TODO Auto-generated method stub
-        mCommunicationService.disconnect();
-        stopService(new Intent(this, CommunicationService.class));
-        doUnbindService();
-        finish();
-        super.onBackPressed();
-    }
-
-    @Override
-    protected void onStop() {
-        // TODO Auto-generated method stub
-        super.onStop();
-        // mCommunicationService.disconnect();
-        // stopService(new Intent(this, CommunicationService.class));
-    }
-
-    private ServiceConnection mConnection = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName aClassName,
-                        IBinder aService) {
-            mCommunicationService = ((CommunicationService.CBinder) aService)
-                            .getService();
-            //            mCommunicationService.connectTo(Protocol.NETWORK, "192.168.0.18");
-            mCommunicationService.setActivityMessenger(mMessenger);
-            enableButtons(true);
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName aClassName) {
-            mCommunicationService = null;
-            enableButtons(false);
-        }
-    };
-
-    void doBindService() {
-        Intent aIntent = new Intent(this, CommunicationService.class);
-        startService(aIntent);
-        bindService(aIntent, mConnection, Context.BIND_IMPORTANT);
-        mIsBound = true;
-    }
-
-    void doUnbindService() {
-        mCommunicationService.setActivityMessenger(null);
-        if (mIsBound) {
-            unbindService(mConnection);
-            mIsBound = false;
-        }
-    }
-
-    private Button mButtonNext;
-
-    private Button mButtonPrevious;
-
-    private ImageView mImageView;
-
-    private TextView mSlideLabel;
-
-    private void setupUI() {
-        mButtonNext = (Button) findViewById(R.id.button_next);
-        mButtonPrevious = (Button) findViewById(R.id.button_previous);
-        mImageView = (ImageView) findViewById(R.id.image_preview);
-        mSlideLabel = (TextView) findViewById(R.id.label_curSlide);
-
-        enableButtons(false);
-
-        mButtonNext.setOnClickListener(new OnClickListener() {
-
-            @Override
-            public void onClick(View v) {
-                mCommunicationService.getTransmitter().nextTransition();
-
-            }
-
-        });
-
-        mButtonPrevious.setOnClickListener(new OnClickListener() {
-
-            @Override
-            public void onClick(View v) {
-                mCommunicationService.getTransmitter().previousTransition();
-
-            }
-
-        });
-
-        Button mThumbnailButton = (Button) findViewById(R.id.button_thumbnail);
-
-        mThumbnailButton.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                Intent aIntent = new Intent(TestClient.this,
-                                PresentationActivity.class);
-                startActivity(aIntent);
-            }
-        });
-
-    }
-
-    private void enableButtons(boolean aEnabled) {
-        mButtonNext.setEnabled(aEnabled);
-        mButtonPrevious.setEnabled(aEnabled);
-    }
-
-    class MessageHandler extends Handler {
-        @Override
-        public void handleMessage(Message aMessage) {
-            Bundle aData = aMessage.getData();
-            switch (aMessage.what) {
-            case CommunicationService.MSG_SLIDE_CHANGED:
-                int newSlide = aData.getInt("slide_number");
-                mSlideLabel.setText("Slide " + newSlide);
-                mCurrentPreviewImageMissing = true;
-                // We continue on to try and update the image.
-            case CommunicationService.MSG_SLIDE_PREVIEW:
-                int aSlideNumber = aData.getInt("slide_number");
-                if (mCurrentPreviewImageMissing) {
-                    Bitmap aImage = mCommunicationService.getSlideShow()
-                                    .getImage(aSlideNumber);
-                    if (aImage != null) {
-                        mImageView.setImageBitmap(aImage);
-                        mCurrentPreviewImageMissing = false;
-                    }
-                }
-                break;
-
-            }
-        }
-    }
-}
-/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
\ No newline at end of file
diff --git a/android/sdremote/src/org/libreoffice/impressremote/communication/CommunicationService.java b/android/sdremote/src/org/libreoffice/impressremote/communication/CommunicationService.java
index 5603684..73b6f3d 100644
--- a/android/sdremote/src/org/libreoffice/impressremote/communication/CommunicationService.java
+++ b/android/sdremote/src/org/libreoffice/impressremote/communication/CommunicationService.java
@@ -33,32 +33,36 @@ public class CommunicationService extends Service implements Runnable {
 
     @Override
     public void run() {
-        while (true) {
-            // Condition
-            try {
-                wait();
-            } catch (InterruptedException e) {
-                // We don't care.
-            }
-            // Work
-            synchronized (mConnectionVariableMutex) {
-                if ((mStateDesired == State.CONNECTED && mState == State.CONNECTED)
-                                || (mStateDesired == State.DISCONNECTED && mState == State.CONNECTED)) {
-                    mClient.closeConnection();
-                    mState = State.DISCONNECTED;
+        synchronized (this) {
+            while (true) {
+                // Condition
+                try {
+
+                    wait();
+                } catch (InterruptedException e) {
+                    // We have finished
+                    return;
                 }
-                if (mStateDesired == State.CONNECTED) {
-                    switch (mServerDesired.getProtocol()) {
-                    case NETWORK:
-                        mClient = new NetworkClient(
-                                        mServerDesired.getAddress(), this);
-                        mTransmitter = new Transmitter(mClient);
-                        mClient.setReceiver(mReceiver);
-                        break;
-                    case BLUETOOTH:
-                        break;
+                // Work
+                synchronized (mConnectionVariableMutex) {
+                    if ((mStateDesired == State.CONNECTED && mState == State.CONNECTED)
+                                    || (mStateDesired == State.DISCONNECTED && mState == State.CONNECTED)) {
+                        mClient.closeConnection();
+                        mState = State.DISCONNECTED;
+                    }
+                    if (mStateDesired == State.CONNECTED) {
+                        switch (mServerDesired.getProtocol()) {
+                        case NETWORK:
+                            mClient = new NetworkClient(
+                                            mServerDesired.getAddress(), this);
+                            mTransmitter = new Transmitter(mClient);
+                            mClient.setReceiver(mReceiver);
+                            break;
+                        case BLUETOOTH:
+                            break;
+                        }
+                        mState = State.CONNECTED;
                     }
-                    mState = State.CONNECTED;
                 }
             }
         }
@@ -90,7 +94,10 @@ public class CommunicationService extends Service implements Runnable {
             }
             mServerDesired = aServer;
             mStateDesired = State.CONNECTED;
-            notify();
+            synchronized (this) {
+                notify();
+            }
+
         }
         // TODO: connect
     }
@@ -98,7 +105,9 @@ public class CommunicationService extends Service implements Runnable {
     public void disconnect() {
         synchronized (mConnectionVariableMutex) {
             mStateDesired = State.DISCONNECTED;
-            notify();
+            synchronized (this) {
+                notify();
+            }
         }
     }
 
@@ -140,14 +149,20 @@ public class CommunicationService extends Service implements Runnable {
         return mBinder;
     }
 
+    private Thread mThread = null;
+
     @Override
     public void onCreate() {
         // TODO Create a notification (if configured).
+        mThread = new Thread(this);
+        mThread.start();
     }
 
     @Override
     public void onDestroy() {
         // TODO Destroy the notification (as necessary).
+        mThread.interrupt();
+        mThread = null;
     }
 
     public Transmitter getTransmitter() {
diff --git a/android/sdremote/src/org/libreoffice/impressremote/communication/NetworkClient.java b/android/sdremote/src/org/libreoffice/impressremote/communication/NetworkClient.java
index f76e14e..33b600b 100644
--- a/android/sdremote/src/org/libreoffice/impressremote/communication/NetworkClient.java
+++ b/android/sdremote/src/org/libreoffice/impressremote/communication/NetworkClient.java
@@ -50,8 +50,15 @@ public class NetworkClient extends Client {
                             CommunicationService.MSG_PAIRING_STARTED);
             aIntent.putExtra("PIN", aPin);
             LocalBroadcastManager.getInstance(mContext).sendBroadcast(aIntent);
+            // Send out
+            String aName = "Bob"; // TODO: get the proper name
+            sendCommand("LO_SERVER_CLIENT_PAIR\n" + aName + "\n" + aPin
+                            + "\n\n");
+
             // Wait until we get the appropriate string back...
+            System.out.println("SF:waiting");
             String aTemp = mReader.readLine();
+            System.out.println("SF:waited");
             if (!aTemp.equals("LO_SERVER_SERVER_PAIRED")) {
                 return;
             } else {
@@ -60,9 +67,11 @@ public class NetworkClient extends Client {
                 LocalBroadcastManager.getInstance(mContext).sendBroadcast(
                                 aIntent);
             }
-            while ((aTemp = mReader.readLine()).length() != 0) {
+            while (mReader.readLine().length() != 0) {
                 // Get rid of extra lines
+                System.out.println("SF: empty line");
             }
+            System.out.println("SD: empty");
             startListening();
         } catch (UnknownHostException e) {
             // TODO Tell the user we have a problem
diff --git a/android/sdremote/src/org/libreoffice/impressremote/communication/TestClient.java b/android/sdremote/src/org/libreoffice/impressremote/communication/TestClient.java
new file mode 100644
index 0000000..662cb50
--- /dev/null
+++ b/android/sdremote/src/org/libreoffice/impressremote/communication/TestClient.java
@@ -0,0 +1,197 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.libreoffice.impressremote.communication;
+
+import org.libreoffice.impressremote.PresentationActivity;
+import org.libreoffice.impressremote.R;
+import org.libreoffice.impressremote.communication.Server.Protocol;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+public class TestClient extends Activity {
+
+    private boolean mCurrentPreviewImageMissing = false;
+
+    private boolean mIsBound = false;
+
+    private CommunicationService mCommunicationService;
+
+    final Messenger mMessenger = new Messenger(new MessageHandler());
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.testlayout);
+        setupUI();
+
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        doBindService();
+
+    }
+
+    // FIXME: move all necessary code to CommunicationService.onUnbind
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+    }
+
+    @Override
+    public void onBackPressed() {
+        // TODO Auto-generated method stub
+        mCommunicationService.disconnect();
+        stopService(new Intent(this, CommunicationService.class));
+        doUnbindService();
+        finish();
+        super.onBackPressed();
+    }
+
+    @Override
+    protected void onStop() {
+        // TODO Auto-generated method stub
+        super.onStop();
+        // mCommunicationService.disconnect();
+        // stopService(new Intent(this, CommunicationService.class));
+    }
+
+    private ServiceConnection mConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName aClassName,
+                        IBinder aService) {
+            mCommunicationService = ((CommunicationService.CBinder) aService)
+                            .getService();
+            mCommunicationService.connectTo(new Server(Protocol.NETWORK,
+                            "10.0.2.2", "TestServer", 0l));
+            mCommunicationService.setActivityMessenger(mMessenger);
+            enableButtons(true);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName aClassName) {
+            mCommunicationService = null;
+            enableButtons(false);
+        }
+    };
+
+    void doBindService() {
+        Intent aIntent = new Intent(this, CommunicationService.class);
+        startService(aIntent);
+        bindService(aIntent, mConnection, Context.BIND_IMPORTANT);
+        mIsBound = true;
+    }
+
+    void doUnbindService() {
+        mCommunicationService.setActivityMessenger(null);
+        if (mIsBound) {
+            unbindService(mConnection);
+            mIsBound = false;
+        }
+    }
+
+    private Button mButtonNext;
+
+    private Button mButtonPrevious;
+
+    private ImageView mImageView;
+
+    private TextView mSlideLabel;
+
+    private void setupUI() {
+        mButtonNext = (Button) findViewById(R.id.button_next);
+        mButtonPrevious = (Button) findViewById(R.id.button_previous);
+        mImageView = (ImageView) findViewById(R.id.image_preview);
+        mSlideLabel = (TextView) findViewById(R.id.label_curSlide);
+
+        enableButtons(false);
+
+        mButtonNext.setOnClickListener(new OnClickListener() {
+
+            @Override
+            public void onClick(View v) {
+                mCommunicationService.getTransmitter().nextTransition();
+
+            }
+
+        });
+
+        mButtonPrevious.setOnClickListener(new OnClickListener() {
+
+            @Override
+            public void onClick(View v) {
+                mCommunicationService.getTransmitter().previousTransition();
+
+            }
+
+        });
+
+        Button mThumbnailButton = (Button) findViewById(R.id.button_thumbnail);
+
+        mThumbnailButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent aIntent = new Intent(TestClient.this,
+                                PresentationActivity.class);
+                startActivity(aIntent);
+            }
+        });
+
+    }
+
+    private void enableButtons(boolean aEnabled) {
+        mButtonNext.setEnabled(aEnabled);
+        mButtonPrevious.setEnabled(aEnabled);
+    }
+
+    class MessageHandler extends Handler {
+        @Override
+        public void handleMessage(Message aMessage) {
+            Bundle aData = aMessage.getData();
+            switch (aMessage.what) {
+            case CommunicationService.MSG_SLIDE_CHANGED:
+                int newSlide = aData.getInt("slide_number");
+                mSlideLabel.setText("Slide " + newSlide);
+                mCurrentPreviewImageMissing = true;
+                // We continue on to try and update the image.
+            case CommunicationService.MSG_SLIDE_PREVIEW:
+                int aSlideNumber = aData.getInt("slide_number");
+                if (mCurrentPreviewImageMissing) {
+                    Bitmap aImage = mCommunicationService.getSlideShow()
+                                    .getImage(aSlideNumber);
+                    if (aImage != null) {
+                        mImageView.setImageBitmap(aImage);
+                        mCurrentPreviewImageMissing = false;
+                    }
+                }
+                break;
+
+            }
+        }
+    }
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
\ No newline at end of file
diff --git a/sd/source/ui/dlg/RemoteDialog.cxx b/sd/source/ui/dlg/RemoteDialog.cxx
index 0df0f70..d920d91 100644
--- a/sd/source/ui/dlg/RemoteDialog.cxx
+++ b/sd/source/ui/dlg/RemoteDialog.cxx
@@ -50,9 +50,12 @@ IMPL_LINK_NOARG(RemoteDialog, HandleConnectButton)
     if ( aSelected < 0 )
         return 1;
     TClientBoxEntry aEntry = mClientBox.GetEntryData(aSelected);
-    OUString aPin = mClientBox.getPin();
+    OUString aPin ( mClientBox.getPin() );
     if ( RemoteServer::connectClient( aEntry->m_pClientInfo, aPin ) )
+    {
+        Close();
         return 0;
+    }
     else
         return 1;
 }
diff --git a/sd/source/ui/dlg/RemoteDialogClientBox.cxx b/sd/source/ui/dlg/RemoteDialogClientBox.cxx
index 11128f3..8ce809e 100644
--- a/sd/source/ui/dlg/RemoteDialogClientBox.cxx
+++ b/sd/source/ui/dlg/RemoteDialogClientBox.cxx
@@ -97,6 +97,8 @@ ClientBox::ClientBox( Dialog* pParent, RemoteServer *pServer,
     m_pScrollBar->SetScrollHdl( LINK( this, ClientBox, ScrollHdl ) );
     m_pScrollBar->EnableDrag();
 
+    m_aPinBox.SetUseThousandSep(false);
+
     SetPaintTransparent( true );
     SetPosPixel( Point( RSC_SP_DLG_INNERBORDER_LEFT, RSC_SP_DLG_INNERBORDER_TOP ) );
     long nIconHeight = 2*TOP_OFFSET + SMALL_ICON_SIZE;
@@ -582,7 +584,7 @@ long ClientBox::PointToPos( const Point& rPos )
 
 OUString ClientBox::getPin()
 {
-    return m_aPinBox.GetText();
+    return OUString::valueOf( m_aPinBox.GetValue() );
 }
 
 //------------------------------------------------------------------------------
diff --git a/sd/source/ui/remotecontrol/BufferedStreamSocket.cxx b/sd/source/ui/remotecontrol/BufferedStreamSocket.cxx
index 8232bd0..0e274c3 100644
--- a/sd/source/ui/remotecontrol/BufferedStreamSocket.cxx
+++ b/sd/source/ui/remotecontrol/BufferedStreamSocket.cxx
@@ -27,8 +27,25 @@ sal_Int32 BufferedStreamSocket::readLine( OString& aLine )
 {
     while ( true )
     {
+        // Process buffer first incase data already present.
+        vector<char>::iterator aIt;
+        if ( (aIt = find( aBuffer.begin(), aBuffer.end(), '\n' ))
+            != aBuffer.end() )
+        {
+            sal_uInt64 aLocation = aIt - aBuffer.begin();
+
+            aLine = OString( &(*aBuffer.begin()), aLocation );
+
+            aBuffer.erase( aBuffer.begin(), aIt + 1 ); // Also delete the empty line
+            aRead -= (aLocation + 1);
+
+            return aLine.getLength();
+        }
+
+        // Then try and receive if nothing present
         aBuffer.resize( aRead + 100 );
         aRet = recv( &aBuffer[aRead], 100 );
+
         if ( aRet == 0 )
         {
                 return aRet;
@@ -40,19 +57,6 @@ sal_Int32 BufferedStreamSocket::readLine( OString& aLine )
             return 0;
         }
         aRead += aRet;
-        vector<char>::iterator aIt;
-        while ( (aIt = find( aBuffer.begin(), aBuffer.end(), '\n' ))
-            != aBuffer.end() )
-        {
-            sal_uInt64 aLocation = aIt - aBuffer.begin();
-
-            aLine = OString( &(*aBuffer.begin()), aLocation );
-
-            aBuffer.erase( aBuffer.begin(), aIt + 1 ); // Also delete the empty line
-            aRead -= (aLocation + 1);
-
-            return aLine.getLength();
-        }
     }
 
 
diff --git a/sd/source/ui/remotecontrol/Server.cxx b/sd/source/ui/remotecontrol/Server.cxx
index af4170a..e7a361e 100644
--- a/sd/source/ui/remotecontrol/Server.cxx
+++ b/sd/source/ui/remotecontrol/Server.cxx
@@ -82,6 +82,7 @@ void RemoteServer::execute()
 
             if ( ! pSocket->readLine( aLine ) ) delete pSocket;
             OString aPin( aLine );
+            fprintf( stderr, "Pin:%s\n", aPin.getStr() );
 
             SocketAddr aClientAddr;
             pSocket->getPeerAddr( aClientAddr );
@@ -191,6 +192,7 @@ sal_Bool RemoteServer::connectClient( ClientInfo* pClient, rtl::OUString aPin )
             break;
             }
         }
+        pCommunicator->launch();
         return true;
     }
     else
commit 4533d34d9acdd3779333334d1d560bf707267e04
Author: Andrzej J.R. Hunt <andrzej at ahunt.org>
Date:   Mon Aug 13 16:09:46 2012 +0200

    Finished pin verification server side.
    
    Change-Id: Ib59be7fb21ae132d1a19b414acd55f3c54448841

diff --git a/sd/source/ui/dlg/RemoteDialog.cxx b/sd/source/ui/dlg/RemoteDialog.cxx
index 465febc..0df0f70 100644
--- a/sd/source/ui/dlg/RemoteDialog.cxx
+++ b/sd/source/ui/dlg/RemoteDialog.cxx
@@ -24,14 +24,14 @@ RemoteDialog::RemoteDialog( Window *pWindow ) :
     mButtonCancel(      this, SdResId( BTN_CANCEL ) ),
     mClientBox(         this, NULL, SdResId( LB_SERVERS ) )
 {
-     FreeResource();
+    FreeResource();
 
     vector<ClientInfo*> aClients( RemoteServer::getClients() );
 
     for ( vector<ClientInfo*>::const_iterator aIt( aClients.begin() );
         aIt < aClients.end(); aIt++ )
     {
-        mClientBox.addEntry( **aIt );
+        mClientBox.addEntry( *aIt );
     }
 
     mButtonConnect.SetClickHdl( LINK( this, RemoteDialog, HandleConnectButton ) );
@@ -46,8 +46,15 @@ IMPL_LINK_NOARG(RemoteDialog, HandleConnectButton)
 {
 //     setBusy( true );
     // Fixme: Try and connect
-
-    return 1;
+    long aSelected = mClientBox.GetActiveEntryIndex();
+    if ( aSelected < 0 )
+        return 1;
+    TClientBoxEntry aEntry = mClientBox.GetEntryData(aSelected);
+    OUString aPin = mClientBox.getPin();
+    if ( RemoteServer::connectClient( aEntry->m_pClientInfo, aPin ) )
+        return 0;
+    else
+        return 1;
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
\ No newline at end of file
diff --git a/sd/source/ui/dlg/RemoteDialogClientBox.cxx b/sd/source/ui/dlg/RemoteDialogClientBox.cxx
index e8c9edc..11128f3 100644
--- a/sd/source/ui/dlg/RemoteDialogClientBox.cxx
+++ b/sd/source/ui/dlg/RemoteDialogClientBox.cxx
@@ -47,9 +47,9 @@ namespace sd {
 //------------------------------------------------------------------------------
 //                          struct ClientBoxEntry
 //------------------------------------------------------------------------------
-ClientBoxEntry::ClientBoxEntry( const ClientInfo& rClientInfo ) :
+ClientBoxEntry::ClientBoxEntry( ClientInfo* pClientInfo ) :
     m_bActive( false ),
-    m_clientInfo( rClientInfo )
+    m_pClientInfo( pClientInfo )
 {
 }
 
@@ -232,6 +232,14 @@ void ClientBox::DeleteRemoved()
     m_bInDelete = false;
 }
 
+long ClientBox::GetActiveEntryIndex()
+{
+    if ( m_bHasActive )
+        return m_nActive;
+    else
+        return -1;
+}
+
 //------------------------------------------------------------------------------
 //This function may be called with nPos < 0
 void ClientBox::selectEntry( const long nPos )
@@ -329,20 +337,20 @@ void ClientBox::DrawRow( const Rectangle& rRect, const TClientBoxEntry pEntry )
     long nMaxTitleWidth = rRect.GetWidth() - ICON_OFFSET;
     nMaxTitleWidth -= ( 2 * SMALL_ICON_SIZE ) + ( 4 * SPACE_BETWEEN );
 
-    long aTitleWidth = GetTextWidth( String( pEntry->m_clientInfo.mName ) ) + (aTextHeight / 3);
+    long aTitleWidth = GetTextWidth( String( pEntry->m_pClientInfo->mName ) ) + (aTextHeight / 3);
 
     aPos = rRect.TopLeft() + Point( ICON_OFFSET, TOP_OFFSET );
 
     if ( aTitleWidth > nMaxTitleWidth )
     {
         aTitleWidth = nMaxTitleWidth - (aTextHeight / 3);
-        String aShortTitle = GetEllipsisString( pEntry->m_clientInfo.mName,
+        String aShortTitle = GetEllipsisString( pEntry->m_pClientInfo->mName,
                                                 aTitleWidth );
         DrawText( aPos, aShortTitle );
         aTitleWidth += (aTextHeight / 3);
     }
     else
-        DrawText( aPos, pEntry->m_clientInfo.mName );
+        DrawText( aPos, pEntry->m_pClientInfo->mName );
 
     SetFont( aStdFont );
 
@@ -572,6 +580,11 @@ long ClientBox::PointToPos( const Point& rPos )
     return nPos;
 }
 
+OUString ClientBox::getPin()
+{
+    return m_aPinBox.GetText();
+}
+
 //------------------------------------------------------------------------------
 void ClientBox::MouseButtonDown( const MouseEvent& rMEvt )
 {
@@ -632,13 +645,13 @@ long ClientBox::Notify( NotifyEvent& rNEvt )
 
 
 //------------------------------------------------------------------------------
-long ClientBox::addEntry( const ClientInfo& rClientInfo )
+long ClientBox::addEntry( ClientInfo* pClientInfo )
 {
     long         nPos = 0;
 //     PackageState eState = m_pManager->getPackageState( xPackage );
 //     bool         bLocked = m_pManager->isReadOnly( xPackage );
 
-    TClientBoxEntry pEntry( new ClientBoxEntry( rClientInfo ) );
+    TClientBoxEntry pEntry( new ClientBoxEntry( pClientInfo ) );
 
     bool bNewEntryInserted = false;
 
@@ -665,7 +678,8 @@ long ClientBox::addEntry( const ClientInfo& rClientInfo )
     //keep in sync with removeEventListener logic
     if (bNewEntryInserted)
     {
-//         pEntry->m_xPackage->addEventListener(uno::Reference< lang::XEventListener > ( m_xRemoveListener, uno::UNO_QUERY ) );
+
+        //         pEntry->m_xPackage->addEventListener(uno::Reference< lang::XEventListener > ( m_xRemoveListener, uno::UNO_QUERY ) );
     }
 
 
@@ -693,9 +707,9 @@ long ClientBox::addEntry( const ClientInfo& rClientInfo )
 }
 
 //------------------------------------------------------------------------------
-void ClientBox::updateEntry( const ClientInfo& rClientInfo )
+void ClientBox::updateEntry( const ClientInfo* pClientInfo )
 {
-    (void) rClientInfo;
+    (void) pClientInfo;
 //     typedef std::vector< TClientBoxEntry >::iterator ITER;
 //     for ( ITER iIndex = m_vEntries.begin(); iIndex < m_vEntries.end(); ++iIndex )
 //     {
@@ -724,9 +738,9 @@ void ClientBox::updateEntry( const ClientInfo& rClientInfo )
 }
 
 //------------------------------------------------------------------------------
-void ClientBox::removeEntry( const ClientInfo& rClientInfo )
+void ClientBox::removeEntry( const ClientInfo* pClientInfo )
 {
-    (void) rClientInfo;
+    (void) pClientInfo;
 //     if ( ! m_bInDelete )
 //     {
 //         ::osl::ClearableMutexGuard aGuard( m_entriesMutex );
diff --git a/sd/source/ui/dlg/RemoteDialogClientBox.hxx b/sd/source/ui/dlg/RemoteDialogClientBox.hxx
index c22af87..fd2b49e 100644
--- a/sd/source/ui/dlg/RemoteDialogClientBox.hxx
+++ b/sd/source/ui/dlg/RemoteDialogClientBox.hxx
@@ -65,10 +65,10 @@ typedef ::boost::shared_ptr< ClientBoxEntry > TClientBoxEntry;
 struct ClientBoxEntry
 {
     bool            m_bActive       :1;
-    ClientInfo     m_clientInfo;
+    ClientInfo*     m_pClientInfo;
 
 
-    ClientBoxEntry( const ClientInfo& rClientInfo );
+    ClientBoxEntry( ClientInfo* pClientInfo );
    ~ClientBoxEntry();
 
 };
@@ -167,6 +167,7 @@ public:
     const Size      GetMinOutputSizePixel() const;
     void            SetExtraSize( long nSize ) { m_nExtraHeight = nSize; }
     TClientBoxEntry     GetEntryData( long nPos ) { return m_vEntries[ nPos ]; }
+    long            GetActiveEntryIndex();
     long            GetEntryCount() { return (long) m_vEntries.size(); }
     Rectangle       GetEntryRect( const long nPos ) const;
     bool            HasActive() { return m_bHasActive; }
@@ -179,13 +180,15 @@ public:
 
     //-----------------
     void    selectEntry( const long nPos );
-    long            addEntry( const ClientInfo& rClientInfo );
-    void            updateEntry( const ClientInfo& rPackageInfo );
-    void            removeEntry( const ClientInfo& rPackageInfo );
+    long            addEntry( ClientInfo* pClientInfo );
+    void            updateEntry( const ClientInfo* rPackageInfo );
+    void            removeEntry( const ClientInfo* rPackageInfo );
 
     void            prepareChecking();
     void            checkEntries();
 
+    OUString getPin();
+
     RemoteServer*    getServer() const { return m_pServer; }
 };
 
commit cf3de756eac964ce073872996956daa0e0840ff8
Author: Andrzej J.R. Hunt <andrzej at ahunt.org>
Date:   Mon Aug 13 15:13:50 2012 +0200

    Pairing implemented client side.
    
    Change-Id: I678e038470824affa2fe4d792e9a5defbd77807a

diff --git a/android/sdremote/src/org/libreoffice/impressremote/communication/Client.java b/android/sdremote/src/org/libreoffice/impressremote/communication/Client.java
index bb76df9..2864b9d 100644
--- a/android/sdremote/src/org/libreoffice/impressremote/communication/Client.java
+++ b/android/sdremote/src/org/libreoffice/impressremote/communication/Client.java
@@ -11,11 +11,12 @@ package org.libreoffice.impressremote.communication;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 
+import android.content.Context;
+
 /**
  * Generic Client for the remote control. To implement a Client for a specific
  * transport medium you must provide input and output streams (
@@ -26,70 +27,74 @@ import java.util.ArrayList;
  */
 public abstract class Client {
 
-	private static final String CHARSET = "UTF-8";
-
-	protected InputStream mInputStream;
-	protected OutputStream mOutputStream;
-
-	public abstract void closeConnection();
-
-	private Receiver mReceiver;
-
-	public void setReceiver(Receiver aReceiver) {
-		mReceiver = aReceiver;
-	}
-
-	protected void startListening() {
-
-		Thread t = new Thread() {
-			public void run() {
-				listen();
-			};
-
-		};
-		t.start();
-	}
-
-	private void listen() {
-		BufferedReader aReader;
-		try {
-			aReader = new BufferedReader(new InputStreamReader(mInputStream,
-			                CHARSET));
-			while (true) {
-				ArrayList<String> aList = new ArrayList<String>();
-				String aTemp;
-				// read until empty line
-				while ((aTemp = aReader.readLine()).length() != 0) {
-					aList.add(aTemp);
-				}
-				mReceiver.parseCommand(aList);
-			}
-		} catch (UnsupportedEncodingException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		} catch (IOException e1) {
-			// TODO stream couldn't be opened.
-			e1.printStackTrace();
-		}
-
-	}
-
-	/**
-	 * Send a valid JSON string to the server.
-	 *
-	 * @param command
-	 *            Must be a valid JSON string.
-	 */
-	public void sendCommand(String command) {
-		try {
-			mOutputStream.write(command.getBytes(CHARSET));
-		} catch (UnsupportedEncodingException e) {
-			throw new Error("Specified network encoding [" + CHARSET
-			                + " not available.");
-		} catch (IOException e) {
-			// TODO Notify that stream has closed.
-		}
-	}
+    protected static final String CHARSET = "UTF-8";
+
+    protected InputStream mInputStream;
+    protected BufferedReader mReader;
+    protected OutputStream mOutputStream;
+
+    public abstract void closeConnection();
+
+    private Receiver mReceiver;
+
+    protected Context mContext;
+
+    public Client(Context aContext) {
+        mContext = aContext;
+    }
+
+    public void setReceiver(Receiver aReceiver) {
+        mReceiver = aReceiver;
+    }
+
+    protected void startListening() {
+
+        Thread t = new Thread() {
+            public void run() {
+                listen();
+            };
+
+        };
+        t.start();
+    }
+
+    private void listen() {
+        try {
+            while (true) {
+                ArrayList<String> aList = new ArrayList<String>();
+                String aTemp;
+                // read until empty line
+                while ((aTemp = mReader.readLine()).length() != 0) {
+                    aList.add(aTemp);
+                }
+                mReceiver.parseCommand(aList);
+            }
+        } catch (UnsupportedEncodingException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        } catch (IOException e1) {
+            // TODO stream couldn't be opened.
+            e1.printStackTrace();
+        }
+
+    }
+
+    /**
+     * Send a valid JSON string to the server.
+     *
+     * @param command
+     *            Must be a valid JSON string.
+     */
+    public void sendCommand(String command) {
+        try {
+            mOutputStream.write(command.getBytes(CHARSET));
+        } catch (UnsupportedEncodingException e) {
+            throw new Error("Specified network encoding [" + CHARSET
+                            + " not available.");
+        } catch (IOException e) {
+            // TODO Notify that stream has closed.
+        }
+    }
 
 }
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
\ No newline at end of file
diff --git a/android/sdremote/src/org/libreoffice/impressremote/communication/CommunicationService.java b/android/sdremote/src/org/libreoffice/impressremote/communication/CommunicationService.java
index c8ecace..5603684 100644
--- a/android/sdremote/src/org/libreoffice/impressremote/communication/CommunicationService.java
+++ b/android/sdremote/src/org/libreoffice/impressremote/communication/CommunicationService.java
@@ -50,7 +50,8 @@ public class CommunicationService extends Service implements Runnable {
                 if (mStateDesired == State.CONNECTED) {
                     switch (mServerDesired.getProtocol()) {
                     case NETWORK:
-                        mClient = new NetworkClient(mServerDesired.getAddress());
+                        mClient = new NetworkClient(
+                                        mServerDesired.getAddress(), this);
                         mTransmitter = new Transmitter(mClient);
                         mClient.setReceiver(mReceiver);
                         break;
diff --git a/android/sdremote/src/org/libreoffice/impressremote/communication/NetworkClient.java b/android/sdremote/src/org/libreoffice/impressremote/communication/NetworkClient.java
index 9c1f163..f76e14e 100644
--- a/android/sdremote/src/org/libreoffice/impressremote/communication/NetworkClient.java
+++ b/android/sdremote/src/org/libreoffice/impressremote/communication/NetworkClient.java
@@ -8,11 +8,17 @@
  */
 package org.libreoffice.impressremote.communication;
 
+import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.net.Socket;
 import java.net.UnknownHostException;
+import java.util.Random;
 
+import android.content.Context;
+import android.content.Intent;
 import android.os.StrictMode;
+import android.support.v4.content.LocalBroadcastManager;
 
 /**
  * Standard Network client. Connects to a server using Sockets.
@@ -21,40 +27,63 @@ import android.os.StrictMode;
  */
 public class NetworkClient extends Client {
 
-	private static final int PORT = 1599;
-
-	private Socket mSocket;
-
-	public NetworkClient(String ipAddress) {
-		// FIXME: eventually networking will be fully threaded.
-		StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder()
-		                .permitAll().build();
-		StrictMode.setThreadPolicy(policy);
-		try {
-			mSocket = new Socket(ipAddress, PORT);
-			mInputStream = mSocket.getInputStream();
-			mOutputStream = mSocket.getOutputStream();
-			startListening();
-		} catch (UnknownHostException e) {
-			// TODO Tell the user we have a problem
-			e.printStackTrace();
-		} catch (IOException e) {
-			// TODO As above
-			e.printStackTrace();
-		}
-
-	}
-
-	@Override
-	public void closeConnection() {
-		try {
-			if (mSocket != null)
-				mSocket.close();
-		} catch (IOException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		}
-	}
+    private static final int PORT = 1599;
+
+    private Socket mSocket;
+
+    public NetworkClient(String ipAddress, Context aContext) {
+        super(aContext);
+        // FIXME: eventually networking will be fully threaded.
+        StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder()
+                        .permitAll().build();
+        StrictMode.setThreadPolicy(policy);
+        try {
+            mSocket = new Socket(ipAddress, PORT);
+            mInputStream = mSocket.getInputStream();
+            mReader = new BufferedReader(new InputStreamReader(mInputStream,
+                            CHARSET));
+            mOutputStream = mSocket.getOutputStream();
+            // Pairing.
+            Random aRandom = new Random();
+            String aPin = "" + aRandom.nextInt(10000);
+            Intent aIntent = new Intent(
+                            CommunicationService.MSG_PAIRING_STARTED);
+            aIntent.putExtra("PIN", aPin);
+            LocalBroadcastManager.getInstance(mContext).sendBroadcast(aIntent);
+            // Wait until we get the appropriate string back...
+            String aTemp = mReader.readLine();
+            if (!aTemp.equals("LO_SERVER_SERVER_PAIRED")) {
+                return;
+            } else {
+                aIntent = new Intent(
+                                CommunicationService.MSG_PAIRING_SUCCESSFUL);
+                LocalBroadcastManager.getInstance(mContext).sendBroadcast(
+                                aIntent);
+            }
+            while ((aTemp = mReader.readLine()).length() != 0) {
+                // Get rid of extra lines
+            }
+            startListening();
+        } catch (UnknownHostException e) {
+            // TODO Tell the user we have a problem
+            e.printStackTrace();
+        } catch (IOException e) {
+            // TODO As above
+            e.printStackTrace();
+        }
+
+    }
+
+    @Override
+    public void closeConnection() {
+        try {
+            if (mSocket != null)
+                mSocket.close();
+        } catch (IOException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
 
 }
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
\ No newline at end of file
commit 61f92ee500c7757e65a3f1944af973c4914c717e
Author: Andrzej J.R. Hunt <andrzej at ahunt.org>
Date:   Mon Aug 13 14:31:06 2012 +0200

    CommunicationService completely threaded. Preparations for pairing.
    
    Change-Id: If5a984230e776b380b8e536655cef061d2298e15

diff --git a/android/sdremote/AndroidManifest.xml b/android/sdremote/AndroidManifest.xml
index cea2e14..4364b24 100644
--- a/android/sdremote/AndroidManifest.xml
+++ b/android/sdremote/AndroidManifest.xml
@@ -16,6 +16,7 @@
         <activity android:name=".SelectorActivity" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
+
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
@@ -23,6 +24,8 @@
         <service android:name=".communication.CommunicationService" >
         </service>
 
+        <activity android:name=".PairingActivity" >
+        </activity>
         <activity
             android:name=".PresentationActivity"
             android:label="@string/title_activity_presentation" >
diff --git a/android/sdremote/res/layout/activity_pairing.xml b/android/sdremote/res/layout/activity_pairing.xml
new file mode 100644
index 0000000..122dec6
--- /dev/null
+++ b/android/sdremote/res/layout/activity_pairing.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <TextView
+        android:id="@+id/textView1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:text="@string/pairing_instructions_1" />
+
+    <TextView
+        android:id="@+id/textView2"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:text="@string/pairing_instructions_2" />
+
+    <TextView
+        android:id="@+id/textView3"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:text="@string/pairing_instructions_3" />
+
+    <TextView
+        android:id="@+id/pairing_pin"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:text=""
+        android:textAppearance="?android:attr/textAppearanceLarge" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/android/sdremote/res/layout/activity_selector_sublayout_server.xml b/android/sdremote/res/layout/activity_selector_sublayout_server.xml
index 14bec23..58a0374 100644
--- a/android/sdremote/res/layout/activity_selector_sublayout_server.xml
+++ b/android/sdremote/res/layout/activity_selector_sublayout_server.xml
@@ -2,7 +2,7 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical" >
+    android:orientation="vertical" android:clickable="true" >
 
     <ImageView
         android:id="@+id/sub_divider"
@@ -15,6 +15,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_margin="10dip"
-        android:textAppearance="?android:attr/textAppearanceMedium" />
+        android:textAppearance="?android:attr/textAppearanceMedium"
+         />
 
 </LinearLayout>
\ No newline at end of file
diff --git a/android/sdremote/res/values/strings.xml b/android/sdremote/res/values/strings.xml
index 0e14caa..523f538 100644
--- a/android/sdremote/res/values/strings.xml
+++ b/android/sdremote/res/values/strings.xml
@@ -23,5 +23,8 @@
     <string name="bluetooth">Bluetooth</string>
     <string name="wifi">WI-FI</string>
     <string name="selector_noservers">Searching for computers…</string>
+    <string name="pairing_instructions_1">In Impress, click on the "Slideshow" menu and select "Impress Remote".</string>
+    <string name="pairing_instructions_2">Choose "{1}" as your device.</string>
+    <string name="pairing_instructions_3">Then input this PIN:</string>
 
 </resources>
\ No newline at end of file
diff --git a/android/sdremote/src/org/libreoffice/impressremote/PairingActivity.java b/android/sdremote/src/org/libreoffice/impressremote/PairingActivity.java
new file mode 100644
index 0000000..386098c
--- /dev/null
+++ b/android/sdremote/src/org/libreoffice/impressremote/PairingActivity.java
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.libreoffice.impressremote;
+
+import org.libreoffice.impressremote.communication.CommunicationService;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.support.v4.content.LocalBroadcastManager;
+import android.widget.TextView;
+
+public class PairingActivity extends Activity {
+
+    private TextView mPinText;
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_pairing);
+
+        mPinText = (TextView) findViewById(R.id.pairing_pin);
+
+        IntentFilter aFilter = new IntentFilter(
+                        CommunicationService.MSG_PAIRING_STARTED);
+        aFilter.addAction(CommunicationService.MSG_PAIRING_SUCCESSFUL);
+        LocalBroadcastManager.getInstance(this).registerReceiver(mListener,
+                        aFilter);
+
+        //        mBluetoothContainer = findViewById(R.id.selector_container_bluetooth);
+        //        mBluetoothList = (LinearLayout) findViewById(R.id.selector_list_bluetooth);
+        //        mNetworkContainer = findViewById(R.id.selector_container_network);
+        //        mNetworkList = (LinearLayout) findViewById(R.id.selector_list_network);
+        //        mNoServerLabel = (TextView) findViewById(R.id.selector_label_none);
+        //
+        //        refreshLists();
+    }
+
+    private BroadcastReceiver mListener = new BroadcastReceiver() {
+
+        @Override
+        public void onReceive(Context aContext, Intent aIntent) {
+            if (aIntent.getAction().equals(
+                            CommunicationService.MSG_PAIRING_STARTED)) {
+                String aPin = aIntent.getStringExtra("PIN");
+                mPinText.setText(aPin);
+                //                refreshLists();
+            } else if (aIntent.getAction().equals(
+                            CommunicationService.MSG_PAIRING_SUCCESSFUL)) {
+                mPinText.setText("Paired!");
+            }
+
+        }
+    };
+
+}
diff --git a/android/sdremote/src/org/libreoffice/impressremote/SelectorActivity.java b/android/sdremote/src/org/libreoffice/impressremote/SelectorActivity.java
index 201f41c..a51df87 100644
--- a/android/sdremote/src/org/libreoffice/impressremote/SelectorActivity.java
+++ b/android/sdremote/src/org/libreoffice/impressremote/SelectorActivity.java
@@ -13,8 +13,8 @@ import java.util.HashMap;
 import java.util.Map.Entry;
 
 import org.libreoffice.impressremote.communication.CommunicationService;
-import org.libreoffice.impressremote.communication.CommunicationService.Protocol;
-import org.libreoffice.impressremote.communication.CommunicationService.Server;
+import org.libreoffice.impressremote.communication.Server;
+import org.libreoffice.impressremote.communication.Server.Protocol;
 
 import android.app.Activity;
 import android.content.BroadcastReceiver;
@@ -27,145 +27,188 @@ import android.os.Bundle;
 import android.os.IBinder;
 import android.support.v4.content.LocalBroadcastManager;
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
 public class SelectorActivity extends Activity {
 
-	private CommunicationService mCommunicationService;
-
-	private View mBluetoothContainer;
-	private LinearLayout mBluetoothList;
-	private View mNetworkContainer;
-	private LinearLayout mNetworkList;
-	private TextView mNoServerLabel;
-
-	/** Called when the activity is first created. */
-	@Override
-	public void onCreate(Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
-		setContentView(R.layout.activity_selector);
-
-		IntentFilter aFilter = new IntentFilter(
-		                CommunicationService.MSG_SERVERLIST_CHANGED);
-		LocalBroadcastManager.getInstance(this).registerReceiver(mListener,
-		                aFilter);
-
-		mBluetoothContainer = findViewById(R.id.selector_container_bluetooth);
-		mBluetoothList = (LinearLayout) findViewById(R.id.selector_list_bluetooth);
-		mNetworkContainer = findViewById(R.id.selector_container_network);
-		mNetworkList = (LinearLayout) findViewById(R.id.selector_list_network);
-		mNoServerLabel = (TextView) findViewById(R.id.selector_label_none);
-
-		refreshLists();
-	}
-
-	@Override
-	protected void onResume() {
-		super.onResume();
-		doBindService();
-	}
-
-	@Override
-	protected void onPause() {
-		// TODO Auto-generated method stub
-		super.onPause();
-		if (mCommunicationService != null) {
-			mCommunicationService.stopFindingServers();
-		}
-		doUnbindService();
-	}
-
-	void doBindService() {
-		Intent aIntent = new Intent(this, CommunicationService.class);
-		startService(aIntent);
-		bindService(aIntent, mConnection, Context.BIND_IMPORTANT);
-	}
-
-	void doUnbindService() {
-		unbindService(mConnection);
-	}
-
-	private ServiceConnection mConnection = new ServiceConnection() {
-		@Override
-		public void onServiceConnected(ComponentName aClassName,
-		                IBinder aService) {
-			mCommunicationService = ((CommunicationService.CBinder) aService)
-			                .getService();
-			mCommunicationService.startFindingServers();
-		}
-
-		@Override
-		public void onServiceDisconnected(ComponentName aClassName) {
-			mCommunicationService = null;
-		}
-	};
-
-	private BroadcastReceiver mListener = new BroadcastReceiver() {
-
-		@Override
-		public void onReceive(Context aContext, Intent aIntent) {
-			if (aIntent.getAction().equals(
-			                CommunicationService.MSG_SERVERLIST_CHANGED)) {
-				refreshLists();
-			}
-
-		}
-	};
-
-	private HashMap<Server, View> mBluetoothServers = new HashMap<Server, View>();
-	private HashMap<Server, View> mNetworkServers = new HashMap<Server, View>();
-
-	private void refreshLists() {
-		if (mCommunicationService != null) {
-
-			Server[] aServers = mCommunicationService.getServers();
-
-			// Bluetooth -- Remove old
-			for (Entry<Server, View> aEntry : mBluetoothServers.entrySet()) {
-				if (!Arrays.asList(aServers).contains(aEntry.getKey())) {
-					mBluetoothServers.remove(aEntry.getKey());
-					mBluetoothList.removeView(aEntry.getValue());
-				}
-			}
-			// Network -- Remove old
-			for (Entry<Server, View> aEntry : mNetworkServers.entrySet()) {
-				if (!Arrays.asList(aServers).contains(aEntry.getKey())) {
-					mNetworkServers.remove(aEntry.getKey());
-					mNetworkList.removeView(aEntry.getValue());
-				}
-			}
-			// Add all new
-			for (Server aServer : aServers) {
-				boolean aIsBluetooth = (aServer.getProtocol() == Protocol.BLUETOOTH);
-				HashMap<Server, View> aMap = aIsBluetooth ? mBluetoothServers
-				                : mNetworkServers;
-				LinearLayout aLayout = aIsBluetooth ? mBluetoothList
-				                : mNetworkList;
-
-				if (!aMap.containsValue(aServer)) {
-					View aView = getLayoutInflater()
-					                .inflate(R.layout.activity_selector_sublayout_server,
-					                                aLayout);
-					TextView aText = (TextView) aView
-					                .findViewById(R.id.selector_sub_label);
-					aText.setText(aServer.getName());
-					aMap.put(aServer, aView);
-				}
-
-			}
-		}
-		// Hide as necessary
-
-		mBluetoothContainer
-		                .setVisibility((mBluetoothServers.size() != 0) ? View.VISIBLE
-		                                : View.GONE);
-		mNetworkContainer
-		                .setVisibility((mNetworkServers.size() != 0) ? View.VISIBLE
-		                                : View.GONE);
-
-		mNoServerLabel.setVisibility(((mBluetoothServers.size() == 0) && (mNetworkServers
-		                .size() == 0)) ? View.VISIBLE : View.GONE);
-	}
+    private CommunicationService mCommunicationService;
+
+    private View mBluetoothContainer;
+    private LinearLayout mBluetoothList;
+    private View mNetworkContainer;
+    private LinearLayout mNetworkList;
+    private TextView mNoServerLabel;
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_selector);
+
+        IntentFilter aFilter = new IntentFilter(
+                        CommunicationService.MSG_SERVERLIST_CHANGED);
+        LocalBroadcastManager.getInstance(this).registerReceiver(mListener,
+                        aFilter);
+
+        mBluetoothContainer = findViewById(R.id.selector_container_bluetooth);
+        mBluetoothList = (LinearLayout) findViewById(R.id.selector_list_bluetooth);
+        mNetworkContainer = findViewById(R.id.selector_container_network);
+        mNetworkList = (LinearLayout) findViewById(R.id.selector_list_network);
+        mNoServerLabel = (TextView) findViewById(R.id.selector_label_none);
+
+        refreshLists();
+    }
+
+    @Override
+    public void onBackPressed() {
+        mCommunicationService.stopFindingServers();
+        Intent aIntent = new Intent(this, CommunicationService.class);
+        stopService(aIntent);
+        super.onBackPressed();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        doBindService();
+    }
+
+    @Override
+    protected void onPause() {
+        // TODO Auto-generated method stub
+        super.onPause();
+        if (mCommunicationService != null) {
+            mCommunicationService.stopFindingServers();
+        }
+        doUnbindService();
+    }
+
+    void doBindService() {
+        Intent aIntent = new Intent(this, CommunicationService.class);
+        startService(aIntent);
+        bindService(aIntent, mConnection, Context.BIND_IMPORTANT);
+    }
+
+    void doUnbindService() {
+        unbindService(mConnection);
+    }
+
+    private ServiceConnection mConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName aClassName,
+                        IBinder aService) {
+            mCommunicationService = ((CommunicationService.CBinder) aService)
+                            .getService();
+            mCommunicationService.startFindingServers();
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName aClassName) {
+            mCommunicationService = null;
+        }
+    };
+
+    private BroadcastReceiver mListener = new BroadcastReceiver() {
+
+        @Override
+        public void onReceive(Context aContext, Intent aIntent) {
+            if (aIntent.getAction().equals(
+                            CommunicationService.MSG_SERVERLIST_CHANGED)) {
+                refreshLists();
+            }
+
+        }
+    };
+
+    private HashMap<Server, View> mBluetoothServers = new HashMap<Server, View>();
+    private HashMap<Server, View> mNetworkServers = new HashMap<Server, View>();
+
+    private void refreshLists() {
+        if (mCommunicationService != null) {
+
+            Server[] aServers = mCommunicationService.getServers();
+
+            // Bluetooth -- Remove old
+            for (Entry<Server, View> aEntry : mBluetoothServers.entrySet()) {
+                if (!Arrays.asList(aServers).contains(aEntry.getKey())) {
+                    mBluetoothServers.remove(aEntry.getKey());
+                    mBluetoothList.removeView(aEntry.getValue());
+                }
+            }
+            // Network -- Remove old
+            for (Entry<Server, View> aEntry : mNetworkServers.entrySet()) {
+                if (!Arrays.asList(aServers).contains(aEntry.getKey())) {
+                    mNetworkServers.remove(aEntry.getKey());
+                    mNetworkList.removeView(aEntry.getValue());
+                }
+            }
+            // Add all new
+            for (Server aServer : aServers) {
+                boolean aIsBluetooth = (aServer.getProtocol() == Protocol.BLUETOOTH);
+                HashMap<Server, View> aMap = aIsBluetooth ? mBluetoothServers
+                                : mNetworkServers;
+                LinearLayout aLayout = aIsBluetooth ? mBluetoothList
+                                : mNetworkList;
+
+                if (!aMap.containsValue(aServer)) {
+                    View aView = getLayoutInflater()
+                                    .inflate(R.layout.activity_selector_sublayout_server,
+                                                    aLayout);
+                    TextView aText = (TextView) aView
+                                    .findViewById(R.id.selector_sub_label);
+                    aView.setOnClickListener(mClickListener);
+                    aText.setText(aServer.getName());
+                    aMap.put(aServer, aView);
+                }
+
+            }
+        }
+        // Hide as necessary
+
+        mBluetoothContainer
+                        .setVisibility((mBluetoothServers.size() != 0) ? View.VISIBLE
+                                        : View.GONE);
+        mNetworkContainer
+                        .setVisibility((mNetworkServers.size() != 0) ? View.VISIBLE
+                                        : View.GONE);
+
+        mNoServerLabel.setVisibility(((mBluetoothServers.size() == 0) && (mNetworkServers
+                        .size() == 0)) ? View.VISIBLE : View.GONE);
+    }
+
+    private OnClickListener mClickListener = new OnClickListener() {
+
+        @Override
+        public void onClick(View aView) {
+            // TODO Auto-generated method stub
+            mCommunicationService.stopFindingServers();
+
+            Server aDesiredServer = null;
+
+            if (mBluetoothServers.containsValue(aView)) {
+                for (Entry<Server, View> aEntry : mBluetoothServers.entrySet()) {
+                    if (aEntry.getValue() == aView) {
+                        aDesiredServer = aEntry.getKey();
+                    }
+                }
+            } else if (mNetworkServers.containsValue(aView)) {
+                for (Entry<Server, View> aEntry : mNetworkServers.entrySet()) {
+                    if (aEntry.getValue() == aView) {
+                        aDesiredServer = aEntry.getKey();
+                    }
+                }
+            }
+            if (aDesiredServer != null) {
+                mCommunicationService.connectTo(aDesiredServer);
+                Intent aIntent = new Intent(SelectorActivity.this,
+                                PairingActivity.class);
+                startActivity(aIntent);
+            }
+
+        }
+    };
+
 }
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
\ No newline at end of file
diff --git a/android/sdremote/src/org/libreoffice/impressremote/TestClient.java b/android/sdremote/src/org/libreoffice/impressremote/TestClient.java
index dc74cd5..2bc6e90 100644
--- a/android/sdremote/src/org/libreoffice/impressremote/TestClient.java
+++ b/android/sdremote/src/org/libreoffice/impressremote/TestClient.java
@@ -29,168 +29,166 @@ import android.widget.TextView;
 
 public class TestClient extends Activity {
 
-	private boolean mCurrentPreviewImageMissing = false;
-
-	private boolean mIsBound = false;
-
-	private CommunicationService mCommunicationService;
-
-	final Messenger mMessenger = new Messenger(new MessageHandler());
-
-	/** Called when the activity is first created. */
-	@Override
-	public void onCreate(Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
-		setContentView(R.layout.testlayout);
-		setupUI();
-
-	}
-
-	@Override
-	protected void onResume() {
-		super.onResume();
-		doBindService();
-
-	}
-
-	// FIXME: move all necessary code to CommunicationService.onUnbind
-
-	@Override
-	protected void onPause() {
-		super.onPause();
-	}
-
-	@Override
-	public void onBackPressed() {
-		// TODO Auto-generated method stub
-		mCommunicationService.disconnect();
-		stopService(new Intent(this, CommunicationService.class));
-		doUnbindService();
-		finish();
-		super.onBackPressed();
-	}
-
-	@Override
-	protected void onStop() {
-		// TODO Auto-generated method stub
-		super.onStop();
-		// mCommunicationService.disconnect();
-		// stopService(new Intent(this, CommunicationService.class));
-	}
-
-	private ServiceConnection mConnection = new ServiceConnection() {
-		@Override
-		public void onServiceConnected(ComponentName aClassName,
-		                IBinder aService) {
-			mCommunicationService = ((CommunicationService.CBinder) aService)
-			                .getService();
-			mCommunicationService.connectTo(
-			                CommunicationService.Protocol.NETWORK,
-			                "192.168.0.18");
-			mCommunicationService.setActivityMessenger(mMessenger);
-			enableButtons(true);
-		}
-
-		@Override
-		public void onServiceDisconnected(ComponentName aClassName) {
-			mCommunicationService = null;
-			enableButtons(false);
-		}
-	};
-
-	void doBindService() {
-		Intent aIntent = new Intent(this, CommunicationService.class);
-		startService(aIntent);
-		bindService(aIntent, mConnection, Context.BIND_IMPORTANT);
-		mIsBound = true;
-	}
-
-	void doUnbindService() {
-		mCommunicationService.setActivityMessenger(null);
-		if (mIsBound) {
-			unbindService(mConnection);
-			mIsBound = false;
-		}
-	}
-
-	private Button mButtonNext;
-
-	private Button mButtonPrevious;
-
-	private ImageView mImageView;
-
-	private TextView mSlideLabel;
-
-	private void setupUI() {
-		mButtonNext = (Button) findViewById(R.id.button_next);
-		mButtonPrevious = (Button) findViewById(R.id.button_previous);
-		mImageView = (ImageView) findViewById(R.id.image_preview);
-		mSlideLabel = (TextView) findViewById(R.id.label_curSlide);
-
-		enableButtons(false);
-
-		mButtonNext.setOnClickListener(new OnClickListener() {
-
-			@Override
-			public void onClick(View v) {
-				mCommunicationService.getTransmitter().nextTransition();
-
-			}
-
-		});
-
-		mButtonPrevious.setOnClickListener(new OnClickListener() {
-
-			@Override
-			public void onClick(View v) {
-				mCommunicationService.getTransmitter().previousTransition();
-
-			}
-
-		});
-
-		Button mThumbnailButton = (Button) findViewById(R.id.button_thumbnail);
-
-		mThumbnailButton.setOnClickListener(new OnClickListener() {
-			@Override
-			public void onClick(View v) {
-				Intent aIntent = new Intent(TestClient.this,
-				                PresentationActivity.class);
-				startActivity(aIntent);
-			}
-		});
-
-	}
-
-	private void enableButtons(boolean aEnabled) {
-		mButtonNext.setEnabled(aEnabled);
-		mButtonPrevious.setEnabled(aEnabled);
-	}
-
-	class MessageHandler extends Handler {
-		@Override
-		public void handleMessage(Message aMessage) {
-			Bundle aData = aMessage.getData();
-			switch (aMessage.what) {
-			case CommunicationService.MSG_SLIDE_CHANGED:
-				int newSlide = aData.getInt("slide_number");
-				mSlideLabel.setText("Slide " + newSlide);
-				mCurrentPreviewImageMissing = true;
-				// We continue on to try and update the image.
-			case CommunicationService.MSG_SLIDE_PREVIEW:
-				int aSlideNumber = aData.getInt("slide_number");
-				if (mCurrentPreviewImageMissing) {
-					Bitmap aImage = mCommunicationService.getSlideShow()
-					                .getImage(aSlideNumber);
-					if (aImage != null) {
-						mImageView.setImageBitmap(aImage);
-						mCurrentPreviewImageMissing = false;
-					}
-				}
-				break;
-
-			}
-		}
-	}
+    private boolean mCurrentPreviewImageMissing = false;
+
+    private boolean mIsBound = false;
+
+    private CommunicationService mCommunicationService;
+
+    final Messenger mMessenger = new Messenger(new MessageHandler());
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.testlayout);
+        setupUI();
+
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        doBindService();
+
+    }
+
+    // FIXME: move all necessary code to CommunicationService.onUnbind
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+    }
+
+    @Override
+    public void onBackPressed() {
+        // TODO Auto-generated method stub
+        mCommunicationService.disconnect();
+        stopService(new Intent(this, CommunicationService.class));
+        doUnbindService();
+        finish();
+        super.onBackPressed();
+    }
+
+    @Override
+    protected void onStop() {
+        // TODO Auto-generated method stub
+        super.onStop();
+        // mCommunicationService.disconnect();
+        // stopService(new Intent(this, CommunicationService.class));
+    }
+
+    private ServiceConnection mConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName aClassName,
+                        IBinder aService) {
+            mCommunicationService = ((CommunicationService.CBinder) aService)
+                            .getService();
+            //            mCommunicationService.connectTo(Protocol.NETWORK, "192.168.0.18");
+            mCommunicationService.setActivityMessenger(mMessenger);
+            enableButtons(true);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName aClassName) {
+            mCommunicationService = null;
+            enableButtons(false);
+        }
+    };
+
+    void doBindService() {
+        Intent aIntent = new Intent(this, CommunicationService.class);
+        startService(aIntent);
+        bindService(aIntent, mConnection, Context.BIND_IMPORTANT);
+        mIsBound = true;
+    }
+
+    void doUnbindService() {
+        mCommunicationService.setActivityMessenger(null);
+        if (mIsBound) {
+            unbindService(mConnection);
+            mIsBound = false;
+        }
+    }
+
+    private Button mButtonNext;
+
+    private Button mButtonPrevious;
+
+    private ImageView mImageView;
+
+    private TextView mSlideLabel;
+
+    private void setupUI() {
+        mButtonNext = (Button) findViewById(R.id.button_next);
+        mButtonPrevious = (Button) findViewById(R.id.button_previous);
+        mImageView = (ImageView) findViewById(R.id.image_preview);
+        mSlideLabel = (TextView) findViewById(R.id.label_curSlide);
+
+        enableButtons(false);
+
+        mButtonNext.setOnClickListener(new OnClickListener() {
+
+            @Override
+            public void onClick(View v) {
+                mCommunicationService.getTransmitter().nextTransition();
+
+            }
+
+        });
+
+        mButtonPrevious.setOnClickListener(new OnClickListener() {
+
+            @Override
+            public void onClick(View v) {
+                mCommunicationService.getTransmitter().previousTransition();
+
+            }
+
+        });
+
+        Button mThumbnailButton = (Button) findViewById(R.id.button_thumbnail);
+
+        mThumbnailButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent aIntent = new Intent(TestClient.this,
+                                PresentationActivity.class);
+                startActivity(aIntent);
+            }
+        });
+
+    }
+
+    private void enableButtons(boolean aEnabled) {
+        mButtonNext.setEnabled(aEnabled);
+        mButtonPrevious.setEnabled(aEnabled);
+    }
+
+    class MessageHandler extends Handler {
+        @Override
+        public void handleMessage(Message aMessage) {
+            Bundle aData = aMessage.getData();
+            switch (aMessage.what) {
+            case CommunicationService.MSG_SLIDE_CHANGED:
+                int newSlide = aData.getInt("slide_number");
+                mSlideLabel.setText("Slide " + newSlide);
+                mCurrentPreviewImageMissing = true;
+                // We continue on to try and update the image.
+            case CommunicationService.MSG_SLIDE_PREVIEW:
+                int aSlideNumber = aData.getInt("slide_number");
+                if (mCurrentPreviewImageMissing) {
+                    Bitmap aImage = mCommunicationService.getSlideShow()
+                                    .getImage(aSlideNumber);
+                    if (aImage != null) {
+                        mImageView.setImageBitmap(aImage);
+                        mCurrentPreviewImageMissing = false;
+                    }
+                }
+                break;
+
+            }
+        }
+    }
 }
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
\ No newline at end of file
diff --git a/android/sdremote/src/org/libreoffice/impressremote/communication/CommunicationService.java b/android/sdremote/src/org/libreoffice/impressremote/communication/CommunicationService.java
index 879c962..c8ecace 100644
--- a/android/sdremote/src/org/libreoffice/impressremote/communication/CommunicationService.java
+++ b/android/sdremote/src/org/libreoffice/impressremote/communication/CommunicationService.java
@@ -14,156 +14,160 @@ import android.os.Binder;
 import android.os.IBinder;
 import android.os.Messenger;
 
-public class CommunicationService extends Service {
-
-	/**
-	 * Return the service to clients.
-	 */
-	public class CBinder extends Binder {
-		public CommunicationService getService() {
-			return CommunicationService.this;
-		}
-	}
-
-	private final IBinder mBinder = new CBinder();
-
-	public enum Protocol {
-		NETWORK, BLUETOOTH
-	};
-
-	public static final int MSG_SLIDESHOW_STARTED = 1;
-	public static final int MSG_SLIDE_CHANGED = 2;
-	public static final int MSG_SLIDE_PREVIEW = 3;
-	public static final int MSG_SLIDE_NOTES = 4;
-
-	public static final String MSG_SERVERLIST_CHANGED = "SERVERLIST_CHANGED";
-
-	private Transmitter mTransmitter;
-
-	private Client mClient;
-
-	private Receiver mReceiver = new Receiver();
-
-	private ServerFinder mFinder = new ServerFinder(this);
-
-	public void setActivityMessenger(Messenger aActivityMessenger) {
-		mReceiver.setActivityMessenger(aActivityMessenger);
-	}
-
-	@Override
-	public IBinder onBind(Intent intent) {
-		// TODO Auto-generated method stub
-		return mBinder;
-	}
-
-	@Override
-	public void onCreate() {
-		// TODO Create a notification (if configured).
-	}
-
-	@Override
-	public void onDestroy() {
-		// TODO Destroy the notification (as necessary).
-	}
-
-	public Transmitter getTransmitter() {
-		return mTransmitter;
-	}
-
-	public Server[] getServers() {
-		return mFinder.getServerList();
-	}
-
-	public void startFindingServers() {
-		mFinder.startFinding();
-	}
-
-	public void stopFindingServers() {
-		mFinder.stopFinding();
-	}
-
-	/**
-	 * Connect to a specific server. This method cannot be called on the main
-	 * activity thread.
-	 *
-	 * @param aServer
-	 *            The Server to connect to.
-	 */
-	public void connectTo(Server aServer) {
-		connectTo(aServer.getProtocol(), aServer.getAddress());
-	}
-
-	/**
-	 * Connect to a specific server. This method cannot be called on the main
-	 * activity thread.
-	 *
-	 * @param aProtocol
-	 * @param address
-	 */
-	public void connectTo(Protocol aProtocol, String address) {
-		switch (aProtocol) {
-		case NETWORK:
-			mClient = new NetworkClient(address);
-			mTransmitter = new Transmitter(mClient);
-			mClient.setReceiver(mReceiver);
-			break;
-		case BLUETOOTH:
-			break;
-		default:
-			break;
-
-		}
-
-	}
-
-	public void disconnect() {
-		mClient.closeConnection();
-	}
-
-	public SlideShow getSlideShow() {
-		return mReceiver.getSlideShow();
-	}
-
-	// ---------------------------------------------------- SERVER -------------
-	/**
-	 * Class describing a remote server.
-	 */
-	public static class Server {
-		private Protocol mProtocol;
-		private String mAddress;
-		private String mName;
-		private long mTimeDiscovered;
-
-		protected Server(Protocol aProtocol, String aAddress, String aName,
-		                long aTimeDiscovered) {
-			mProtocol = aProtocol;
-			mAddress = aAddress;
-			mName = aName;
-			mTimeDiscovered = aTimeDiscovered;
-		}
-
-		public Protocol getProtocol() {
-			return mProtocol;
-		}
-
-		public String getAddress() {
-			return mAddress;
-		}
-
-		/**
-		 * Get a human friendly name for the server.
-		 *
-		 * @return The name.
-		 */
-		public String getName() {
-			return mName;
-		}
-
-		public long getTimeDiscovered() {
-			return mTimeDiscovered;
-		}
-
-	}
+public class CommunicationService extends Service implements Runnable {
+
+    public enum State {
+        DISCONNECTED, SEARCHING, CONNECTING, CONNECTED
+    };
+
+    /**
+     * Used to protect all writes to mState, mStateDesired, and mServerDesired.
+     */
+    private Object mConnectionVariableMutex = new Object();
+
+    private State mState = State.DISCONNECTED;
+
+    private State mStateDesired = State.DISCONNECTED;
+
+    private Server mServerDesired = null;
+
+    @Override
+    public void run() {
+        while (true) {
+            // Condition
+            try {
+                wait();
+            } catch (InterruptedException e) {
+                // We don't care.
+            }
+            // Work
+            synchronized (mConnectionVariableMutex) {
+                if ((mStateDesired == State.CONNECTED && mState == State.CONNECTED)
+                                || (mStateDesired == State.DISCONNECTED && mState == State.CONNECTED)) {
+                    mClient.closeConnection();
+                    mState = State.DISCONNECTED;
+                }
+                if (mStateDesired == State.CONNECTED) {
+                    switch (mServerDesired.getProtocol()) {
+                    case NETWORK:
+                        mClient = new NetworkClient(mServerDesired.getAddress());
+                        mTransmitter = new Transmitter(mClient);
+                        mClient.setReceiver(mReceiver);
+                        break;
+                    case BLUETOOTH:
+                        break;
+                    }
+                    mState = State.CONNECTED;
+                }
+            }
+        }
+
+    }
+
+    public void startSearching() {
+        synchronized (mConnectionVariableMutex) {
+            if (mState == State.CONNECTING || mState == State.CONNECTED) {
+                disconnect();
+            }
+            mFinder.startFinding();
+            mState = State.SEARCHING;
+        }
+    }
+
+    public void stopSearching() {
+        synchronized (mConnectionVariableMutex) {
+            mFinder.stopFinding();
+            mState = State.DISCONNECTED;
+        }
+    }
+
+    public void connectTo(Server aServer) {
+        synchronized (mConnectionVariableMutex) {
+            if (mState == State.SEARCHING) {
+                mFinder.stopFinding();
+                mState = State.DISCONNECTED;
+            }
+            mServerDesired = aServer;
+            mStateDesired = State.CONNECTED;
+            notify();
+        }
+        // TODO: connect
+    }
+
+    public void disconnect() {
+        synchronized (mConnectionVariableMutex) {
+            mStateDesired = State.DISCONNECTED;
+            notify();
+        }
+    }
+
+    /**
+     * Return the service to clients.
+     */
+    public class CBinder extends Binder {
+        public CommunicationService getService() {
+            return CommunicationService.this;
+        }
+    }
+
+    private final IBinder mBinder = new CBinder();
+
+    public static final int MSG_SLIDESHOW_STARTED = 1;
+    public static final int MSG_SLIDE_CHANGED = 2;
+    public static final int MSG_SLIDE_PREVIEW = 3;
+    public static final int MSG_SLIDE_NOTES = 4;
+
+    public static final String MSG_SERVERLIST_CHANGED = "SERVERLIST_CHANGED";
+    public static final String MSG_PAIRING_STARTED = "PAIRING_STARTED";
+    public static final String MSG_PAIRING_SUCCESSFUL = "PAIRING_SUCCESSFUL";
+
+    private Transmitter mTransmitter;
+
+    private Client mClient;
+
+    private Receiver mReceiver = new Receiver();
+
+    private ServerFinder mFinder = new ServerFinder(this);
+
+    public void setActivityMessenger(Messenger aActivityMessenger) {
+        mReceiver.setActivityMessenger(aActivityMessenger);
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        // TODO Auto-generated method stub
+        return mBinder;
+    }
+
+    @Override
+    public void onCreate() {
+        // TODO Create a notification (if configured).
+    }
+
+    @Override
+    public void onDestroy() {
+        // TODO Destroy the notification (as necessary).
+    }
+
+    public Transmitter getTransmitter() {
+        return mTransmitter;
+    }
+
+    public Server[] getServers() {
+        return mFinder.getServerList();
+    }
+
+    public void startFindingServers() {
+        mFinder.startFinding();
+    }
+
+    public void stopFindingServers() {
+        mFinder.stopFinding();
+    }
+
+    public SlideShow getSlideShow() {
+        return mReceiver.getSlideShow();
+    }
 
 }
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
\ No newline at end of file
diff --git a/android/sdremote/src/org/libreoffice/impressremote/communication/Server.java b/android/sdremote/src/org/libreoffice/impressremote/communication/Server.java
new file mode 100644
index 0000000..dfa75fe
--- /dev/null
+++ b/android/sdremote/src/org/libreoffice/impressremote/communication/Server.java
@@ -0,0 +1,43 @@
+package org.libreoffice.impressremote.communication;
+
+public class Server {
+
+    public enum Protocol {
+        NETWORK, BLUETOOTH
+    };
+
+    private Protocol mProtocol;
+    private String mAddress;
+    private String mName;
+    private long mTimeDiscovered;
+
+    protected Server(Protocol aProtocol, String aAddress, String aName,
+                    long aTimeDiscovered) {
+        mProtocol = aProtocol;
+        mAddress = aAddress;
+        mName = aName;
+        mTimeDiscovered = aTimeDiscovered;
+    }
+
+    public Protocol getProtocol() {
+        return mProtocol;
+    }
+
+    public String getAddress() {
+        return mAddress;
+    }
+
+    /**
+     * Get a human friendly name for the server.
+     *
+     * @return The name.
+     */
+    public String getName() {
+        return mName;
+    }
+
+    public long getTimeDiscovered() {
+        return mTimeDiscovered;
+    }
+
+}
diff --git a/android/sdremote/src/org/libreoffice/impressremote/communication/ServerFinder.java b/android/sdremote/src/org/libreoffice/impressremote/communication/ServerFinder.java
index 29ae7de..55b6f54 100644
--- a/android/sdremote/src/org/libreoffice/impressremote/communication/ServerFinder.java
+++ b/android/sdremote/src/org/libreoffice/impressremote/communication/ServerFinder.java
@@ -8,8 +8,6 @@ import java.net.InetAddress;
 import java.net.SocketException;
 import java.util.HashMap;
 
-import org.libreoffice.impressremote.communication.CommunicationService.Server;
-
 import android.content.Context;
 import android.content.Intent;
 import android.support.v4.content.LocalBroadcastManager;
@@ -66,8 +64,8 @@ public class ServerFinder {
             if (aName == null) {
                 return;
             }
-            Server aServer = new Server(CommunicationService.Protocol.NETWORK,
-                            aPacket.getAddress().toString(), aName,
+            Server aServer = new Server(Server.Protocol.NETWORK, aPacket
+                            .getAddress().toString(), aName,
                             System.currentTimeMillis());
             mServerList.put(aServer.getAddress(), aServer);
 


More information about the Libreoffice-commits mailing list