http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/activities/SignUpActivity.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/activities/SignUpActivity.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/activities/SignUpActivity.java
new file mode 100644
index 0000000..76bdaa0
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/activities/SignUpActivity.java
@@ -0,0 +1,95 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package app.android.box.waveprotocol.org.androidwave.activities;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import app.android.box.waveprotocol.org.androidwave.R;
+import app.android.box.waveprotocol.org.androidwave.service.WaveService;
+import app.android.box.waveprotocol.org.androidwave.util.Util;
+
+public class SignUpActivity extends Activity {
+
+    EditText username;
+    EditText password;
+    EditText reEnterPassword;
+    Button signUp;
+    TextView signIn;
+
+    AsyncTask<String, Void, Boolean> waveSignUpTask;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.signup_activity);
+
+        username = (EditText) findViewById(R.id.input_username);
+        password = (EditText) findViewById(R.id.input_password);
+        reEnterPassword = (EditText) findViewById(R.id.input_reEnterPassword);
+        signUp = (Button) findViewById(R.id.btn_signIn);
+        signIn = (TextView) findViewById(R.id.link_signup);
+
+        waveSignUpTask = new AsyncTask<String, Void, Boolean>() {
+
+            @Override
+            protected Boolean doInBackground(String... params) {
+                WaveService waveService = new WaveService();
+                return waveService.waveSignUpTask(params[0], params[1], 
params[2]);
+            }
+
+
+            @Override
+            protected void onPostExecute(Boolean signUpResult) {
+                if (signUpResult) {
+                    Intent openLoginActivity = new 
Intent("app.android.box.waveprotocol.org.androidwave.LOGINACTIVITY");
+                    startActivity(openLoginActivity);
+                    Toast.makeText(SignUpActivity.this, "User sign up 
successfully", Toast.LENGTH_LONG).show();
+                } else {
+                    Toast.makeText(SignUpActivity.this, "User sign up fail", 
Toast.LENGTH_LONG).show();
+                    Toast.makeText(SignUpActivity.this, "Please try again 
later...", Toast.LENGTH_LONG).show();
+                }
+            }
+        };
+
+        signUp.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                
waveSignUpTask.execute(Util.getHostAndUserNames(username.getText().toString())[1],
 Util.getHostAndUserNames(username.getText().toString())[0], 
password.getText().toString());
+            }
+        });
+
+        signIn.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent openLoginActivity = new 
Intent("app.android.box.waveprotocol.org.androidwave.LOGINACTIVITY");
+                startActivity(openLoginActivity);
+            }
+        });
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/activities/TestMainActivity.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/activities/TestMainActivity.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/activities/TestMainActivity.java
new file mode 100644
index 0000000..d809336
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/activities/TestMainActivity.java
@@ -0,0 +1,124 @@
+package app.android.box.waveprotocol.org.androidwave.activities;
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import app.android.box.waveprotocol.org.androidwave.R;
+
+/**
+ * Created by roellk on 8/20/2015.
+ */
+public class TestMainActivity extends AppCompatActivity implements 
AdapterView.OnItemClickListener{
+    public static final String TYPE = "TYPE";
+    private DataSource mDataSource;
+    private ListView mListView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main_activity);
+
+        mListView = (ListView) findViewById(R.id.listView);
+        mDataSource = new DataSource(this);
+        mListView.setAdapter(new SampleAdapter());
+        mListView.setOnItemClickListener(this);
+    }
+
+    @Override
+    public void onItemClick(AdapterView<?> parent, View view, int position, 
long id) {
+        DataItem item = (DataItem) mListView.getItemAtPosition(position);
+
+        // if navigation is supported, open the next activity
+        if (item.getNavigationInfo() != DataSource.NO_NAVIGATION) {
+            Intent intent = new Intent(this, ListActivity.class);
+            intent.putExtra(TYPE, item.getNavigationInfo());
+            startActivity(intent);
+        }
+    }
+
+    private class SampleAdapter extends BaseAdapter {
+
+        @Override
+        public int getCount() {
+            return mDataSource.getCount();
+        }
+
+        @Override
+        public DataItem getItem(int position) {
+            return mDataSource.getItem(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            ViewHolder holder;
+            if (convertView == null) {
+                convertView = View.inflate(TestMainActivity.this, 
R.layout.list_item_layout, null);
+                holder = new ViewHolder(convertView);
+                convertView.setTag(holder);
+            } else {
+                holder = (ViewHolder) convertView.getTag();
+            }
+
+            DataItem item = getItem(position);
+
+            final Drawable drawable = item.getDrawable();
+            holder.imageView.setImageDrawable(drawable);
+            holder.textView.setText(item.getLabel());
+
+            // if navigation is supported, show the ">" navigation icon
+            if (item.getNavigationInfo() != DataSource.NO_NAVIGATION) {
+                holder.textView.setCompoundDrawablesWithIntrinsicBounds(null,
+                        null,
+                        
getResources().getDrawable(R.drawable.ic_action_next_item),
+                        null);
+            }
+            else {
+                holder.textView.setCompoundDrawablesWithIntrinsicBounds(null,
+                        null,
+                        null,
+                        null);
+            }
+
+            // fix for animation not playing for some below 4.4 devices
+            if (drawable instanceof AnimationDrawable) {
+                holder.imageView.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        ((AnimationDrawable) drawable).stop();
+                        ((AnimationDrawable) drawable).start();
+                    }
+                });
+            }
+
+            return convertView;
+        }
+    }
+
+    private static class ViewHolder {
+
+        private ImageView imageView;
+
+        private TextView textView;
+
+        private ViewHolder(View view) {
+            imageView = (ImageView) view.findViewById(R.id.imageView);
+            textView = (TextView) view.findViewById(R.id.textView);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/ClientPercentEncoderDecoder.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/ClientPercentEncoderDecoder.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/ClientPercentEncoderDecoder.java
new file mode 100644
index 0000000..a15b592
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/ClientPercentEncoderDecoder.java
@@ -0,0 +1,30 @@
+package app.android.box.waveprotocol.org.androidwave.service;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+
+import org.waveprotocol.wave.model.id.URIEncoderDecoder;
+import org.waveprotocol.wave.model.id.URIEncoderDecoder.EncodingException;
+
+public class ClientPercentEncoderDecoder implements 
URIEncoderDecoder.PercentEncoderDecoder {
+
+    @Override
+    public String encode(String encodingValue) throws EncodingException {
+        String encodedValue;
+        try {
+            return URLEncoder.encode(encodingValue, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new EncodingException("Unable to encoding value " + 
encodingValue );
+        }
+    }
+
+    @Override
+    public String decode(String decodingValue) throws EncodingException {
+        try {
+            return URLDecoder.decode(decodingValue, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new EncodingException("Unable to decoding value " + 
decodingValue );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/RemoteViewServiceMultiplexer.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/RemoteViewServiceMultiplexer.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/RemoteViewServiceMultiplexer.java
new file mode 100644
index 0000000..1ffb832
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/RemoteViewServiceMultiplexer.java
@@ -0,0 +1,22 @@
+package app.android.box.waveprotocol.org.androidwave.service;
+
+import org.waveprotocol.box.common.comms.ProtocolWaveletUpdate;
+
+public class RemoteViewServiceMultiplexer implements WaveWebSocketCallback {
+
+    private final WaveWebSocketClient socket;
+
+    private final String userId;
+
+    public RemoteViewServiceMultiplexer(WaveWebSocketClient socket, String 
userId) {
+        this.socket = socket;
+        this.userId = userId;
+
+        socket.attachHandler(this);
+    }
+
+    @Override
+    public void onWaveletUpdate(ProtocolWaveletUpdate message) {
+
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/RemoteWaveViewService.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/RemoteWaveViewService.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/RemoteWaveViewService.java
new file mode 100644
index 0000000..98e8cad
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/RemoteWaveViewService.java
@@ -0,0 +1,42 @@
+package app.android.box.waveprotocol.org.androidwave.service;
+
+import org.waveprotocol.wave.concurrencycontrol.channel.WaveViewService;
+import org.waveprotocol.wave.concurrencycontrol.wave.CcDataDocumentImpl;
+import org.waveprotocol.wave.model.id.IdFilter;
+import org.waveprotocol.wave.model.id.WaveId;
+import org.waveprotocol.wave.model.id.WaveletId;
+import org.waveprotocol.wave.model.id.WaveletName;
+import org.waveprotocol.wave.model.operation.wave.WaveletDelta;
+import org.waveprotocol.wave.model.version.HashedVersion;
+
+import java.util.List;
+import java.util.Map;
+
+import 
app.android.box.waveprotocol.org.androidwave.service.documents.WaveDocuments;
+
+
+public class RemoteWaveViewService implements WaveViewService {
+
+    public RemoteWaveViewService(WaveId waveId, RemoteViewServiceMultiplexer 
channel, WaveDocuments<CcDataDocumentImpl> documentRegistry) {
+    }
+
+    @Override
+    public void viewOpen(IdFilter idFilter, Map<WaveletId, 
List<HashedVersion>> map, OpenCallback openCallback) {
+
+    }
+
+    @Override
+    public String viewSubmit(WaveletName waveletName, WaveletDelta 
waveletDelta, String s, SubmitCallback submitCallback) {
+        return null;
+    }
+
+    @Override
+    public void viewClose(WaveId waveId, String s, CloseCallback 
closeCallback) {
+
+    }
+
+    @Override
+    public String debugGetProfilingInfo(String s) {
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/SubmitResponseCallback.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/SubmitResponseCallback.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/SubmitResponseCallback.java
new file mode 100644
index 0000000..4f60e45
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/SubmitResponseCallback.java
@@ -0,0 +1,8 @@
+package app.android.box.waveprotocol.org.androidwave.service;
+
+import org.waveprotocol.box.common.comms.ProtocolSubmitResponse;
+
+public interface SubmitResponseCallback {
+
+    void run(ProtocolSubmitResponse response);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveRender.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveRender.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveRender.java
new file mode 100644
index 0000000..39ce08a
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveRender.java
@@ -0,0 +1,285 @@
+package app.android.box.waveprotocol.org.androidwave.service;
+
+import com.google.common.base.Preconditions;
+
+import 
org.waveprotocol.wave.concurrencycontrol.channel.OperationChannelMultiplexer;
+import 
org.waveprotocol.wave.concurrencycontrol.channel.OperationChannelMultiplexerImpl;
+import org.waveprotocol.wave.concurrencycontrol.channel.ViewChannelFactory;
+import org.waveprotocol.wave.concurrencycontrol.channel.ViewChannelImpl;
+import org.waveprotocol.wave.concurrencycontrol.channel.WaveViewService;
+import org.waveprotocol.wave.concurrencycontrol.common.UnsavedDataListener;
+import 
org.waveprotocol.wave.concurrencycontrol.common.UnsavedDataListenerFactory;
+import org.waveprotocol.wave.concurrencycontrol.wave.CcDataDocumentImpl;
+import org.waveprotocol.wave.model.conversation.ObservableConversationView;
+import org.waveprotocol.wave.model.conversation.WaveBasedConversationView;
+import org.waveprotocol.wave.model.document.WaveContext;
+import org.waveprotocol.wave.model.document.indexed.IndexedDocumentImpl;
+import org.waveprotocol.wave.model.document.operation.DocInitialization;
+import org.waveprotocol.wave.model.document.operation.automaton.DocumentSchema;
+import org.waveprotocol.wave.model.id.IdConstants;
+import org.waveprotocol.wave.model.id.IdFilter;
+import org.waveprotocol.wave.model.id.IdGenerator;
+import org.waveprotocol.wave.model.id.IdURIEncoderDecoder;
+import org.waveprotocol.wave.model.id.WaveId;
+import org.waveprotocol.wave.model.id.WaveletId;
+import org.waveprotocol.wave.model.schema.SchemaProvider;
+import org.waveprotocol.wave.model.schema.conversation.ConversationSchemas;
+import org.waveprotocol.wave.model.util.FuzzingBackOffScheduler;
+import 
org.waveprotocol.wave.model.util.FuzzingBackOffScheduler.CollectiveScheduler;
+import org.waveprotocol.wave.model.version.HashedVersion;
+import org.waveprotocol.wave.model.version.HashedVersionFactory;
+import org.waveprotocol.wave.model.version.HashedVersionZeroFactoryImpl;
+import org.waveprotocol.wave.model.wave.ParticipantId;
+import org.waveprotocol.wave.model.wave.data.ObservableWaveletData;
+import org.waveprotocol.wave.model.wave.data.WaveViewData;
+import 
org.waveprotocol.wave.model.wave.data.impl.ObservablePluggableMutableDocument;
+import org.waveprotocol.wave.model.wave.data.impl.WaveViewDataImpl;
+import org.waveprotocol.wave.model.wave.data.impl.WaveletDataImpl;
+import org.waveprotocol.wave.model.wave.data.DocumentFactory;
+import org.waveprotocol.wave.model.wave.opbased.OpBasedWavelet;
+import org.waveprotocol.wave.model.wave.opbased.WaveViewImpl;
+import org.waveprotocol.wave.model.wave.opbased.WaveViewImpl.WaveletFactory;
+import 
org.waveprotocol.wave.model.wave.opbased.WaveViewImpl.WaveletConfigurator;
+import org.waveprotocol.wave.model.waveref.WaveRef;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Set;
+import java.util.Timer;
+
+import 
app.android.box.waveprotocol.org.androidwave.service.concurrencycontrol.Connector;
+import 
app.android.box.waveprotocol.org.androidwave.service.concurrencycontrol.Connector.Command;
+import 
app.android.box.waveprotocol.org.androidwave.service.concurrencycontrol.LiveChannelBinder;
+import 
app.android.box.waveprotocol.org.androidwave.service.concurrencycontrol.WaveletOperationalizer;
+import 
app.android.box.waveprotocol.org.androidwave.service.documents.WaveDocuments;
+import app.android.box.waveprotocol.org.androidwave.service.logger.WaveLogger;
+import app.android.box.waveprotocol.org.androidwave.service.models.Model;
+import 
app.android.box.waveprotocol.org.androidwave.service.scheduler.OptimalGroupingScheduler;
+import 
app.android.box.waveprotocol.org.androidwave.service.scheduler.Scheduler;
+import 
app.android.box.waveprotocol.org.androidwave.service.scheduler.SchedulerInstance;
+
+public class WaveRender {
+
+    private final WaveRef waveRef;
+    private final boolean isNewWave;
+    private final Set<ParticipantId> otherParticipants;
+    private ParticipantId signedInuser;
+    private IdGenerator idGenerator;
+
+    private final RemoteViewServiceMultiplexer channel;
+    private final UnsavedDataListener unsavedDataListener;
+
+    private WaveViewData waveData;
+    private Connector connector;
+    private WaveContext waveContext;
+
+    private ObservableConversationView conversations;
+    private WaveViewImpl<OpBasedWavelet> wave;
+    private WaveletOperationalizer wavelets;
+    private WaveDocuments<CcDataDocumentImpl> documentRegistry;
+
+    private CollectiveScheduler rpcScheduler;
+
+    private boolean isClosed = true;
+    private Timer timer;
+
+
+    public WaveRender(boolean isNewWave, WaveRef waveRef, 
RemoteViewServiceMultiplexer waveChannel,
+                      ParticipantId waveParticipant,
+                      Set<ParticipantId> waveOtherParticipants, IdGenerator 
waveIdGenerator,
+                      UnsavedDataListener unsavedDataListener, Timer timer) {
+        this.signedInuser = waveParticipant;
+        this.waveRef = waveRef;
+        this.isNewWave = isNewWave;
+        this.idGenerator = waveIdGenerator;
+        this.channel = waveChannel;
+        this.otherParticipants = waveOtherParticipants;
+        this.unsavedDataListener = unsavedDataListener;
+        this.timer = timer;
+    }
+
+    public void init(Command command) {
+
+        waveData = WaveViewDataImpl.create(waveRef.getWaveId());
+
+        if (isNewWave) {
+
+            
getConversations().createRoot().addParticipantIds(otherParticipants);
+            getConnector().connect(command);
+        } else {
+            getConnector().connect(command);
+        }
+
+        isClosed = false;
+    }
+
+    private ObservableConversationView getConversations() {
+        return conversations == null ? conversations = createConversations() : 
conversations;
+    }
+
+    private Connector getConnector() {
+        return connector == null ? connector = createConnector() : connector;
+    }
+
+    private ObservableConversationView createConversations() {
+        return WaveBasedConversationView.create(getWave(), getIdGenerator());
+    }
+
+    private WaveViewImpl<OpBasedWavelet> getWave() {
+        return wave == null ? wave = createWave() : wave;
+    }
+
+    private IdGenerator getIdGenerator() {
+        return idGenerator;
+    }
+
+    public WaveContext getWaveContext() {
+
+        if (isClosed) {
+            return null;
+        }
+        if (waveContext == null) {
+            waveContext = new WaveContext(getWave(), getConversations(), null, 
null);
+        }
+
+        return waveContext;
+    }
+
+    private WaveViewImpl<OpBasedWavelet> createWave() {
+
+        WaveViewData snapshot = getWaveData();
+
+        final WaveletOperationalizer operationalizer = getWavelets();
+        WaveletFactory<OpBasedWavelet> waveletFactory = new 
WaveletFactory<OpBasedWavelet>() {
+            @Override
+            public OpBasedWavelet create(WaveId waveId, WaveletId id, 
ParticipantId creator) {
+                long now = System.currentTimeMillis();
+                ObservableWaveletData data = new WaveletDataImpl(id, creator, 
now, 0L,
+                        HashedVersion.unsigned(0), now, waveId, 
getDocumentRegistry());
+                return operationalizer.operationalize(data);
+            }
+        };
+        WaveViewImpl<OpBasedWavelet> wave = 
WaveViewImpl.create(waveletFactory, snapshot.getWaveId(),
+                getIdGenerator(), getSignedInUser(), 
WaveletConfigurator.ADD_CREATOR);
+
+        for (ObservableWaveletData waveletData : snapshot.getWavelets()) {
+            wave.addWavelet(operationalizer.operationalize(waveletData));
+        }
+        return wave;
+    }
+
+    private WaveViewData getWaveData() {
+        Preconditions.checkState(waveData != null, "wave not ready");
+        return waveData;
+    }
+
+    private WaveletOperationalizer getWavelets() {
+        return wavelets == null ? wavelets = createWavelets() : wavelets;
+    }
+
+    private WaveletOperationalizer createWavelets() {
+        return WaveletOperationalizer.create(getWaveData().getWaveId(), 
getSignedInUser());
+    }
+
+    private ParticipantId getSignedInUser() {
+        return signedInuser;
+    }
+
+    private WaveDocuments<CcDataDocumentImpl> getDocumentRegistry() {
+        return documentRegistry == null
+                ? documentRegistry = createDocumentRegistry() : 
documentRegistry;
+    }
+
+    private WaveDocuments<CcDataDocumentImpl> createDocumentRegistry() {
+        IndexedDocumentImpl.performValidation = false;
+
+        DocumentFactory<?> dataDocFactory =
+                
ObservablePluggableMutableDocument.createFactory(createSchemas());
+
+        DocumentFactory<CcDataDocumentImpl> fakeBlipDocFactory = new 
DocumentFactory<CcDataDocumentImpl>() {
+
+            @Override
+            public CcDataDocumentImpl create(WaveletId waveletId, String 
docId, DocInitialization content) {
+                return new 
CcDataDocumentImpl(DocumentSchema.NO_SCHEMA_CONSTRAINTS, content);
+            }
+
+        };
+
+        return WaveDocuments.create(fakeBlipDocFactory, dataDocFactory);
+    }
+
+    private SchemaProvider createSchemas() {
+        return new ConversationSchemas();
+    }
+
+    private Connector createConnector() {
+
+        WaveLogger loggerView = new WaveLogger();
+
+        IdURIEncoderDecoder uriCodec = new IdURIEncoderDecoder(new 
ClientPercentEncoderDecoder());
+        HashedVersionFactory hashFactory = new 
HashedVersionZeroFactoryImpl(uriCodec);
+
+        Scheduler scheduler = (Scheduler) new 
FuzzingBackOffScheduler.Builder(getRpcScheduler())
+                
.setInitialBackOffMs(1000).setMaxBackOffMs(60000).setRandomisationFactor(0.5).build();
+
+        ViewChannelFactory viewFactory = 
ViewChannelImpl.factory(createWaveViewService(), loggerView);
+
+        UnsavedDataListenerFactory unsyncedListeners = new 
UnsavedDataListenerFactory() {
+
+            private final UnsavedDataListener listener = unsavedDataListener;
+
+            @Override
+            public UnsavedDataListener create(WaveletId waveletId) {
+                return listener;
+            }
+
+            @Override
+            public void destroy(WaveletId waveletId) {
+
+            }
+        };
+
+        WaveletId udwId = 
getIdGenerator().newUserDataWaveletId(getSignedInUser().getAddress());
+
+        ArrayList<String> prefixes = new ArrayList<String>();
+        prefixes.add(IdConstants.CONVERSATION_WAVELET_PREFIX);
+        prefixes.add(Model.WAVELET_ID_PREFIX);
+
+        final IdFilter filter = IdFilter.of(Collections.singleton(udwId), 
prefixes);
+
+        OperationChannelMultiplexerImpl.LoggerContext loggers = new 
OperationChannelMultiplexerImpl.LoggerContext(loggerView, 
loggerView,loggerView, loggerView);
+
+        WaveletDataImpl.Factory snapshotFactory = 
WaveletDataImpl.Factory.create(getDocumentRegistry());
+        final OperationChannelMultiplexer mux = new 
OperationChannelMultiplexerImpl(getWave()
+                .getWaveId(), viewFactory, snapshotFactory, loggers, 
unsyncedListeners, (org.waveprotocol.wave.model.util.Scheduler) scheduler,
+                hashFactory);
+
+        final WaveViewImpl<OpBasedWavelet> wave = getWave();
+
+
+        return new Connector() {
+            @Override
+            public void connect(Command onOpened) {
+                LiveChannelBinder.openAndBind(getWavelets(), wave, 
getDocumentRegistry(), mux, filter,
+                        onOpened);
+            }
+
+            @Override
+            public void close() {
+                mux.close();
+            }
+        };
+    }
+
+    private CollectiveScheduler getRpcScheduler() {
+        return rpcScheduler == null ? rpcScheduler = createRpcScheduler() : 
rpcScheduler;
+    }
+
+    protected WaveViewService createWaveViewService() {
+        return new RemoteWaveViewService(waveRef.getWaveId(), channel, 
getDocumentRegistry());
+    }
+
+    protected CollectiveScheduler createRpcScheduler() {
+        return new 
OptimalGroupingScheduler(SchedulerInstance.getLowPriorityTimer());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveService.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveService.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveService.java
new file mode 100644
index 0000000..482e2b2
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveService.java
@@ -0,0 +1,191 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package app.android.box.waveprotocol.org.androidwave.service;
+
+import android.os.AsyncTask;
+
+import org.waveprotocol.wave.model.document.WaveContext;
+import org.waveprotocol.wave.model.id.IdGenerator;
+import org.waveprotocol.wave.model.id.IdGeneratorImpl;
+import org.waveprotocol.wave.model.id.WaveId;
+import org.waveprotocol.wave.model.wave.ParticipantId;
+import org.waveprotocol.wave.model.waveref.WaveRef;
+import org.waveprotocol.wave.model.util.Pair;
+
+import java.util.Collections;
+import java.util.Timer;
+import java.util.Map;
+
+import 
app.android.box.waveprotocol.org.androidwave.service.concurrencycontrol.Connector.Command;
+import app.android.box.waveprotocol.org.androidwave.service.models.Model;
+import 
app.android.box.waveprotocol.org.androidwave.service.models.TypeIdGenerator;
+
+public class WaveService {
+
+    private String waveHost;
+    private String waveSessionId;
+    private String waveUsername;
+
+    private Timer timer;
+
+    private ParticipantId waveParticipantId;
+    private IdGenerator waveIdGenerator;
+    private TypeIdGenerator waveTypeIdGenerator;
+
+    private WaveWebSocketClient waveWebSocketClient;
+    private RemoteViewServiceMultiplexer waveChannel;
+
+    private Map<WaveRef, Pair<WaveRender, Model>> waveStore;
+
+
+
+    public boolean waveSignUpTask(String host, String username, String 
password){
+        WaveSignUp waveSignUpService = new WaveSignUp();
+        return waveSignUpService.waveSignUp(host,username,password);
+    }
+
+    public void waveLoginTask(String host, String username, String password){
+        new WaveSession().execute(host,username,password);
+    }
+
+    public String getWaveSessionId() {
+        return waveSessionId;
+    }
+
+    public void setWaveSessionId(String waveSessionId) {
+        this.waveSessionId = waveSessionId;
+    }
+
+    public String getWaveHost() {
+        return waveHost;
+    }
+
+    public void setWaveHost(String waveHost) {
+        this.waveHost = waveHost;
+    }
+
+    public String getWaveUsername() {
+        return waveUsername;
+    }
+
+    public void setWaveUsername(String waveUsername) {
+        this.waveUsername = waveUsername;
+    }
+
+    public boolean isWaveSessionStarted() {
+        return waveSessionId != null;
+    }
+
+    private void openWebSocketConnection(String hostName, String SessionId){
+
+        String waveWebSocketUrl = "http://"+ hostName +"/atmosphere";
+
+        waveIdGenerator = new IdGeneratorImpl(waveWebSocketUrl, new 
IdGeneratorImpl.Seed() {
+            @Override
+            public String get() {
+                return waveSessionId.substring(0, 5);
+            }
+        });
+
+        waveTypeIdGenerator = TypeIdGenerator.get(waveIdGenerator);
+
+        waveWebSocketClient = new WaveWebSocketClient(waveWebSocketUrl, 
waveSessionId);
+
+        waveWebSocketClient.connect(new 
WaveWebSocketClient.ConnectionListener() {
+            @Override
+            public void onConnect() {
+
+            }
+
+            @Override
+            public void onReconnect() {
+
+            }
+
+            @Override
+            public void onDisconnect() {
+
+            }
+        });
+
+        waveChannel = new RemoteViewServiceMultiplexer(waveWebSocketClient, 
waveParticipantId.getAddress());
+
+    }
+
+    private void closeWebSocket() {
+
+        waveIdGenerator = null;
+        waveChannel = null;
+        waveWebSocketClient = null;
+    }
+
+    public String createModel() {
+
+        WaveId newWaveId = waveTypeIdGenerator.newWaveId();
+        final WaveRef waveRef = WaveRef.of(newWaveId);
+
+        final WaveRender waveRender = new WaveRender(true, waveRef, 
waveChannel, waveParticipantId,
+                Collections.<ParticipantId>emptySet(), waveIdGenerator, null, 
timer);
+
+        waveRender.init(new Command() {
+
+            @Override
+            public void execute() {
+
+                WaveContext wave = waveRender.getWaveContext();
+                Model model = Model.create(wave, waveHost, waveParticipantId, 
true, waveIdGenerator);
+
+                waveStore.put(waveRef, new Pair<WaveRender, Model>(waveRender, 
model));
+            }
+        });
+
+        return waveRef.getWaveId().serialise();
+    }
+
+    public void openModel(String modelId) {
+
+    }
+
+    public Model getModel(String modelId) {
+
+        return null;
+    }
+
+    public void closeModel(String modelId) {
+
+    }
+
+    public class WaveSession extends AsyncTask<String, Void, String> {
+
+        @Override
+        protected String doInBackground(String... params) {
+            WaveSignIn waveSignIn = new WaveSignIn();
+            waveSessionId = waveSignIn.waveSignIn(params[0], params[1], 
params[2]);
+            return waveSessionId;
+        }
+
+        @Override
+        protected void onPostExecute(String result) {
+            if (result != null) {
+                openWebSocketConnection(waveHost, waveSessionId);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveSignIn.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveSignIn.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveSignIn.java
new file mode 100644
index 0000000..59aa211
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveSignIn.java
@@ -0,0 +1,112 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package app.android.box.waveprotocol.org.androidwave.service;
+
+import android.util.Log;
+
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.List;
+
+import app.android.box.waveprotocol.org.androidwave.util.Util;
+
+public class WaveSignIn {
+
+    private static final String CHARSET = "utf-8";
+    private static String WAVE_SESSION_COOKIE = "WSESSIONID";
+
+    /**
+     * This method get Wave server name, Wave user's username and Wave user's 
password as input parameters
+     * and it will invoke AuthenticationServlet in the Wave server. If sign in 
get success the method
+     * will return session id
+     *
+     * @param host     Apache Wave hostname
+     * @param username Apache Wave user's username
+     * @param password Apache Wave user's password
+     * @return Apache Wave user's sessionId
+     */
+    public String waveSignIn(String host, String username, String password) {
+
+        String sessionId = null;
+
+        String servlet = "auth/signin?r=none";
+        String hostURL = Util.hostCreator(host, servlet);
+        String httpQuery = "";
+        HttpURLConnection connection = null;
+
+        try {
+            httpQuery = "address=" + URLEncoder.encode(username, "UTF-8") + 
"&password="
+                    + URLEncoder.encode(password, CHARSET) + "&signIn="
+                    + URLEncoder.encode("Sign+in", CHARSET);
+
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+
+        try {
+
+            URL url = new URL(hostURL);
+            connection = (HttpURLConnection) url.openConnection();
+
+            connection.setDoOutput(true);
+            connection.setRequestProperty("Accept-Charset", CHARSET);
+            connection.setRequestProperty("Content-Type", 
"application/x-www-form-urlencoded;charset="
+                    + CHARSET);
+
+            OutputStream out = connection.getOutputStream();
+            out.write(httpQuery.getBytes(CHARSET));
+
+
+            if (connection.getResponseCode() == 200) {
+
+                List<String> cookies = 
connection.getHeaderFields().get("Set-Cookie");
+
+                for (String c : cookies) {
+                    if (c.startsWith(WAVE_SESSION_COOKIE)) {
+
+                        String cookie = c;
+
+                        if (cookie.contains(";"))
+                            cookie = cookie.split(";")[0];
+
+                        sessionId = cookie.split("=")[1];
+                        break;
+                    }
+                }
+
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            return sessionId;
+        } finally {
+
+            if(connection != null){
+                connection.disconnect();
+            }
+        }
+
+        return sessionId;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveSignUp.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveSignUp.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveSignUp.java
new file mode 100644
index 0000000..d987e41
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveSignUp.java
@@ -0,0 +1,66 @@
+package app.android.box.waveprotocol.org.androidwave.service;
+
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.widget.Toast;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+
+import app.android.box.waveprotocol.org.androidwave.util.Util;
+
+public class WaveSignUp {
+
+    private static final String CHARSET = "utf-8";
+
+    /**
+     * This method get Wave server name, Wave user's username and Wave user's 
password as input parameters
+     * and it will invoke UserRegistrationServlet in the Wave server. If sign 
up get success the method
+     * will return true if not it return false
+     *
+     * @param host     Apache Wave hostname
+     * @param username Apache Wave user's username
+     * @param password Apache Wave user's password
+     * @return True or false
+     */
+
+    public boolean waveSignUp(String host, String username, String password) {
+
+        String servlet = "auth/register";
+        String hostURL = Util.hostCreator(host, servlet);
+        String httpQuery = "";
+        HttpURLConnection connection = null;
+
+        try {
+            httpQuery = "address=" + URLEncoder.encode(username, CHARSET) + 
"&password="
+                    + URLEncoder.encode(password, CHARSET);
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            URL url = new URL(hostURL);
+            connection = (HttpURLConnection) url.openConnection();
+            connection.setDoOutput(true);
+            connection.setRequestProperty("Accept-Charset", CHARSET);
+            connection.setRequestProperty("Content-Type", 
"application/x-www-form-urlencoded;charset="
+                    + CHARSET);
+
+            OutputStream out = connection.getOutputStream();
+            out.write(httpQuery.getBytes(CHARSET));
+
+            return connection.getResponseCode() == 200;
+
+        } catch (IOException e) {
+            e.printStackTrace();
+            return false;
+        } finally {
+            connection.disconnect();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveWebSocket.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveWebSocket.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveWebSocket.java
new file mode 100644
index 0000000..77c6be8
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveWebSocket.java
@@ -0,0 +1,14 @@
+package app.android.box.waveprotocol.org.androidwave.service;
+
+public interface WaveWebSocket {
+
+    interface WaveSocketCallback {
+        void onConnect();
+        void onDisconnect();
+        void onMessage(String message);
+    }
+
+    void connect();
+    void disconnect();
+    void sendMessage(String message);
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveWebSocketCallback.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveWebSocketCallback.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveWebSocketCallback.java
new file mode 100644
index 0000000..65353f0
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveWebSocketCallback.java
@@ -0,0 +1,7 @@
+package app.android.box.waveprotocol.org.androidwave.service;
+
+import org.waveprotocol.box.common.comms.ProtocolWaveletUpdate;
+
+public interface WaveWebSocketCallback {
+    void onWaveletUpdate(ProtocolWaveletUpdate message);
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveWebSocketClient.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveWebSocketClient.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveWebSocketClient.java
new file mode 100644
index 0000000..a992ba7
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/WaveWebSocketClient.java
@@ -0,0 +1,232 @@
+package app.android.box.waveprotocol.org.androidwave.service;
+
+import android.util.Log;
+
+import com.google.common.base.Preconditions;
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonPrimitive;
+
+import org.waveprotocol.box.common.comms.gson.ProtocolAuthenticateGsonImpl;
+import org.waveprotocol.box.common.comms.gson.ProtocolOpenRequestGsonImpl;
+import org.waveprotocol.box.common.comms.gson.ProtocolSubmitRequestGsonImpl;
+import org.waveprotocol.box.common.comms.gson.ProtocolSubmitResponseGsonImpl;
+import org.waveprotocol.box.common.comms.gson.ProtocolWaveletUpdateGsonImpl;
+import org.waveprotocol.wave.communication.gson.GsonException;
+import org.waveprotocol.wave.model.util.CollectionUtils;
+import org.waveprotocol.wave.model.util.IntMap;
+
+import java.util.Queue;
+
+public class WaveWebSocketClient implements WaveWebSocket.WaveSocketCallback {
+
+    private static final String TAG = "WaveWebSocketClient";
+
+    public interface ConnectionListener {
+
+        public void onConnect();
+
+        public void onReconnect();
+
+        public void onDisconnect();
+
+    }
+
+    private static final int MAX_INITIAL_FAILURES = 2;
+
+    private static final int RECONNECT_TIME_MS = 5000;
+
+    private static final String JETTY_SESSION_TOKEN_NAME = "WSESSIONID";
+
+    private static class MessageWrapper {
+        private final static JsonParser parser = new JsonParser();
+
+        final int sequenceNumber;
+        final String messageType;
+        final JsonElement message;
+
+        public MessageWrapper(int sequenceNumber, String messageType, 
JsonElement message) {
+            this.sequenceNumber = sequenceNumber;
+            this.messageType = messageType;
+            this.message = message;
+        }
+
+        public static MessageWrapper deserialize(Gson gson, String data) {
+            JsonElement e = parser.parse(data);
+            JsonObject obj = e.getAsJsonObject();
+            String type = obj.get("messageType").getAsString();
+            int seqNo = obj.get("sequenceNumber").getAsInt();
+            JsonElement message = obj.get("message");
+            return new MessageWrapper(seqNo, type, message);
+        }
+
+        public static String serialize(String type, int seqno, JsonElement 
message) {
+            JsonObject o = new JsonObject();
+            o.add("messageType", new JsonPrimitive(type));
+            o.add("sequenceNumber", new JsonPrimitive(seqno));
+            o.add("message", message);
+            return o.toString();
+        }
+    }
+
+    private WaveWebSocket socket = null;
+    private final IntMap<SubmitResponseCallback> submitRequestCallbacks;
+
+    private enum ConnectState {
+        CONNECTED, CONNECTING, DISCONNECTED
+    }
+
+    private ConnectState connected = ConnectState.DISCONNECTED;
+    private WaveWebSocketCallback callback;
+    private int sequenceNo;
+
+    private final Queue<String> messages = CollectionUtils.createQueue();
+
+
+    private boolean connectedAtLeastOnce = false;
+    private long connectTry = 0;
+    private final String urlBase;
+    private final String httpSessionId;
+
+    private final Gson gson = new Gson();
+
+    private ConnectionListener connectionListener = null;
+
+    public WaveWebSocketClient(String urlBase, String httpSessionId) {
+        this.httpSessionId = httpSessionId;
+        this.urlBase = urlBase;
+
+        submitRequestCallbacks = CollectionUtils.createIntMap();
+        //socket = WaveSocketFactory.create(false, urlBase, httpSessionId, 
this);
+    }
+
+    public void attachHandler(WaveWebSocketCallback callback) {
+        Preconditions.checkState(this.callback == null);
+        Preconditions.checkArgument(callback != null);
+        this.callback = callback;
+    }
+
+    public void connect(ConnectionListener listener) {
+
+        connectionListener = listener;
+
+        if (socket == null) {
+           // socket = WaveSocketFactory.create(true, urlBase, httpSessionId, 
WaveWebSocketClient.this);
+        }
+
+        connectTry++;
+        if (connected == ConnectState.DISCONNECTED) {
+            Log.i(TAG, "Attemping to reconnect");
+            connected = ConnectState.CONNECTING;
+            socket.connect();
+        }
+
+    }
+
+    @Override
+    public void onConnect() {
+
+        connected = ConnectState.CONNECTED;
+
+        if (httpSessionId != null && !connectedAtLeastOnce) {
+            ProtocolAuthenticateGsonImpl auth = new 
ProtocolAuthenticateGsonImpl();
+            auth.setToken(httpSessionId);
+            sendMessage(sequenceNo++, "ProtocolAuthenticate", 
auth.toGson(null, null));
+        }
+
+        while (!messages.isEmpty() && connected == ConnectState.CONNECTED) {
+            send(messages.poll());
+        }
+
+        if (connectionListener != null)
+            if (!connectedAtLeastOnce)
+                connectionListener.onConnect();
+            else
+                connectionListener.onReconnect();
+
+        connectedAtLeastOnce = true;
+
+    }
+
+    @Override
+    public void onDisconnect() {
+        connected = ConnectState.DISCONNECTED;
+
+        if (connectionListener != null)
+            connectionListener.onDisconnect();
+    }
+
+    @Override
+    public void onMessage(final String message) {
+        Log.i(TAG, "Received JSON message " + message);
+        MessageWrapper wrapper;
+        wrapper = MessageWrapper.deserialize(gson, message);
+
+        String messageType = wrapper.messageType;
+        if ("ProtocolWaveletUpdate".equals(messageType)) {
+            if (callback != null) {
+                ProtocolWaveletUpdateGsonImpl waveletUpdate = new 
ProtocolWaveletUpdateGsonImpl();
+
+                try {
+                    waveletUpdate.fromGson(wrapper.message, gson, null);
+                } catch (GsonException e) {
+                    Log.i(TAG, "Error parsing WaveletUpdate JSON message", e);
+                    return;
+                }
+                callback.onWaveletUpdate(waveletUpdate);
+            }
+        } else if ("ProtocolSubmitResponse".equals(messageType)) {
+            int seqno = wrapper.sequenceNumber;
+            SubmitResponseCallback callback = 
submitRequestCallbacks.get(seqno);
+            if (callback != null) {
+                submitRequestCallbacks.remove(seqno);
+                ProtocolSubmitResponseGsonImpl submitResponse = new 
ProtocolSubmitResponseGsonImpl();
+                try {
+                    submitResponse.fromGson(wrapper.message, gson, null);
+                } catch (GsonException e) {
+                    Log.e(TAG, "Error parsing SubmitResponse JSON message", e);
+                    return;
+                }
+                callback.run(submitResponse);
+            }
+        }
+    }
+
+    public void submit(ProtocolSubmitRequestGsonImpl message, 
SubmitResponseCallback callback) {
+        int submitId = sequenceNo++;
+        submitRequestCallbacks.put(submitId, callback);
+        sendMessage(submitId, "ProtocolSubmitRequest", message.toGson(null, 
null));
+    }
+
+    public void open(ProtocolOpenRequestGsonImpl message) {
+        sendMessage(sequenceNo++, "ProtocolOpenRequest", message.toGson(null, 
null));
+    }
+
+
+    private void sendMessage(int sequenceNo, String type, JsonElement message) 
{
+
+        String json = "";
+        try {
+            json = MessageWrapper.serialize(type, sequenceNo, message);
+        } catch (Exception e) {
+            Log.e(TAG, "Error serializing message ", e);
+        } finally {
+
+        }
+        switch (connected) {
+            case CONNECTED:
+                send(json);
+                break;
+            default:
+                messages.add(json);
+        }
+    }
+
+    private void send(String json) {
+        Log.i(TAG, "Sending JSON data " + json);
+        socket.sendMessage(json);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/Connector.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/Connector.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/Connector.java
new file mode 100644
index 0000000..bcd4a33
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/Connector.java
@@ -0,0 +1,14 @@
+package 
app.android.box.waveprotocol.org.androidwave.service.concurrencycontrol;
+
+public interface Connector {
+
+    public interface Command {
+
+        public void execute();
+
+    }
+
+    void connect(Command onOpened);
+
+    void close();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/LiveChannelBinder.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/LiveChannelBinder.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/LiveChannelBinder.java
new file mode 100644
index 0000000..3aed86e
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/LiveChannelBinder.java
@@ -0,0 +1,101 @@
+package 
app.android.box.waveprotocol.org.androidwave.service.concurrencycontrol;
+
+
+import org.waveprotocol.wave.concurrencycontrol.channel.Accessibility;
+import org.waveprotocol.wave.concurrencycontrol.channel.OperationChannel;
+import 
org.waveprotocol.wave.concurrencycontrol.channel.OperationChannelMultiplexer;
+import org.waveprotocol.wave.concurrencycontrol.common.CorruptionDetail;
+import org.waveprotocol.wave.concurrencycontrol.wave.CcDocument;
+import org.waveprotocol.wave.model.id.IdFilter;
+import org.waveprotocol.wave.model.id.WaveletId;
+import org.waveprotocol.wave.model.util.CollectionUtils;
+import org.waveprotocol.wave.model.wave.ObservableWavelet;
+import org.waveprotocol.wave.model.wave.WaveViewListener;
+import org.waveprotocol.wave.model.wave.data.ObservableWaveletData;
+import org.waveprotocol.wave.model.wave.opbased.OpBasedWavelet;
+import org.waveprotocol.wave.model.wave.opbased.WaveViewImpl;
+
+import java.util.Collection;
+
+import 
app.android.box.waveprotocol.org.androidwave.service.documents.WaveDocuments;
+
+public final class LiveChannelBinder
+        implements WaveViewListener, OperationChannelMultiplexer.Listener {
+
+    private final StaticChannelBinder binder;
+    private final WaveletOperationalizer operationalizer;
+    private final WaveViewImpl<OpBasedWavelet> wave;
+    private final OperationChannelMultiplexer mux;
+    private final Connector.Command whenOpened;
+
+    private LiveChannelBinder(StaticChannelBinder binder, 
WaveletOperationalizer operationalizer,
+                              WaveViewImpl<OpBasedWavelet> wave, 
OperationChannelMultiplexer mux, Connector.Command whenOpened) {
+        this.binder = binder;
+        this.operationalizer = operationalizer;
+        this.wave = wave;
+        this.mux = mux;
+        this.whenOpened = whenOpened;
+    }
+
+    public static void openAndBind(WaveletOperationalizer operationalizer,
+                                   WaveViewImpl<OpBasedWavelet> wave,
+                                   WaveDocuments<? extends CcDocument> 
docRegistry,
+                                   OperationChannelMultiplexer mux,
+                                   IdFilter filter,
+                                   Connector.Command whenOpened) {
+
+        StaticChannelBinder staticBinder = new 
StaticChannelBinder(operationalizer, docRegistry);
+
+        LiveChannelBinder liveBinder =
+                new LiveChannelBinder(staticBinder, operationalizer, wave, 
mux, whenOpened);
+
+        final Collection<OperationChannelMultiplexer.KnownWavelet> 
remoteWavelets = CollectionUtils.createQueue();
+        final Collection<ObservableWaveletData> localWavelets = 
CollectionUtils.createQueue();
+        for (ObservableWaveletData wavelet : operationalizer.getWavelets()) {
+
+            if (wavelet.getVersion() > 0) {
+                remoteWavelets.add(
+                        new OperationChannelMultiplexer.KnownWavelet(wavelet, 
wavelet.getHashedVersion(), Accessibility.READ_WRITE));
+            } else {
+                localWavelets.add(wavelet);
+            }
+        }
+
+        wave.addListener(liveBinder);
+
+        mux.open(liveBinder, filter, remoteWavelets);
+        for (ObservableWaveletData local : localWavelets) {
+            mux.createOperationChannel(local.getWaveletId(), 
local.getCreator());
+        }
+    }
+
+    @Override
+    public void onOperationChannelCreated(OperationChannel operationChannel, 
ObservableWaveletData observableWaveletData, Accessibility accessibility) {
+
+    }
+
+    @Override
+    public void onOperationChannelRemoved(OperationChannel operationChannel, 
WaveletId waveletId) {
+
+    }
+
+    @Override
+    public void onOpenFinished() {
+
+    }
+
+    @Override
+    public void onFailed(CorruptionDetail corruptionDetail) {
+
+    }
+
+    @Override
+    public void onWaveletAdded(ObservableWavelet observableWavelet) {
+
+    }
+
+    @Override
+    public void onWaveletRemoved(ObservableWavelet observableWavelet) {
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/LiveTarget.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/LiveTarget.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/LiveTarget.java
new file mode 100644
index 0000000..b312f03
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/LiveTarget.java
@@ -0,0 +1,50 @@
+package 
app.android.box.waveprotocol.org.androidwave.service.concurrencycontrol;
+
+import org.waveprotocol.wave.model.operation.Operation;
+import org.waveprotocol.wave.model.operation.OperationException;
+import org.waveprotocol.wave.model.operation.OperationRuntimeException;
+import org.waveprotocol.wave.model.operation.SilentOperationSink;
+
+public final class LiveTarget<T, O extends Operation<? super T>> {
+
+    private final T target;
+
+    private final SilentOperationSink<O> executor;
+
+    private final ProxyOperationSink<O> output;
+
+    private LiveTarget(T target, SilentOperationSink<O> executor, 
ProxyOperationSink<O> output) {
+        this.target = target;
+        this.executor = executor;
+        this.output = output;
+    }
+
+    public static <T, O extends Operation<? super T>> LiveTarget<T, O> 
create(final T data) {
+        ProxyOperationSink<O> output = ProxyOperationSink.create();
+        SilentOperationSink<O> executor = new SilentOperationSink<O>() {
+            @Override
+            public void consume(O operation) {
+                try {
+                    operation.apply(data);
+                } catch (OperationException e) {
+                    // Fail this object permanently
+                    throw new OperationRuntimeException("Error applying op", 
e);
+                }
+            }
+        };
+        return new LiveTarget<T, O>(data, executor, output);
+    }
+
+    public T getTarget() {
+        return target;
+    }
+
+    public SilentOperationSink<O> getExecutorSink() {
+        return executor;
+    }
+
+    public ProxyOperationSink<O> getOutputSink() {
+        return output;
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/ProxyOperationSink.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/ProxyOperationSink.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/ProxyOperationSink.java
new file mode 100644
index 0000000..4298013
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/ProxyOperationSink.java
@@ -0,0 +1,46 @@
+package 
app.android.box.waveprotocol.org.androidwave.service.concurrencycontrol;
+
+import com.google.common.base.Preconditions;
+
+import org.waveprotocol.wave.model.operation.Operation;
+import org.waveprotocol.wave.model.operation.SilentOperationSink;
+import org.waveprotocol.wave.model.util.CollectionUtils;
+
+import java.util.Queue;
+
+public final class ProxyOperationSink<O extends Operation<?>> implements 
SilentOperationSink<O> {
+
+    private Queue<O> queue;
+    private SilentOperationSink<O> target;
+
+    private ProxyOperationSink() {
+    }
+
+    public static <O extends Operation<?>> ProxyOperationSink<O> create() {
+        return new ProxyOperationSink<O>();
+    }
+
+    public void setTarget(SilentOperationSink<O> target) {
+        Preconditions.checkState(this.target == null);
+        this.target = target;
+
+        if (queue != null) {
+            while (!queue.isEmpty()) {
+                target.consume(queue.poll());
+            }
+            queue = null;
+        }
+    }
+
+    @Override
+    public void consume(O op) {
+        if (target != null) {
+            target.consume(op);
+        } else {
+            if (queue == null) {
+                queue = CollectionUtils.createQueue();
+            }
+            queue.add(op);
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/StaticChannelBinder.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/StaticChannelBinder.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/StaticChannelBinder.java
new file mode 100644
index 0000000..6b9d6bc
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/StaticChannelBinder.java
@@ -0,0 +1,17 @@
+package 
app.android.box.waveprotocol.org.androidwave.service.concurrencycontrol;
+
+import org.waveprotocol.wave.concurrencycontrol.wave.CcDocument;
+
+import 
app.android.box.waveprotocol.org.androidwave.service.documents.WaveDocuments;
+
+public final class StaticChannelBinder {
+
+    private final WaveletOperationalizer operationalizer;
+    private final WaveDocuments<? extends CcDocument> docRegistry;
+
+    public StaticChannelBinder(
+            WaveletOperationalizer operationalizer, WaveDocuments<? extends 
CcDocument> docRegistry) {
+        this.operationalizer = operationalizer;
+        this.docRegistry = docRegistry;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/WaveletOperationalizer.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/WaveletOperationalizer.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/WaveletOperationalizer.java
new file mode 100644
index 0000000..ed03a10
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/concurrencycontrol/WaveletOperationalizer.java
@@ -0,0 +1,71 @@
+package 
app.android.box.waveprotocol.org.androidwave.service.concurrencycontrol;
+
+import com.google.common.base.Preconditions;
+
+import org.waveprotocol.wave.model.id.ModernIdSerialiser;
+import org.waveprotocol.wave.model.id.WaveId;
+import org.waveprotocol.wave.model.operation.wave.WaveletOperation;
+import org.waveprotocol.wave.model.operation.wave.WaveletOperationContext;
+import 
org.waveprotocol.wave.model.operation.wave.BasicWaveletOperationContextFactory;
+import 
org.waveprotocol.wave.model.operation.wave.WaveletOperationContext.Factory;
+import org.waveprotocol.wave.model.util.CollectionUtils;
+import org.waveprotocol.wave.model.util.ReadableStringMap;
+import org.waveprotocol.wave.model.util.StringMap;
+import org.waveprotocol.wave.model.wave.ParticipantId;
+import org.waveprotocol.wave.model.wave.ParticipationHelper;
+import org.waveprotocol.wave.model.wave.data.ObservableWaveletData;
+import org.waveprotocol.wave.model.wave.opbased.OpBasedWavelet;
+
+import java.util.Collection;
+
+public class WaveletOperationalizer {
+
+    private final WaveId waveId;
+    private final StringMap<LiveTarget<ObservableWaveletData, 
WaveletOperation>> wavelets =
+            CollectionUtils.createStringMap();
+    private final WaveletOperationContext.Factory opContextFactory;
+
+    private WaveletOperationalizer(WaveId waveId, Factory opContextFactory) {
+        this.waveId = waveId;
+        this.opContextFactory = opContextFactory;
+    }
+
+    public static WaveletOperationalizer create(WaveId wave, ParticipantId 
user) {
+        WaveletOperationContext.Factory opContexts = new 
BasicWaveletOperationContextFactory(user);
+        return new WaveletOperationalizer(wave, opContexts);
+    }
+
+    public OpBasedWavelet operationalize(ObservableWaveletData data) {
+        LiveTarget<ObservableWaveletData, WaveletOperation> target = 
createSinks(data);
+        return new OpBasedWavelet(waveId,
+                data,
+                opContextFactory,
+                ParticipationHelper.DEFAULT,
+                target.getExecutorSink(),
+                target.getOutputSink());
+    }
+
+    private LiveTarget<ObservableWaveletData, WaveletOperation> createSinks(
+            ObservableWaveletData data) {
+        return putAndReturn(wavelets,
+                
ModernIdSerialiser.INSTANCE.serialiseWaveletId(data.getWaveletId()),
+                LiveTarget.<ObservableWaveletData, 
WaveletOperation>create(data));
+    }
+
+    private static <V> V putAndReturn(StringMap<V> map, String key, V value) {
+        Preconditions.checkState(!map.containsKey(key));
+        map.put(key, value);
+        return value;
+    }
+
+    public Collection<ObservableWaveletData> getWavelets() {
+        final Collection<ObservableWaveletData> targets = 
CollectionUtils.createQueue();
+        this.wavelets.each(new 
ReadableStringMap.ProcV<LiveTarget<ObservableWaveletData, WaveletOperation>>() {
+            @Override
+            public void apply(String id, LiveTarget<ObservableWaveletData, 
WaveletOperation> triple) {
+                targets.add(triple.getTarget());
+            }
+        });
+        return targets;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/documents/DocumentRegistry.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/documents/DocumentRegistry.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/documents/DocumentRegistry.java
new file mode 100644
index 0000000..08e9893
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/documents/DocumentRegistry.java
@@ -0,0 +1,8 @@
+package app.android.box.waveprotocol.org.androidwave.service.documents;
+
+import org.waveprotocol.wave.model.conversation.ConversationBlip;
+
+public interface DocumentRegistry<D> {
+    D get(ConversationBlip blip);
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/documents/WaveDocuments.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/documents/WaveDocuments.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/documents/WaveDocuments.java
new file mode 100644
index 0000000..114776b
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/documents/WaveDocuments.java
@@ -0,0 +1,65 @@
+package app.android.box.waveprotocol.org.androidwave.service.documents;
+
+import com.google.common.base.Preconditions;
+
+import org.waveprotocol.wave.model.conversation.ConversationBlip;
+import org.waveprotocol.wave.model.document.operation.DocInitialization;
+import org.waveprotocol.wave.model.id.IdUtil;
+import org.waveprotocol.wave.model.id.ModernIdSerialiser;
+import org.waveprotocol.wave.model.id.WaveletId;
+import org.waveprotocol.wave.model.util.CollectionUtils;
+import org.waveprotocol.wave.model.util.StringMap;
+import org.waveprotocol.wave.model.wave.data.DocumentFactory;
+import org.waveprotocol.wave.model.wave.data.DocumentOperationSink;
+
+public final class WaveDocuments<BlipDocument extends DocumentOperationSink>
+        implements DocumentFactory<DocumentOperationSink>, 
DocumentRegistry<BlipDocument> {
+
+    private final DocumentFactory<BlipDocument> blipDocFactory;
+    private final DocumentFactory<?> dataDocFactory;
+    private final StringMap<StringMap<BlipDocument>> blips = 
CollectionUtils.createStringMap();
+
+    private WaveDocuments(DocumentFactory<BlipDocument> blip, 
DocumentFactory<?> data) {
+        this.blipDocFactory = blip;
+        this.dataDocFactory = data;
+    }
+
+    public static <B extends DocumentOperationSink> WaveDocuments<B> create(
+            DocumentFactory<B> blipDocFactory, DocumentFactory<?> 
dataDocFactory) {
+        return new WaveDocuments<B>(blipDocFactory, dataDocFactory);
+    }
+
+    @Override
+    public DocumentOperationSink create(
+            final WaveletId waveletId, final String blipId, final 
DocInitialization content) {
+
+        String waveletIdStr = 
ModernIdSerialiser.INSTANCE.serialiseWaveletId(waveletId);
+        if (IdUtil.isBlipId(blipId)) {
+            BlipDocument document = blipDocFactory.create(waveletId, blipId, 
content);
+            StringMap<BlipDocument> convDocuments = 
getConversationDocuments(waveletIdStr);
+            Preconditions.checkState(!convDocuments.containsKey(blipId));
+            convDocuments.put(blipId, document);
+            return document;
+        } else {
+            return dataDocFactory.create(waveletId, blipId, content);
+        }
+    }
+
+    private StringMap<BlipDocument> getConversationDocuments(String id) {
+        StringMap<BlipDocument> convDocuments = blips.get(id);
+        if (convDocuments == null) {
+            convDocuments = CollectionUtils.createStringMap();
+            blips.put(id, convDocuments);
+        }
+        return convDocuments;
+    }
+
+    public BlipDocument get(ConversationBlip blip) {
+        return getBlipDocument(blip.getConversation().getId(), blip.getId());
+    }
+
+    public BlipDocument getBlipDocument(String waveletId, String docId) {
+        StringMap<BlipDocument> convDocuments = blips.get(waveletId);
+        return convDocuments != null ? convDocuments.get(docId) : null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/logger/WaveLogger.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/logger/WaveLogger.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/logger/WaveLogger.java
new file mode 100644
index 0000000..5996203
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/logger/WaveLogger.java
@@ -0,0 +1,33 @@
+package app.android.box.waveprotocol.org.androidwave.service.logger;
+
+import org.waveprotocol.wave.common.logging.AbstractLogger;
+import org.waveprotocol.wave.common.logging.Logger;
+import org.waveprotocol.wave.common.logging.LoggerBundle;
+
+public class WaveLogger implements LoggerBundle {
+
+    @Override
+    public void log(AbstractLogger.Level level, Object... objects) {
+
+    }
+
+    @Override
+    public Logger trace() {
+        return null;
+    }
+
+    @Override
+    public Logger error() {
+        return null;
+    }
+
+    @Override
+    public Logger fatal() {
+        return null;
+    }
+
+    @Override
+    public boolean isModuleEnabled() {
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/models/IdGeneratorGeneric.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/models/IdGeneratorGeneric.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/models/IdGeneratorGeneric.java
new file mode 100644
index 0000000..b6fa2d8
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/models/IdGeneratorGeneric.java
@@ -0,0 +1,12 @@
+package app.android.box.waveprotocol.org.androidwave.service.models;
+
+import org.waveprotocol.wave.model.id.IdGenerator;
+import org.waveprotocol.wave.model.id.WaveId;
+
+public interface IdGeneratorGeneric {
+
+    IdGeneratorGeneric initialize(IdGenerator idGenerator);
+
+    WaveId newWaveId();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/models/ListElementFactory.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/models/ListElementFactory.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/models/ListElementFactory.java
new file mode 100644
index 0000000..37db889
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/models/ListElementFactory.java
@@ -0,0 +1,55 @@
+package app.android.box.waveprotocol.org.androidwave.service.models;
+
+import org.waveprotocol.wave.model.document.Doc.E;
+import org.waveprotocol.wave.model.document.util.DocumentEventRouter;
+import org.waveprotocol.wave.model.util.Preconditions;
+
+import java.util.Map;
+
+public class ListElementFactory implements
+        org.waveprotocol.wave.model.adt.docbased.Factory<E, Type, 
ListElementInitializer> {
+
+
+    private Model model;
+
+    protected ListElementFactory(Model model) {
+        this.model = model;
+    }
+
+    @Override
+    public Type adapt(DocumentEventRouter<? super E, E, ?> router, E element) {
+
+        Map<String, String> attributes = 
router.getDocument().getAttributes(element);
+        Preconditions.checkArgument(attributes != null,
+                "Adapting a list element to Type but attributes not found");
+
+        String type = attributes.get("t");
+        Preconditions.checkArgument(type != null,
+                "Adapting a list element to Type but attribute for type not 
found");
+
+        String value = attributes.get("r");
+        Preconditions.checkArgument(value != null,
+                "Adapting a list element to Type but attribute for reference 
not found");
+
+        return Type.createInstance(type, value, model);
+
+    }
+
+    @Override
+    public org.waveprotocol.wave.model.adt.docbased.Initializer 
createInitializer(
+            final ListElementInitializer initialState) {
+
+        return new org.waveprotocol.wave.model.adt.docbased.Initializer() {
+
+            @Override
+            public void initialize(Map<String, String> target) {
+                target.put("t", initialState.getType());
+                if (initialState.getBackendId() != null) {
+                    target.put("r", initialState.getBackendId());
+                }
+            }
+
+        };
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/models/ListElementInitializer.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/models/ListElementInitializer.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/models/ListElementInitializer.java
new file mode 100644
index 0000000..50c9885
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/models/ListElementInitializer.java
@@ -0,0 +1,9 @@
+package app.android.box.waveprotocol.org.androidwave.service.models;
+
+public interface ListElementInitializer {
+
+    String getType();
+
+    String getBackendId();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/models/ListType.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/models/ListType.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/models/ListType.java
new file mode 100644
index 0000000..ed79f0b
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/models/ListType.java
@@ -0,0 +1,211 @@
+package app.android.box.waveprotocol.org.androidwave.service.models;
+
+
+import org.waveprotocol.wave.model.adt.ObservableElementList;
+import org.waveprotocol.wave.model.adt.docbased.DocumentBasedElementList;
+import org.waveprotocol.wave.model.document.Doc;
+import org.waveprotocol.wave.model.document.ObservableDocument;
+import org.waveprotocol.wave.model.document.util.DefaultDocEventRouter;
+import org.waveprotocol.wave.model.document.util.DocEventRouter;
+import org.waveprotocol.wave.model.document.util.DocHelper;
+import org.waveprotocol.wave.model.util.CopyOnWriteSet;
+import org.waveprotocol.wave.model.util.Preconditions;
+import org.waveprotocol.wave.model.wave.SourcesEvents;
+
+import java.util.Collections;
+
+public class ListType extends Type implements SourcesEvents<ListType.Listener> 
{
+
+    public final static String PREFIX = "list";
+    public final static String ROOT_TAG = "list";
+    private final static String ITEM_TAG = "item";
+    private final CopyOnWriteSet<Listener> listeners = CopyOnWriteSet.create();
+    private ObservableElementList<Type, ListElementInitializer> observableList;
+    private ObservableElementList.Listener<Type> observableListListener;
+    private Model model;
+    private String backendDocumentId;
+    private ObservableDocument backendDocument;
+    private Doc.E backendRootElement;
+    private boolean isAttached;
+
+
+    protected ListType(Model model) {
+        this.model = model;
+        this.isAttached = false;
+
+
+        observableListListener = new ObservableElementList.Listener<Type>() {
+
+            @Override
+            public void onValueAdded(Type entry) {
+                for (Listener l : listeners)
+                    l.onValueAdded(entry);
+            }
+
+            @Override
+            public void onValueRemoved(Type entry) {
+                for (Listener l : listeners)
+                    l.onValueRemoved(entry);
+            }
+
+        };
+    }
+
+    protected static Type createAndAttach(Model model, String id) {
+
+        Preconditions.checkArgument(id.startsWith(PREFIX), 
"ListType.createAndAttach() not a list id");
+        ListType list = new ListType(model);
+        list.attach(id);
+        return list;
+
+    }
+
+    @Override
+    protected String getPrefix() {
+        return PREFIX;
+    }
+
+    @Override
+    protected void attach(String docId) {
+
+        if (docId == null) {
+
+            docId = model.generateDocId(getPrefix());
+            backendDocument = model.createDocument(docId);
+
+        } else
+            backendDocument = model.getDocument(docId);
+
+        backendDocumentId = docId;
+
+        backendRootElement = DocHelper.getElementWithTagName(backendDocument, 
ROOT_TAG);
+        if (backendRootElement == null)
+            backendRootElement =
+                    
backendDocument.createChildElement(backendDocument.getDocumentElement(), 
ROOT_TAG,
+                            Collections.<String, String>emptyMap());
+
+        DocEventRouter router = DefaultDocEventRouter.create(backendDocument);
+
+        this.observableList =
+                DocumentBasedElementList.create(router, backendRootElement, 
ITEM_TAG,
+                        new ListElementFactory(model));
+        this.observableList.addListener(observableListListener);
+
+        this.isAttached = true;
+    }
+
+    protected void deAttach() {
+        Preconditions.checkArgument(isAttached, "Unable to deAttach an 
unattached MapType");
+    }
+
+    @Override
+    protected boolean isAttached() {
+        return isAttached;
+    }
+
+    @Override
+    protected String serializeToModel() {
+        Preconditions.checkArgument(isAttached, "Unable to serialize an 
unattached ListType");
+        return backendDocumentId;
+    }
+
+    @Override
+    protected ListElementInitializer getListElementInitializer() {
+        return new ListElementInitializer() {
+
+            @Override
+            public String getType() {
+                return PREFIX;
+            }
+
+            @Override
+            public String getBackendId() {
+                return serializeToModel();
+            }
+
+        };
+    }
+
+    @Override
+    public void addListener(Listener listener) {
+        listeners.add(listener);
+    }
+
+    @Override
+    public void removeListener(Listener listener) {
+        listeners.remove(listener);
+    }
+
+    public Type add(Type value) {
+        Preconditions.checkArgument(isAttached, "ListType.add(): not attached 
to model");
+        Preconditions.checkArgument(!value.isAttached(),
+                "ListType.add(): forbidden to add an already attached Type");
+
+        value.attach(null);
+
+        return observableList.add(value.getListElementInitializer());
+    }
+
+    public Type add(int index, Type value) {
+
+        Preconditions.checkArgument(index >= 0 && index <= 
observableList.size(),
+                "ListType.add(): add to index out of bounds");
+        Preconditions.checkArgument(isAttached, "ListType.add(): not attached 
to model");
+        Preconditions.checkArgument(!value.isAttached(),
+                "ListType.add(): forbidden to add an already attached Type");
+
+        value.attach(null);
+
+        return observableList.add(index, value.getListElementInitializer());
+    }
+
+    public Type remove(int index) {
+        if (observableList == null) return null;
+        Type removedInstance = observableList.get(index);
+        if (!observableList.remove(removedInstance)) return null;
+        return removedInstance;
+    }
+
+    public Type get(int index) {
+        if (observableList == null) return null;
+        Preconditions.checkArgument(index >= 0 && index < 
observableList.size(),
+                "ListType.get(): add to index out of bounds");
+        return observableList.get(index);
+    }
+
+    public int indexOf(Type type) {
+        return observableList != null ? observableList.indexOf(type) : -1;
+    }
+
+    public int size() {
+        return observableList != null ? observableList.size() : 0;
+    }
+
+    public Iterable<Type> getValues() {
+        return observableList != null ? observableList.getValues() : 
Collections.<Type>emptyList();
+    }
+
+    @Override
+    public String getDocumentId() {
+        return backendDocumentId;
+    }
+
+    @Override
+    public Model getModel() {
+        return model;
+    }
+
+    @Override
+    public String getType() {
+        return "ListType";
+    }
+
+    public interface Listener {
+
+        void onValueAdded(Type entry);
+
+        void onValueRemoved(Type entry);
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave-android/blob/514564b1/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/models/MapSerializer.java
----------------------------------------------------------------------
diff --git 
a/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/models/MapSerializer.java
 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/models/MapSerializer.java
new file mode 100644
index 0000000..e42b673
--- /dev/null
+++ 
b/app/src/main/java/app/android/box/waveprotocol/org/androidwave/service/models/MapSerializer.java
@@ -0,0 +1,47 @@
+package app.android.box.waveprotocol.org.androidwave.service.models;
+
+public class MapSerializer implements 
org.waveprotocol.wave.model.util.Serializer<Type> {
+
+    protected Model model;
+
+    protected MapSerializer(Model model) {
+        this.model = model;
+    }
+
+    @Override
+    public String toString(Type x) {
+        return x.serializeToModel();
+    }
+
+    @Override
+    public Type fromString(String s) {
+
+        if (s.startsWith(StringType.PREFIX)) {
+
+            return StringType.createAndAttach(model, s);
+
+        } else if (s.startsWith(MapType.PREFIX)) {
+
+            return MapType.createAndAttach(model, s);
+
+        } else if (s.startsWith(ListType.PREFIX)) {
+
+            return ListType.createAndAttach(model, s);
+
+        } else if (s.startsWith(TextType.PREFIX)) {
+
+            return TextType.createAndAttach(model, s);
+        }
+
+
+        return null;
+    }
+
+    @Override
+    public Type fromString(String s, Type defaultValue) {
+        if (s == null) return defaultValue;
+        Type instance = fromString(s);
+        return instance != null ? instance : defaultValue;
+    }
+
+}

Reply via email to