Made a number of changes to meet the need of the user study:
-Added message groups (inbox,sent,draft)
-Added auto-complete & contact permission
-Added attachments
-Added individual message field permission toggling
-disabled default cast blessing permissions
-Added a service for picking up share events. It currently doesn't trigger any actions.
-Fixed dissasociation bugs
-Tweaked text synchronization

Change-Id: I2293d060f1751207a91cb907dd325359399a571b
diff --git a/permissions/app/src/main/AndroidManifest.xml b/permissions/app/src/main/AndroidManifest.xml
index 85126f9..18d37af 100644
--- a/permissions/app/src/main/AndroidManifest.xml
+++ b/permissions/app/src/main/AndroidManifest.xml
@@ -3,6 +3,7 @@
     package="examples.baku.io.permissions">
 
     <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.READ_CONTACTS" />
 
     <application
         android:name=".PermissionApplication"
@@ -25,17 +26,69 @@
         <service
             android:name=".PermissionService"
             android:enabled="true"
-            android:exported="true" />
+            android:exported="true">
+            <intent-filter>
+                <action android:name="examples.baku.io.permissions.ShareEvent" />
+            </intent-filter>
+        </service>
 
         <activity
             android:name=".examples.ComposeActivity"
             android:label="@string/title_activity_compose"
-            android:windowSoftInputMode="adjustResize"
-            android:theme="@style/AppTheme.NoActionBar" />
+            android:theme="@style/AppTheme.NoActionBar"
+            android:windowSoftInputMode="adjustResize" />
         <activity
             android:name=".discovery.DevicePickerActivity"
             android:label="Device Picker"
-            android:theme="@style/Theme.AppCompat.Light.Dialog"></activity>
+            android:theme="@style/Theme.AppCompat.Light.Dialog" />
+
+        <service
+            android:name=".PermissionTargetService"
+            android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
+            <intent-filter>
+                <action android:name="android.service.chooser.ChooserTargetService" />
+            </intent-filter>
+        </service>
+
+        <activity android:name=".PermissionTargetActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND" />
+
+                <category android:name="android.intent.category.DEFAULT" />
+
+                <data android:mimeType="*/*" />
+                <data android:mimeType="application/*" />
+                <data android:mimeType="audio/*" />
+                <data android:mimeType="image/*" />
+                <data android:mimeType="message/*" />
+                <data android:mimeType="multipart/*" />
+                <data android:mimeType="text/*" />
+                <data android:mimeType="video/*" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <action android:name="android.intent.action.SENDTO" />
+
+                <data android:scheme="mailto" />
+
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+            </intent-filter>
+
+            <meta-data
+                android:name="android.service.chooser.chooser_target_service"
+                android:value=".PermissionTargetService" />
+        </activity>
+        <activity
+            android:name=".examples.ProcessTextActivity"
+            android:theme="@android:style/Theme.NoDisplay"
+            android:label="Cast">
+            <intent-filter>
+                <action android:name="android.intent.action.PROCESS_TEXT"/>
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="text/*" />
+            </intent-filter>
+        </activity>
     </application>
 
 </manifest>
\ No newline at end of file
diff --git a/permissions/app/src/main/java/examples/baku/io/permissions/Blessing.java b/permissions/app/src/main/java/examples/baku/io/permissions/Blessing.java
index 6cda9a6..cb0e9e4 100644
--- a/permissions/app/src/main/java/examples/baku/io/permissions/Blessing.java
+++ b/permissions/app/src/main/java/examples/baku/io/permissions/Blessing.java
@@ -79,7 +79,7 @@
 
     //root blessings have no source blessing and their id is the same as their target
     public static Blessing createRoot(PermissionManager permissionManager, String target) {
-        return create(permissionManager,target, null, target);
+        return create(permissionManager, target, null, target);
     }
 
     public static Blessing fromSnapshot(PermissionManager permissionManager, DataSnapshot snapshot) {
@@ -88,7 +88,7 @@
         String source = null;
         if (snapshot.hasChild("source"))
             source = snapshot.child("source").getValue(String.class);
-        return create(permissionManager,id, source, target);
+        return create(permissionManager, id, source, target);
     }
 
     public OnBlessingUpdatedListener addListener(OnBlessingUpdatedListener listener) {
@@ -123,6 +123,10 @@
         }
     };
 
+    public int getPermissions(String path) {
+        return permissionTree.getPermissions(path);
+    }
+
     public boolean isSynched() {
         return snapshot != null;
     }
@@ -470,7 +474,7 @@
         }
 
         public int getPermissions(String path) {
-            path = Utils.getNearestCommonAncestor(path,keySet());
+            path = Utils.getNearestCommonAncestor(path, keySet());
             Permission permission = get(path);
             if (permission == null) {
                 return 0;
diff --git a/permissions/app/src/main/java/examples/baku/io/permissions/PermissionService.java b/permissions/app/src/main/java/examples/baku/io/permissions/PermissionService.java
index 575fe19..0bbc920 100644
--- a/permissions/app/src/main/java/examples/baku/io/permissions/PermissionService.java
+++ b/permissions/app/src/main/java/examples/baku/io/permissions/PermissionService.java
@@ -8,8 +8,10 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.graphics.drawable.Icon;
 import android.os.Binder;
@@ -68,6 +70,8 @@
     public static final String EXTRA_NOTIFICATION_ID = "notificationId";
     public static final String EXTRA_ACTION_ID = "notificationId";
 
+    public static final String ACTION_SHARE_EVENT = "examples.baku.io.permissions.ShareEvent";
+
     NotificationManager mNotificationManager;
 
     FirebaseDatabase mFirebaseDB;
@@ -192,6 +196,12 @@
                         .setVibrate(new long[]{100})
                         .setPriority(Notification.PRIORITY_MAX)
                         .build();
+
+                Integer previousNotificationId = mRequestNotifications.get(request.getId());
+                if (previousNotificationId != null) {
+                    mNotificationManager.cancel(previousNotificationId);
+                }
+
                 mNotificationManager.notify(nId, notification);
                 mRequestNotifications.put(request.getId(), nId);
                 return true;
@@ -205,9 +215,12 @@
             }
         });
 
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ACTION_SHARE_EVENT);
+        registerReceiver(eventReceiver, filter);
+
         mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
         initForegroundNotification();
-
         registerDevice();
         initMessenger();
         initDiscovery();
@@ -215,24 +228,33 @@
         mRunning = true;
     }
 
-    public void requestDialog(String requestId, String title, String subtitle, ActionCallback accept, ActionCallback reject) {
+    public void requestDialog(String requestId, String title, String subtitle, ActionCallback accept, ActionCallback reject, Intent content) {
         Integer previousNotificationId = mRequestNotifications.get(requestId);
         if (previousNotificationId != null) {
             mNotificationManager.cancel(previousNotificationId);
         }
-        Notification notification = new Notification.Builder(PermissionService.this)
+        String aId = UUID.randomUUID().toString();
+        String dId = UUID.randomUUID().toString();
+        Notification.Builder builder = new Notification.Builder(PermissionService.this)
                 .setSmallIcon(keyIcon)
                 .setContentTitle(title)
                 .setSubText(subtitle)
-                .addAction(createAction(grantIcon, "Accept", UUID.randomUUID().toString(), accept))
-                .setDeleteIntent(createNotificationCallback(UUID.randomUUID().toString(), reject))
+                .addAction(createAction(grantIcon, "Accept", aId, accept))
+                .setDeleteIntent(createNotificationCallback(dId, reject))
                 .setVibrate(new long[]{100})
-                .setPriority(Notification.PRIORITY_MAX)
-                .build();
+                .setPriority(Notification.PRIORITY_MAX);
+
+        if (content != null) {
+            builder.setContentIntent(PendingIntent.getActivity(this, mActionCounter++, content, PendingIntent.FLAG_CANCEL_CURRENT));
+        }
+
+        Notification notification = builder.build();
 
         int nId = mNotificationCounter++;
         mNotificationManager.notify(nId, notification);
         mRequestNotifications.put(requestId, nId);
+        mRequestNotifications.put(aId, nId);
+        mRequestNotifications.put(dId, nId);
     }
 
 
@@ -290,7 +312,8 @@
             Intent emailIntent = new Intent(PermissionService.this, ComposeActivity.class);
             emailIntent.putExtra(ComposeActivity.EXTRA_MESSAGE_PATH, focusPath);
             emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            notificationBuilder.addAction(new Notification.Action.Builder(castIcon, "Pull Message", PendingIntent.getActivity(this, 0, emailIntent, PendingIntent.FLAG_CANCEL_CURRENT)).build());
+//            notificationBuilder.addAction(new Notification.Action.Builder(castIcon, "Pull Message", PendingIntent.getActivity(this, 0, emailIntent, PendingIntent.FLAG_CANCEL_CURRENT)).build());
+            notificationBuilder.setContentIntent(PendingIntent.getActivity(this, 0, emailIntent, PendingIntent.FLAG_CANCEL_CURRENT));
         }
 
         mConstellation.add(dId);
@@ -384,7 +407,7 @@
         mMessenger.on("disassociate", new Messenger.Listener() {
             @Override
             public void call(Message msg, Messenger.Ack callback) {
-
+                removeFromConstellation(msg.getMessage());
             }
         });
 
@@ -416,8 +439,15 @@
     }
 
     public void removeFromConstellation(String deviceId) {
+        Integer nId = mConstellationNotifications.remove(deviceId);
+        if (nId != null) {
+            mNotificationManager.cancel(nId);
+        }
+
+        if (!mConstellation.contains(deviceId)) {
+            return;
+        }
         mConstellation.remove(deviceId);
-        mConstellationNotifications.remove(deviceId);
         //revoke all blessings
         for (Blessing blessing : mPermissionManager.getReceivedBlessings()) {
             Blessing granted = blessing.getBlessing(deviceId);
@@ -425,8 +455,19 @@
                 granted.revoke();
             }
         }
+        for (DiscoveryListener listener : mDiscoveryListener) {
+            listener.onDisassociate(deviceId);
+        }
+        mMessenger.to(deviceId).emit("disassociate", mDeviceId);
     }
 
+    private final BroadcastReceiver eventReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+
+        }
+    };
+
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
 
@@ -479,10 +520,10 @@
                 String dId = intent.getStringExtra("deviceId");
                 if (dId != null) {
                     removeFromConstellation(dId);
-
-
                 }
 
+            } else if ("clipboardEvent".equals(type)) {
+
             } else if ("close".equals(type)) {
                 stopSelf();
             }
@@ -588,6 +629,7 @@
         if (mPermissionManager != null) {
             mPermissionManager.onDestroy();
         }
+        unregisterReceiver(eventReceiver);
         super.onDestroy();
     }
 
diff --git a/permissions/app/src/main/java/examples/baku/io/permissions/PermissionTargetActivity.java b/permissions/app/src/main/java/examples/baku/io/permissions/PermissionTargetActivity.java
new file mode 100644
index 0000000..a53b1f5
--- /dev/null
+++ b/permissions/app/src/main/java/examples/baku/io/permissions/PermissionTargetActivity.java
@@ -0,0 +1,23 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package examples.baku.io.permissions;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
+
+public class PermissionTargetActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Intent intent = new Intent(PermissionService.ACTION_SHARE_EVENT);
+        sendBroadcast(intent);
+        finish();
+    }
+}
diff --git a/permissions/app/src/main/java/examples/baku/io/permissions/PermissionTargetService.java b/permissions/app/src/main/java/examples/baku/io/permissions/PermissionTargetService.java
new file mode 100644
index 0000000..7216625
--- /dev/null
+++ b/permissions/app/src/main/java/examples/baku/io/permissions/PermissionTargetService.java
@@ -0,0 +1,39 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package examples.baku.io.permissions;
+
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.drawable.Icon;
+import android.service.chooser.ChooserTarget;
+import android.service.chooser.ChooserTargetService;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by phamilton on 8/16/16.
+ */
+public class PermissionTargetService extends ChooserTargetService {
+
+    @Override
+    public List<ChooserTarget> onGetChooserTargets(ComponentName targetActivityName, IntentFilter matchedFilter) {
+        final List<ChooserTarget> targets = new ArrayList<>();
+
+        final String title = "Cast";
+        final Icon icon = Icon.createWithResource(this, R.mipmap.ic_launcher);
+        final float score = 1.0f;
+
+        Intent intent = new Intent(PermissionService.ACTION_SHARE_EVENT);
+        sendBroadcast(intent);
+
+        targets.add(new ChooserTarget(title, icon, score, targetActivityName, null));
+        return targets;
+    }
+
+}
diff --git a/permissions/app/src/main/java/examples/baku/io/permissions/PermissionedTextLayout.java b/permissions/app/src/main/java/examples/baku/io/permissions/PermissionedTextLayout.java
index 477bf0b..083505c 100644
--- a/permissions/app/src/main/java/examples/baku/io/permissions/PermissionedTextLayout.java
+++ b/permissions/app/src/main/java/examples/baku/io/permissions/PermissionedTextLayout.java
@@ -10,30 +10,42 @@
 import android.content.SharedPreferences;
 import android.graphics.Color;
 import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.support.design.widget.TextInputLayout;
 import android.text.Editable;
 import android.text.InputType;
 import android.text.Spannable;
 import android.text.SpannableStringBuilder;
 import android.text.TextWatcher;
+import android.text.method.KeyListener;
 import android.text.style.BackgroundColorSpan;
 import android.text.style.StrikethroughSpan;
 import android.util.AttributeSet;
 import android.view.Gravity;
+import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.inputmethod.EditorInfo;
+import android.widget.ArrayAdapter;
+import android.widget.AutoCompleteTextView;
 import android.widget.EditText;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
 
 import com.google.firebase.database.DatabaseError;
 import com.joanzapata.iconify.IconDrawable;
 import com.joanzapata.iconify.fonts.MaterialIcons;
 
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
 
 import examples.baku.io.permissions.synchronization.SyncText;
 import examples.baku.io.permissions.synchronization.SyncTextDiff;
@@ -53,12 +65,20 @@
     private PermissionedEditText editText;
     private FrameLayout overlay;
 
+    private final SortedMap<Integer, ActionItem> mActions = new TreeMap<>();
+    private ActionItem mPrimaryAction;
+
     private ImageView actionButton;
 
     private PermissionedTextListener permissionedTextListener = null;
 
     private int inputType = InputType.TYPE_CLASS_TEXT;
 
+    private boolean editable = true;
+
+
+    private int version = -1;
+
     public void unlink() {
         if (syncText != null) {
             syncText.unlink();
@@ -85,6 +105,10 @@
         init(context, attrs, defStyleAttr);
     }
 
+    public void setAutoCompleteAdapter(ArrayAdapter<String> adapter) {
+        editText.setAdapter(adapter);
+    }
+
     public void init(final Context context, AttributeSet attrs, int defStyleAttr) {
         FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                 LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
@@ -102,19 +126,31 @@
         if (inputType != EditorInfo.TYPE_NULL) {
             editText.setInputType(inputType);
         }
+
+
         textInputLayout.addView(editText, params);
         addView(textInputLayout);
 
         overlay = new FrameLayout(context);
         overlay.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
-        overlay.setBackgroundColor(Color.BLACK);
+//        overlay.setBackgroundColor(Color.BLACK);
+        overlay.setBackgroundResource(R.drawable.permission_overlay_bg);
         overlay.setOnTouchListener(new OnTouchListener() {
             @Override
             public boolean onTouch(View v, MotionEvent event) {
+                if (event.getAction() == MotionEvent.ACTION_UP) {
+                    Toast.makeText(getContext(), "This device does not have permission to view this field", 0).show();
+                }
                 return true;
             }
         });
         overlay.setVisibility(GONE);
+        TextView overlayLabel = new TextView(context);
+        overlayLabel.setTextSize(24);
+        overlayLabel.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
+        overlayLabel.setText(hint + " Unavailable");
+
+        overlay.addView(overlayLabel);
         addView(overlay);
 
         actionButton = new ImageView(context);
@@ -134,7 +170,11 @@
                     }
                     case MotionEvent.ACTION_UP:
                         if (permissionedTextListener != null) {
-                            permissionedTextListener.onAction(0, PermissionedTextLayout.this);
+                            int aId = 0;
+                            if (mPrimaryAction != null) {
+                                aId = mPrimaryAction.id;
+                            }
+                            permissionedTextListener.onAction(aId, PermissionedTextLayout.this);
                         }
                     case MotionEvent.ACTION_CANCEL: {
                         ImageView view = (ImageView) v;
@@ -210,26 +250,44 @@
         if (syncText != null) {
             this.syncText.setPermissions(permissions);
             if ((permissions & PermissionManager.FLAG_WRITE) == PermissionManager.FLAG_WRITE) {
+                editText.setVisibility(VISIBLE);
                 overlay.setVisibility(GONE);
                 editText.setInputType(inputType);
                 editText.setEnabled(true);
                 syncText.acceptSuggestions();
             } else if ((permissions & PermissionManager.FLAG_SUGGEST) == PermissionManager.FLAG_SUGGEST) {
+                editText.setVisibility(VISIBLE);
                 overlay.setVisibility(GONE);
                 editText.setInputType(inputType);
                 editText.setEnabled(true);
             } else if ((permissions & PermissionManager.FLAG_READ) == PermissionManager.FLAG_READ) {
+                editText.setVisibility(VISIBLE);
                 overlay.setVisibility(GONE);
                 syncText.rejectSuggestions();
                 editText.setInputType(EditorInfo.TYPE_NULL);
                 editText.setEnabled(false);
             } else {
+                editText.setVisibility(GONE);
                 overlay.setVisibility(VISIBLE);
                 syncText.rejectSuggestions();
                 editText.setInputType(EditorInfo.TYPE_NULL);
                 editText.setEnabled(false);
             }
         }
+        if (!editable) {
+            editText.disable();
+        } else {
+            editText.enable();
+        }
+    }
+
+    public void setEditable(boolean editable) {
+//        this.editable = editable;
+        update();
+    }
+
+    public String getText() {
+        return editText.getText().toString();
     }
 
     private synchronized void updateText(final LinkedList<SyncTextDiff> diffs) {
@@ -251,8 +309,6 @@
         }
     }
 
-    private int version = -1;
-
     private TextWatcher watcher = new TextWatcher() {
         @Override
         public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@@ -270,6 +326,33 @@
         }
     };
 
+    public void clearActions() {
+        mActions.clear();
+        mPrimaryAction = null;
+        updateActions();
+    }
+
+    public void removeAction(int id) {
+        mActions.remove(id);
+        updateActions();
+    }
+
+    public void setAction(int id, Drawable icon, String label) {
+        mActions.put(id, new ActionItem(id, icon, label));
+        mPrimaryAction = mActions.values().iterator().next();
+        updateActions();
+    }
+
+    public void updateActions() {
+        if (mActions.size() == 1) {
+            ActionItem action = mPrimaryAction;
+            actionButton.setVisibility(VISIBLE);
+            actionButton.setImageDrawable(action.icon);
+        } else {
+            actionButton.setVisibility(GONE);
+        }
+    }
+
     public void acceptSuggestions(String src) {
         syncText.acceptSuggestions(src);
     }
@@ -319,6 +402,15 @@
         }
     };
 
+    public List<SyncTextDiff> getSuggestions() {
+        List<SyncTextDiff> result = new LinkedList<>();
+        for (SyncTextDiff diff : syncText.getDiffs()) {
+            if (diff.operation != SyncTextDiff.EQUAL && diff.permission == PermissionManager.FLAG_SUGGEST) {
+                result.add(diff);
+            }
+        }
+        return result;
+    }
 
     private SyncTextDiff getDiffAt(int index) {
         int count = 0;
@@ -333,29 +425,58 @@
         return null;
     }
 
+    class ActionItem {
+        int id;
+        Drawable icon;
+        String label;
+
+        public ActionItem(int id, Drawable icon, String label) {
+            this.id = id;
+            this.icon = icon;
+            this.label = label;
+        }
+    }
+
     private interface OnSelectionChangedListener {
         void onSelectionChanged(int selStart, int selEnd, boolean focus);
     }
 
-    private class PermissionedEditText extends EditText {
+    private class PermissionedEditText extends AutoCompleteTextView {
         private OnSelectionChangedListener mSelectionListener;
+        private KeyListener keyListener;
 
         public PermissionedEditText(Context context) {
             super(context);
+            init();
         }
 
         public PermissionedEditText(Context context, AttributeSet attrs) {
             super(context, attrs);
+            init();
         }
 
         public PermissionedEditText(Context context, AttributeSet attrs, int defStyleAttr) {
             super(context, attrs, defStyleAttr);
+            init();
         }
 
+        private void init() {
+            keyListener = getKeyListener();
+        }
+
+
         public void setSelectionListener(OnSelectionChangedListener mSelectionListener) {
             this.mSelectionListener = mSelectionListener;
         }
 
+        public void enable() {
+            setKeyListener(keyListener);
+        }
+
+        public void disable() {
+            setKeyListener(null);
+        }
+
         @Override
         protected void onSelectionChanged(int selStart, int selEnd) {
             super.onSelectionChanged(selStart, selEnd);
diff --git a/permissions/app/src/main/java/examples/baku/io/permissions/discovery/DevicePickerActivity.java b/permissions/app/src/main/java/examples/baku/io/permissions/discovery/DevicePickerActivity.java
index 5ced936..5b60bef 100644
--- a/permissions/app/src/main/java/examples/baku/io/permissions/discovery/DevicePickerActivity.java
+++ b/permissions/app/src/main/java/examples/baku/io/permissions/discovery/DevicePickerActivity.java
@@ -12,6 +12,7 @@
 import android.os.IBinder;
 import android.support.v7.app.AppCompatActivity;
 
+import java.util.HashMap;
 import java.util.Map;
 
 import examples.baku.io.permissions.PermissionService;
@@ -22,7 +23,7 @@
 public class DevicePickerActivity extends AppCompatActivity implements EventFragment.EventFragmentListener, ServiceConnection {
 
     private PermissionService mPermissionService;
-    private Map<String, DeviceData> mDevices;
+    private Map<String, DeviceData> mDevices = new HashMap<>();
     private DevicePickerActivityFragment mFragment;
 
     private int requestCode;
@@ -85,7 +86,13 @@
     @Override
     public void onServiceConnected(ComponentName name, IBinder service) {
         mPermissionService = ((PermissionService.PermissionServiceBinder)service).getInstance();
-        mDevices = mPermissionService.getDiscovered();
+
+        mDevices.clear();
+        for(Map.Entry<String, DeviceData> device : mPermissionService.getDiscovered().entrySet()){
+            if(device.getValue().isActive()){
+                mDevices.put(device.getKey(), device.getValue());
+            }
+        }
         if(mFragment != null){
             mFragment.setDevices(mDevices);
         }
diff --git a/permissions/app/src/main/java/examples/baku/io/permissions/examples/ComposeActivity.java b/permissions/app/src/main/java/examples/baku/io/permissions/examples/ComposeActivity.java
index a04fd16..2442c0e 100644
--- a/permissions/app/src/main/java/examples/baku/io/permissions/examples/ComposeActivity.java
+++ b/permissions/app/src/main/java/examples/baku/io/permissions/examples/ComposeActivity.java
@@ -4,25 +4,36 @@
 
 package examples.baku.io.permissions.examples;
 
+import android.Manifest;
 import android.app.Service;
 import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
 import android.graphics.Color;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.provider.ContactsContract;
+import android.support.annotation.NonNull;
 import android.support.design.widget.FloatingActionButton;
-import android.support.design.widget.TextInputLayout;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AlertDialog;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.Toolbar;
-import android.text.Editable;
 import android.text.InputType;
-import android.text.TextWatcher;
-import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
-import android.widget.EditText;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
 import android.widget.Toast;
 
 import com.google.common.collect.HashMultimap;
@@ -37,8 +48,11 @@
 import org.json.JSONException;
 import org.json.JSONObject;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 import java.util.UUID;
 
 import examples.baku.io.permissions.Blessing;
@@ -47,14 +61,17 @@
 import examples.baku.io.permissions.PermissionService;
 import examples.baku.io.permissions.PermissionedTextLayout;
 import examples.baku.io.permissions.R;
+import examples.baku.io.permissions.discovery.DeviceData;
 import examples.baku.io.permissions.discovery.DevicePickerActivity;
 import examples.baku.io.permissions.synchronization.SyncText;
 import examples.baku.io.permissions.synchronization.SyncTextDiff;
+import examples.baku.io.permissions.util.Utils;
 
 public class ComposeActivity extends AppCompatActivity implements ServiceConnection {
 
     public final static String EXTRA_MESSAGE_ID = "messageId";
     public final static String EXTRA_MESSAGE_PATH = "messagePath";
+    private final static int SELECT_ATTACHMENT = 1232;
 
     private String mPath;
 
@@ -66,42 +83,52 @@
     private DatabaseReference mMessageRef;
     private DatabaseReference mSyncedMessageRef;
 
-    private Blessing mCastBlessing;
+    private Blessing mSourceBlessing;
     private Blessing mPublicBlessing;
+    private final Set<String> targetDevices = new HashSet<>();
 
-    PermissionedTextLayout mTo;
-    PermissionedTextLayout mFrom;
-    PermissionedTextLayout mSubject;
-    PermissionedTextLayout mMessage;
 
-    Multimap<String, PermissionRequest> mRequests = HashMultimap.create();
-    HashMap<String, PermissionedTextLayout> mPermissionedFields = new HashMap<>();
-    HashMap<String, Integer> mPermissions = new HashMap<>();
-    HashMap<String, SyncText> syncTexts = new HashMap<>();
+    private String mGroup;
+    private String mAttachment;
+    private int mAttachmentPermission;
+    private RelativeLayout mAttachmentView;
+    private TextView mAttachmentText;
+    private ImageView mAttachmentIcon;
+    private ImageView mAttachmentCast;
 
+    private PermissionedTextLayout mTo;
+    private PermissionedTextLayout mFrom;
+    private PermissionedTextLayout mSubject;
+    private PermissionedTextLayout mMessage;
+
+    private Multimap<String, PermissionRequest> mRequests = HashMultimap.create();
+    private HashMap<String, PermissionedTextLayout> mPermissionedFields = new HashMap<>();
+    private HashMap<String, Integer> mPermissions = new HashMap<>();
+    private HashMap<String, SyncText> syncTexts = new HashMap<>();
+
+    private ArrayAdapter<String> contactAdapter;
+    FloatingActionButton mFab;
+    private Menu mMenu;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+
         setContentView(R.layout.activity_compose);
         Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
         toolbar.setTitle("Compose");
         setSupportActionBar(toolbar);
         getSupportActionBar().setDisplayHomeAsUpEnabled(true);
         getSupportActionBar().setDisplayShowHomeEnabled(true);
-        toolbar.setTitle("Compose Message");
+        toolbar.setTitle("Compose");
 
 
-        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
-        fab.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                sendMessage();
-            }
-        });
+        mFab = (FloatingActionButton) findViewById(R.id.fab);
 
         mTo = (PermissionedTextLayout) findViewById(R.id.composeTo);
 
+
         mFrom = (PermissionedTextLayout) findViewById(R.id.composeFrom);
 
         mSubject = (PermissionedTextLayout) findViewById(R.id.composeSubject);
@@ -109,6 +136,34 @@
         mMessage = (PermissionedTextLayout) findViewById(R.id.composeMessage);
         mMessage.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
 
+        mAttachmentView = (RelativeLayout) findViewById(R.id.composeAttachment);
+        mAttachmentText = (TextView) findViewById(R.id.composeAttachmentText);
+        mAttachmentIcon = (ImageView) findViewById(R.id.composeAttachmentIcon);
+        mAttachmentIcon.setImageDrawable(new IconDrawable(this, MaterialIcons.md_attach_file).actionBarSize());
+        mAttachmentCast = (ImageView) findViewById(R.id.composeAttachmentCast);
+        mAttachmentCast.setImageDrawable(new IconDrawable(this, MaterialIcons.md_cast).actionBarSize());
+        mAttachmentCast.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if(mPublicBlessing != null){
+                    int current = mPublicBlessing.getPermissions(mPath + "/attachment");
+                    if((current & PermissionManager.FLAG_READ) == PermissionManager.FLAG_READ){
+                        mPublicBlessing.setPermissions(mPath + "/attachment", current & ~PermissionManager.FLAG_READ);
+                        mAttachmentCast.setImageDrawable(new IconDrawable(ComposeActivity.this, MaterialIcons.md_cast).actionBarSize());
+                    }else{
+                        mPublicBlessing.setPermissions(mPath + "/attachment", current | PermissionManager.FLAG_READ);
+                        mAttachmentCast.setImageDrawable(new IconDrawable(ComposeActivity.this, MaterialIcons.md_cancel).color(Color.RED).actionBarSize());
+                    }
+                }
+            }
+        });
+        mAttachmentCast.setVisibility(View.GONE);
+
+        setGroup("Inbox");  //default
+        setAttachment(null, mAttachmentPermission);
+
+        getContactsPermission();
+
         bindService(new Intent(this, PermissionService.class), this, Service.BIND_AUTO_CREATE);
     }
 
@@ -121,6 +176,18 @@
                 new IconDrawable(this, MaterialIcons.md_cast)
                         .color(Color.WHITE)
                         .actionBarSize());
+        MenuItem deleteItem = menu.findItem(R.id.action_delete);
+
+        MenuItem attachItem = menu.findItem(R.id.action_attach).setIcon(
+                new IconDrawable(this, MaterialIcons.md_attach_file)
+                        .color(Color.WHITE)
+                        .actionBarSize());
+        attachItem.setVisible(false);
+        if (mOwner == null || !mOwner.equals(mDeviceId)) {
+            attachItem.setVisible(false);
+            deleteItem.setVisible(false);
+        }
+        mMenu = menu;
         return true;
     }
 
@@ -131,9 +198,8 @@
         // as you specify a parent activity in AndroidManifest.xml.
         int id = item.getItemId();
 
-        //noinspection SimplifiableIfStatement
-        if (id == R.id.action_send) {
-            sendMessage();
+        if (id == R.id.action_delete) {
+            checkAndSendMessage();  //currently send and delete have the same result
         } else if (id == R.id.action_cast) {
             if (mPermissionService != null) {
                 Intent requestIntent = new Intent(ComposeActivity.this, DevicePickerActivity.class);
@@ -141,9 +207,8 @@
                 requestIntent.putExtra(DevicePickerActivity.EXTRA_REQUEST_ARGS, mPath);
                 startActivityForResult(requestIntent, DevicePickerActivity.REQUEST_DEVICE_ID);
             }
-
-        } else if (id == R.id.action_settings) {
-
+        } else if (id == R.id.action_attach) {
+            pickAttachment();
         } else if (id == android.R.id.home) {
             finish();
         }
@@ -151,15 +216,198 @@
         return super.onOptionsItemSelected(item);
     }
 
+    public void getContactsPermission() {
+        // Here, thisActivity is the current activity
+        if (ContextCompat.checkSelfPermission(this,
+                Manifest.permission.READ_CONTACTS)
+                != PackageManager.PERMISSION_GRANTED) {
 
-    void sendMessage() {
+            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
+                    Manifest.permission.READ_CONTACTS)) {
+
+            } else {
+                ActivityCompat.requestPermissions(this,
+                        new String[]{Manifest.permission.READ_CONTACTS},
+                        1);
+            }
+        } else { //already granted
+            onContactsPermissionGranted();
+        }
+    }
+
+    private void setGroup(String group) {
+        mGroup = group;
+        boolean editable = false;
+        if ("Inbox".equals(group)) {
+            mFab.setImageDrawable(new IconDrawable(this, MaterialIcons.md_reply).color(Color.WHITE));
+            mFab.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    launchAndCreateMessage(ComposeActivity.this, mMessageRef.getParent(), mFrom.getText(), "myself@email.com", "RE: " + mSubject.getText(), "", null, "Drafts");
+                }
+            });
+
+        } else if ("Drafts".equals(group)) {
+            editable = true;
+            mFab.setImageDrawable(new IconDrawable(this, MaterialIcons.md_send).color(Color.WHITE));
+            if (mOwner == null || !mOwner.equals(mDeviceId)) {
+                if (mMenu != null) {
+                    mMenu.findItem(R.id.action_delete).setVisible(false);
+                }
+                mFab.setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        Toast.makeText(ComposeActivity.this, "Can't send message from this device", 0).show();
+                    }
+                });
+            } else {
+                mFab.setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        checkAndSendMessage();
+                    }
+                });
+            }
+
+
+            if (mMenu != null) {
+                mMenu.findItem(R.id.action_attach).setVisible(true);
+            }
+        } else if ("Sent".equals(group)) {
+            mFab.setImageDrawable(new IconDrawable(this, MaterialIcons.md_forward).color(Color.WHITE));
+            mFab.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    launchAndCreateMessage(ComposeActivity.this, mMessageRef.getParent(), "", "myself@email.com", "FWD: " + mSubject.getText(), mMessage.getText(), null, "Drafts");
+                }
+            });
+            if (mMenu != null) {
+                mMenu.findItem(R.id.action_attach).setVisible(true);
+            }
+        }
+
+        mTo.setEditable(editable);
+        mSubject.setEditable(editable);
+        mMessage.setEditable(editable);
+    }
+
+
+
+    public void setAttachment(final String attachment, final int permission) {
+        this.mAttachment = attachment;
+        this.mAttachmentPermission = permission;
+        if (attachment == null || (permission & PermissionManager.FLAG_READ) != PermissionManager.FLAG_READ) {
+            mAttachmentView.setVisibility(View.GONE);
+        } else {
+            mAttachmentView.setVisibility(View.VISIBLE);
+            mAttachmentView.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    Utils.viewAttachment(ComposeActivity.this, attachment);
+                }
+            });
+            mAttachmentText.setText(attachment);
+
+        }
+    }
+
+    public static void launchAndCreateMessage(Context context, DatabaseReference messagesRef, String to, String from, String subject, String msg, String attachment, String group) {
+        MessageData data = new MessageData(UUID.randomUUID().toString(), to, from, subject, msg, attachment, group);
+        messagesRef.child(data.getId()).setValue(data);
+        Intent intent = new Intent(context, ComposeActivity.class);
+        intent.putExtra(ComposeActivity.EXTRA_MESSAGE_ID, data.getId());
+        context.startActivity(intent);
+    }
+
+    private void onContactsPermissionGranted() {
+        ArrayList<String> emailAddressCollection = new ArrayList<String>();
+        ContentResolver cr = getContentResolver();
+        Cursor emailCur = cr.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, null, null, null);
+        while (emailCur.moveToNext()) {
+            String email = emailCur.getString(emailCur.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));
+            emailAddressCollection.add(email);
+        }
+        emailCur.close();
+        String[] emailAddresses = new String[emailAddressCollection.size()];
+        emailAddressCollection.toArray(emailAddresses);
+        contactAdapter = new ArrayAdapter<String>(this,
+                android.R.layout.simple_dropdown_item_1line, emailAddresses);
+        mTo.setAutoCompleteAdapter(contactAdapter);
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+        //TODO: generalize to other permissions. Not just contacts
+        if (grantResults.length > 0
+                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+            onContactsPermissionGranted();
+        }
+    }
+
+
+    ArrayList<SyncTextDiff> getSuggestions() {
+        ArrayList<SyncTextDiff> result = new ArrayList<>();
+        result.addAll(mTo.getSuggestions());
+        result.addAll(mFrom.getSuggestions());
+        result.addAll(mSubject.getSuggestions());
+        result.addAll(mMessage.getSuggestions());
+        return result;
+    }
+
+    void checkAndSendMessage() {
         //TODO: PermissionManager.requestDialog()
         mPermissionManager.request(mPath + "/send", mDeviceId)
                 .putExtra(PermissionManager.EXTRA_TIMEOUT, "2000")
                 .putExtra(PermissionManager.EXTRA_COLOR, "#F00");
+
+        if (getSuggestions().size() > 0) {
+            AlertDialog.Builder builder = new AlertDialog.Builder(this);
+            builder.setMessage("Confirm suggestions before sending message");
+            builder.setPositiveButton("Confirm", new DialogInterface.OnClickListener() {
+                public void onClick(DialogInterface dialog, int id) {
+                    sendMessage();
+                }
+            });
+            builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+                public void onClick(DialogInterface dialog, int id) {
+                    // User cancelled the dialog
+                }
+            });
+
+            AlertDialog dialog = builder.create();
+            dialog.show();
+        } else {
+            sendMessage();
+        }
+    }
+
+    public void sendMessage() {
+        mMessageRef.child("group").setValue("Sent");
+        for (String device : targetDevices) {
+            mPermissionService.removeFromConstellation(device);
+        }
         finish();
     }
 
+    private void deleteMessage() {
+
+
+        if ((mPermissionManager.getPermissions(mPath) & PermissionManager.FLAG_WRITE) == PermissionManager.FLAG_WRITE) {
+            mMessageRef.removeValue();
+        }
+        finish();
+
+    }
+
+    private void pickAttachment() {
+        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+//        Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath());
+        intent.setType("*/*");
+//        intent.setDataAndType(uri, "file/*");
+        startActivityForResult(Intent.createChooser(intent, "Choose Attachment"), SELECT_ATTACHMENT);
+    }
+
 
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
@@ -169,14 +417,16 @@
 
             if (!mOwner.equals(targetDevice)) {
                 //find most appropriate blessing to extend from
-                mCastBlessing = mPermissionManager.getBlessing(mOwner, mDeviceId);
-                if (mCastBlessing == null) {
-                    mCastBlessing = mPermissionManager.getRootBlessing();
+                mSourceBlessing = mPermissionManager.getBlessing(mOwner, mDeviceId);
+                if (mSourceBlessing == null) {
+                    mSourceBlessing = mPermissionManager.getRootBlessing();
                 }
-                mCastBlessing.bless(targetDevice)
-                        .setPermissions(mPath + "/to", PermissionManager.FLAG_READ)
-                        .setPermissions(mPath + "/subject", PermissionManager.FLAG_SUGGEST)
-                        .setPermissions(mPath + "/message", PermissionManager.FLAG_SUGGEST);
+                //set default blessings
+//                Blessing deviceBlessing = mSourceBlessing.bless(targetDevice)
+//                        .setPermissions(mPath + "/to", PermissionManager.FLAG_SUGGEST)
+//                        .setPermissions(mPath + "/subject", PermissionManager.FLAG_SUGGEST)
+//                        .setPermissions(mPath + "/message", PermissionManager.FLAG_SUGGEST);
+                addTargetDevice(targetDevice);
             }
 
             JSONObject castArgs = new JSONObject();
@@ -185,12 +435,49 @@
                 castArgs.put(EXTRA_MESSAGE_PATH, mPath);
                 mPermissionService.getMessenger().to(targetDevice).emit("cast", castArgs.toString());
                 mPermissionService.updateConstellationDevice(targetDevice);
+
             } catch (JSONException e) {
                 e.printStackTrace();
             }
+        } else if (requestCode == SELECT_ATTACHMENT && resultCode == RESULT_OK) {
+            Uri attachmentUri = data.getData();
+//            String path = Utils.getRealPathFromURI(this, attachmentUri);
+            String path = Utils.getFileName(this, attachmentUri);
+//            File f = new File(path);
+            Toast.makeText(this, "Attached " + path, 0).show();
+            mMessageRef.child("attachment").setValue(path);
         }
     }
 
+    public void addTargetDevice(String id) {
+        targetDevices.add(id);
+        for (Map.Entry<String, PermissionedTextLayout> entry : mPermissionedFields.entrySet()) {
+            PermissionedTextLayout edit = entry.getValue();
+            String key = entry.getKey();
+            int current = mPublicBlessing.getPermissions(mPath + "/" + key);
+            if ((current & PermissionManager.FLAG_SUGGEST) == PermissionManager.FLAG_SUGGEST) {
+                edit.setAction(1, new IconDrawable(ComposeActivity.this, MaterialIcons.md_cancel)
+                        .color(Color.RED)
+                        .actionBarSize(), "Toggle Permission");
+            } else {
+                edit.setAction(1, new IconDrawable(ComposeActivity.this, MaterialIcons.md_cast)
+                        .color(Color.BLACK)
+                        .actionBarSize(), "Toggle Permission");
+            }
+        }
+        mAttachmentCast.setVisibility(View.VISIBLE);
+    }
+
+    public void removeTargetDevice(String id) {
+        targetDevices.remove(id);
+        if (targetDevices.size() == 0) {
+            for (Map.Entry<String, PermissionedTextLayout> entry : mPermissionedFields.entrySet()) {
+                PermissionedTextLayout edit = entry.getValue();
+                edit.clearActions();
+            }
+            mAttachmentCast.setVisibility(View.GONE);
+        }
+    }
 
     @Override
     public void onServiceConnected(ComponentName name, IBinder service) {
@@ -229,15 +516,26 @@
                 mOwner = pathElements[1];
             }
 
+            if (mMenu != null && !mOwner.equals(mDeviceId)) {
+                mMenu.findItem(R.id.action_attach).setVisible(false);
+                mMenu.findItem(R.id.action_delete).setVisible(false);
+            }
+
             mMessageRef = mPermissionService.getFirebaseDB().getReference(mPath);
             mSyncedMessageRef = mPermissionService.getFirebaseDB().getReference("documents/" + mOwner + "/emails/syncedMessages/" + mId);
             mPermissionManager.addPermissionEventListener(mPath, messagePermissionListener);
+
+            mMessageRef.child("group").addValueEventListener(groupListener);
+
+            mMessageRef.child("attachment").addValueEventListener(attachmentListener);
+            mPermissionManager.addPermissionEventListener(mPath + "/attachment", attachmentPermissionListner);
+
             initField(mTo, "to");
             initField(mFrom, "from");
             initField(mSubject, "subject");
             initField(mMessage, "message");
 
-//            mPublicBlessing = mPermissionManager.bless("public")
+            mPublicBlessing = mPermissionManager.bless("public");
 //                    .setPermissions(mPath + "/subject", PermissionManager.FLAG_READ);
 
             mPermissionManager.addOnRequestListener("documents/" + mDeviceId + "/emails/messages/" + mId + "/*", new PermissionManager.OnRequestListener() {
@@ -252,12 +550,69 @@
 
                 }
             });
-
             mPermissionService.setStatus(EXTRA_MESSAGE_PATH, mPath);
 
+            mPermissionService.addDiscoveryListener(new PermissionService.DiscoveryListener() {
+                @Override
+                public void onChange(Map<String, DeviceData> devices) {
+
+                }
+
+                @Override
+                public void onDisassociate(String deviceId) {
+                    if (deviceId.equals(mOwner)) {
+                        finish();
+                    }else{
+                        removeTargetDevice(deviceId);
+                    }
+                }
+            });
         }
     }
 
+    PermissionManager.OnPermissionChangeListener attachmentPermissionListner = new PermissionManager.OnPermissionChangeListener() {
+        @Override
+        public void onPermissionChange(int current) {
+            setAttachment(mAttachment, current);
+        }
+
+        @Override
+        public void onCancelled(DatabaseError databaseError) {
+
+        }
+    };
+
+
+    ValueEventListener groupListener = new ValueEventListener() {
+        @Override
+        public void onDataChange(DataSnapshot dataSnapshot) {
+            if (dataSnapshot.exists()) {
+                String newGroup = dataSnapshot.getValue(String.class);
+                setGroup(newGroup);
+            }
+        }
+
+        @Override
+        public void onCancelled(DatabaseError databaseError) {
+
+        }
+    };
+
+    ValueEventListener attachmentListener = new ValueEventListener() {
+        @Override
+        public void onDataChange(DataSnapshot dataSnapshot) {
+            if (dataSnapshot.exists()) {
+                String newAttachment = dataSnapshot.getValue(String.class);
+                setAttachment(newAttachment, mAttachmentPermission);
+            }
+        }
+
+        @Override
+        public void onCancelled(DatabaseError databaseError) {
+
+        }
+    };
+
     PermissionManager.OnPermissionChangeListener messagePermissionListener = new PermissionManager.OnPermissionChangeListener() {
         @Override
         public void onPermissionChange(int current) {
@@ -285,25 +640,51 @@
             public void onSelected(final SyncTextDiff diff, PermissionedTextLayout text) {
                 int current = mPermissionManager.getPermissions(mPath + "/" + key);
                 if ((current & PermissionManager.FLAG_WRITE) == PermissionManager.FLAG_WRITE) {
-                    mPermissionService.requestDialog(diff.source + "@" + key, "Apply changes from " + diff.source, "be vigilant",
+                    Intent content = new Intent(ComposeActivity.this, ComposeActivity.class);
+                    content.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+                    String name = diff.getSource();
+                    if(mPermissionService.getDiscovered().containsKey(name)){
+                        name = mPermissionService.getDiscovered().get(diff.source).getName();
+                    }
+                    mPermissionService.requestDialog(diff.source, "Apply changes from " + name, "be vigilant",
                             new PermissionService.ActionCallback() {
                                 @Override
                                 public boolean onAction(Intent intent) {
-                                    edit.acceptSuggestions(diff.source);
+                                    acceptSuggestions(diff.source);
                                     return true;
                                 }
                             }, new PermissionService.ActionCallback() {
                                 @Override
                                 public boolean onAction(Intent intent) {
-                                    edit.rejectSuggestions(diff.source);
+                                    rejectSuggestions(diff.source);
                                     return true;
                                 }
-                            });
+                            }, content);
                 }
             }
 
             @Override
             public void onAction(int action, PermissionedTextLayout text) {
+                if (action == 1) {
+                    if (mPublicBlessing != null) {
+                        int current = mPublicBlessing.getPermissions(mPath + "/" + key);
+                        if ((current & PermissionManager.FLAG_SUGGEST) == PermissionManager.FLAG_SUGGEST) {
+                            mPublicBlessing.setPermissions(mPath + "/" + key, current & ~PermissionManager.FLAG_SUGGEST);
+                            text.setAction(1, new IconDrawable(ComposeActivity.this, MaterialIcons.md_cast)
+                                    .color(Color.BLACK)
+                                    .actionBarSize(), "Toggle Permission");
+                        } else {
+                            mPublicBlessing.setPermissions(mPath + "/" + key, current | PermissionManager.FLAG_SUGGEST);
+                            text.setAction(1, new IconDrawable(ComposeActivity.this, MaterialIcons.md_cancel)
+                                    .color(Color.RED)
+                                    .actionBarSize(), "Toggle Permission");
+                        }
+                    }
+                } else if (action == 2) {
+                    mPermissionManager.request(mPath + "/to", mDeviceId)
+                            .setPermissions(PermissionManager.FLAG_WRITE)
+                            .udpate();
+                }
             }
         });
 
@@ -314,6 +695,21 @@
             @Override
             public void onPermissionChange(int current) {
                 edit.onPermissionChange(current);
+
+                //TODO:generalize the following request button
+                if ("to".equals(key)) {
+                    if (current == PermissionManager.FLAG_READ) {
+                        edit.setAction(2, new IconDrawable(ComposeActivity.this, MaterialIcons.md_vpn_key), "Request Permission");
+                    } else {
+                        edit.removeAction(2);
+                    }
+
+                    if ((current & PermissionManager.FLAG_WRITE) == PermissionManager.FLAG_WRITE) {
+                        edit.setAutoCompleteAdapter(contactAdapter);
+                    } else {
+                        edit.setAutoCompleteAdapter(null);
+                    }
+                }
             }
 
             @Override
@@ -323,10 +719,24 @@
         });
     }
 
+    public void acceptSuggestions(String src) {
+        for (PermissionedTextLayout text : mPermissionedFields.values()) {
+            text.acceptSuggestions(src);
+        }
+    }
+
+    public void rejectSuggestions(String src) {
+        for (PermissionedTextLayout text : mPermissionedFields.values()) {
+            text.rejectSuggestions(src);
+        }
+    }
+
     public void unlink() {
         for (PermissionedTextLayout text : mPermissionedFields.values()) {
             text.unlink();
         }
+        mMessageRef.child("attachment").removeEventListener(attachmentListener);
+        mMessageRef.child("group").removeEventListener(groupListener);
         if (mPermissionService != null) {
             if (mPublicBlessing != null) {
                 mPublicBlessing.revokePermissions(mPath);
@@ -335,6 +745,7 @@
             //cancel all requests made from this activity
             mPermissionManager.cancelRequests(mDeviceId + mId);
         }
+
         unbindService(this);
     }
 
diff --git a/permissions/app/src/main/java/examples/baku/io/permissions/examples/EmailActivity.java b/permissions/app/src/main/java/examples/baku/io/permissions/examples/EmailActivity.java
index e152faa..3d98a0d 100644
--- a/permissions/app/src/main/java/examples/baku/io/permissions/examples/EmailActivity.java
+++ b/permissions/app/src/main/java/examples/baku/io/permissions/examples/EmailActivity.java
@@ -11,6 +11,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.support.design.widget.FloatingActionButton;
+import android.support.v4.widget.DrawerLayout;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.CardView;
 import android.support.v7.widget.LinearLayoutManager;
@@ -23,6 +24,9 @@
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
 import android.widget.TextView;
 
 import com.google.common.collect.Iterables;
@@ -33,6 +37,9 @@
 import com.google.firebase.database.DatabaseReference;
 import com.google.firebase.database.FirebaseDatabase;
 import com.google.firebase.database.ValueEventListener;
+import com.joanzapata.iconify.IconDrawable;
+import com.joanzapata.iconify.Iconify;
+import com.joanzapata.iconify.fonts.MaterialIcons;
 import com.joanzapata.iconify.widget.IconTextView;
 
 import org.json.JSONException;
@@ -40,6 +47,8 @@
 
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.UUID;
 
 import examples.baku.io.permissions.Blessing;
 import examples.baku.io.permissions.PermissionManager;
@@ -52,6 +61,8 @@
 
 
     private static final String TAG = PermissionService.class.getSimpleName();
+    private DrawerLayout mDrawerLayout;
+    private ItemTouchHelper mItemTouchHelper;
 
     static void l(String msg) {
         Log.e(TAG, msg);
@@ -67,6 +78,8 @@
     private FirebaseDatabase mFirebaseDB;
     private DatabaseReference mMessagesRef;
 
+    private Toolbar mToolbar;
+
     private RecyclerView mInboxRecyclerView;
     private MessagesAdapter mInboxAdapter;
     private LinearLayoutManager mLayoutManager;
@@ -75,22 +88,35 @@
 
     private ArrayList<String> mMessageOrder = new ArrayList<>();
 
+    private ListView mDrawerList;
+    private String[] mGroupList = new String[]{"Inbox", "Sent", "Drafts"};
+    private String mGroup = "Inbox";
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_permission);
-        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
-        toolbar.setTitle("Inbox");
-        setSupportActionBar(toolbar);
+        mToolbar = (Toolbar) findViewById(R.id.toolbar);
+        mToolbar.setTitle(mGroup);
+        setSupportActionBar(mToolbar);
+        mToolbar.setNavigationIcon(
+                new IconDrawable(this, MaterialIcons.md_menu)
+                        .color(Color.WHITE)
+                        .actionBarSize());
+        mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mDrawerLayout.openDrawer(mDrawerList);
+                mDrawerLayout.openDrawer(mDrawerList);
+            }
+        });
 
         FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
         if (fab != null) {
             fab.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View view) {
-                    startActivity(new Intent(EmailActivity.this, ComposeActivity.class));
-//                    mPermissionService.getDeviceBlessing().setPermissions("documents/" + mDeviceId +"/snake", new Random().nextInt());
+                    ComposeActivity.launchAndCreateMessage(EmailActivity.this, mMessagesRef, "", "myself@email.com", "", "", null, "Drafts");
 
                 }
             });
@@ -99,18 +125,47 @@
         PermissionService.start(this);
         PermissionService.bind(this);
 
+        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+        mDrawerList = (ListView) findViewById(R.id.left_drawer);
+        mDrawerList.setAdapter(new ArrayAdapter<String>(this,
+                R.layout.navigation_menu_item, R.id.navigationItemText, mGroupList));
+        // Set the list's click listener
+        mDrawerList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                mDrawerList.setSelection(position);
+                mDrawerLayout.closeDrawers();
+                setGroup(mGroupList[position]);
+            }
+        });
+        mDrawerList.setSelection(0);
+
+
         mInboxAdapter = new MessagesAdapter(mMessages);
         mLayoutManager = new LinearLayoutManager(this);
         mInboxRecyclerView = (RecyclerView) findViewById(R.id.inboxRecyclerView);
         mInboxRecyclerView.setLayoutManager(mLayoutManager);
         mInboxRecyclerView.setAdapter(mInboxAdapter);
 
-        ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
+        //add swipe behavior
+        mItemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
+//        itemTouchHelper.attachToRecyclerView(mInboxRecyclerView);
 
-        itemTouchHelper.attachToRecyclerView(mInboxRecyclerView);
-
+        setGroup(mGroup);
     }
 
+    public void setGroup(String group) {
+        mGroup = group;
+        mToolbar.setTitle(group);
+        mInboxAdapter.setGroup(group);
+        if ("Sent".equals(group)) {
+            mItemTouchHelper.attachToRecyclerView(mInboxRecyclerView);
+        } else {
+            mItemTouchHelper.attachToRecyclerView(null);
+        }
+    }
+
+
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         // Inflate the menu; this adds items to the action bar if it is present.
@@ -130,10 +185,15 @@
 
             return true;
         }
-
         return super.onOptionsItemSelected(item);
     }
 
+    MessageData addNewMessage(String group) {
+        MessageData msg = new MessageData(UUID.randomUUID().toString(), "", "", "", "", null, group);
+        mMessagesRef.child(msg.getId()).setValue(msg);
+        return msg;
+    }
+
     ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT) {
         @Override
         public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
@@ -167,7 +227,7 @@
             mPermissionManager.addOnRequestListener("documents/" + mDeviceId + "/emails/messages/*", new PermissionManager.OnRequestListener() {
                 @Override
                 public boolean onRequest(PermissionRequest request, Blessing blessing) {
-                    mInboxAdapter.notifyDataSetChanged();
+                    mInboxAdapter.refreshDataSet();
                     return true;
                 }
 
@@ -192,19 +252,19 @@
         @Override
         public void onChildAdded(DataSnapshot dataSnapshot, String s) {
             onMessageUpdated(dataSnapshot);
-            mInboxAdapter.notifyDataSetChanged();
+            mInboxAdapter.refreshDataSet();
         }
 
         @Override
         public void onChildChanged(DataSnapshot dataSnapshot, String s) {
             onMessageUpdated(dataSnapshot);
-            mInboxAdapter.notifyDataSetChanged();
+            mInboxAdapter.refreshDataSet();
         }
 
         @Override
         public void onChildRemoved(DataSnapshot dataSnapshot) {
             onMessageRemoved(dataSnapshot.getKey());
-            mInboxAdapter.notifyDataSetChanged();
+            mInboxAdapter.refreshDataSet();
         }
 
         @Override
@@ -222,7 +282,7 @@
         @Override
         public void onDataChange(DataSnapshot dataSnapshot) {
             onMessagesUpdated(dataSnapshot);
-            mInboxAdapter.notifyDataSetChanged();
+            mInboxAdapter.refreshDataSet();
         }
 
         @Override
@@ -298,18 +358,38 @@
     }
 
     public class MessagesAdapter extends RecyclerView.Adapter<ViewHolder> {
-        private LinkedHashMap<String, MessageData> mDataset;
+
+        private LinkedHashMap<String, MessageData> mRawDataSet;
+        private LinkedHashMap<String, MessageData> mDataSet = new LinkedHashMap<>();
+
+        private String mGroup;
 
         public MessagesAdapter(LinkedHashMap<String, MessageData> dataset) {
             setDataset(dataset);
         }
 
         public void setDataset(LinkedHashMap<String, MessageData> mDataset) {
-            this.mDataset = mDataset;
+            this.mRawDataSet = mDataset;
+            refreshDataSet();
+        }
+
+        public void refreshDataSet() {
+            mDataSet.clear();
+            for (Map.Entry<String, MessageData> entry : mRawDataSet.entrySet()) {
+                if (mGroup == null || entry.getValue().getGroup().equals(mGroup)) {
+                    mDataSet.put(entry.getKey(), entry.getValue());
+                }
+            }
+            notifyDataSetChanged();
         }
 
         public MessageData getItem(int position) {
-            return Iterables.get(mDataset.values(), position);
+            return Iterables.get(mDataSet.values(), position);
+        }
+
+        public void setGroup(String group) {
+            this.mGroup = group;
+            refreshDataSet();
         }
 
         @Override
@@ -328,30 +408,35 @@
 
             final MessageData item = getItem(position);
 
-            String title = item.getFrom();
-            if (title != null) {
-                TextView titleView = (TextView) holder.mCardView.findViewById(R.id.card_title);
-                titleView.setText(item.getFrom());
-                TextView subtitleView = (TextView) holder.mCardView.findViewById(R.id.card_subtitle);
-                subtitleView.setText(item.getSubject());
+            String title = "";
+            if ("Inbox".equals(mGroup)) {
+                title = item.getFrom();
+            } else {
+                title = item.getTo();
             }
+            String subtitle = item.getSubject();
+            TextView titleView = (TextView) holder.mCardView.findViewById(R.id.card_title);
+            titleView.setText(title);
+            TextView subtitleView = (TextView) holder.mCardView.findViewById(R.id.card_subtitle);
+            subtitleView.setText(subtitle);
 
             IconTextView castButton = (IconTextView) holder.mCardView.findViewById(R.id.card_trailing);
-            castButton.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    //choose device
-                    Intent requestIntent = new Intent(EmailActivity.this, DevicePickerActivity.class);
-                    String path = EmailActivity.KEY_DOCUMENTS
-                            + "/" + mDeviceId
-                            + "/" + EmailActivity.KEY_EMAILS
-                            + "/" + EmailActivity.KEY_MESSAGES
-                            + "/" + item.getId();
-                    requestIntent.putExtra(DevicePickerActivity.EXTRA_REQUEST, DevicePickerActivity.REQUEST_DEVICE_ID);
-                    requestIntent.putExtra(DevicePickerActivity.EXTRA_REQUEST_ARGS, path);
-                    startActivityForResult(requestIntent, DevicePickerActivity.REQUEST_DEVICE_ID);
-                }
-            });
+            castButton.setVisibility(View.GONE);
+//            castButton.setOnClickListener(new View.OnClickListener() {
+//                @Override
+//                public void onClick(View v) {
+//                    //choose device
+//                    Intent requestIntent = new Intent(EmailActivity.this, DevicePickerActivity.class);
+//                    String path = EmailActivity.KEY_DOCUMENTS
+//                            + "/" + mDeviceId
+//                            + "/" + EmailActivity.KEY_EMAILS
+//                            + "/" + EmailActivity.KEY_MESSAGES
+//                            + "/" + item.getId();
+//                    requestIntent.putExtra(DevicePickerActivity.EXTRA_REQUEST, DevicePickerActivity.REQUEST_DEVICE_ID);
+//                    requestIntent.putExtra(DevicePickerActivity.EXTRA_REQUEST_ARGS, path);
+//                    startActivityForResult(requestIntent, DevicePickerActivity.REQUEST_DEVICE_ID);
+//                }
+//            });
 
             holder.mCardView.setOnClickListener(new View.OnClickListener() {
                 @Override
@@ -376,7 +461,7 @@
 
         @Override
         public int getItemCount() {
-            return mDataset.size();
+            return mDataSet.size();
         }
     }
 
@@ -395,4 +480,6 @@
         super.onDestroy();
         unbindService(this);
     }
+
+
 }
\ No newline at end of file
diff --git a/permissions/app/src/main/java/examples/baku/io/permissions/examples/MessageData.java b/permissions/app/src/main/java/examples/baku/io/permissions/examples/MessageData.java
index 12d2793..45181f3 100644
--- a/permissions/app/src/main/java/examples/baku/io/permissions/examples/MessageData.java
+++ b/permissions/app/src/main/java/examples/baku/io/permissions/examples/MessageData.java
@@ -18,18 +18,23 @@
     String from = "";
     String subject = "";
     String message = "";
+    String group = "Drafts";
+    String attachment;
     long timeStamp;
 
 //    Map<String, Map<String, Integer>> shared = new HashMap<>();
 
-    public MessageData(){}
+    public MessageData() {
+    }
 
-    public MessageData(String id, String to, String from, String subject, String message) {
+    public MessageData(String id, String to, String from, String subject, String message, String attachment, String group) {
         this.id = id;
         this.to = to;
         this.from = from;
         this.subject = subject;
         this.message = message;
+        this.attachment = attachment;
+        this.group = group;
     }
 
     public String getId() {
@@ -72,8 +77,23 @@
         this.message = message != null ? message : "";
     }
 
+    public String getGroup() {
+        return group;
+    }
 
-    public Map<String,String> getTimeStamp() {
+    public void setGroup(String group) {
+        this.group = group;
+    }
+
+    public String getAttachment() {
+        return attachment;
+    }
+
+    public void setAttachment(String attachment) {
+        this.attachment = attachment;
+    }
+
+    public Map<String, String> getTimeStamp() {
         return ServerValue.TIMESTAMP;
     }
 
diff --git a/permissions/app/src/main/java/examples/baku/io/permissions/examples/ProcessTextActivity.java b/permissions/app/src/main/java/examples/baku/io/permissions/examples/ProcessTextActivity.java
new file mode 100644
index 0000000..0c29ca7
--- /dev/null
+++ b/permissions/app/src/main/java/examples/baku/io/permissions/examples/ProcessTextActivity.java
@@ -0,0 +1,19 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package examples.baku.io.permissions.examples;
+
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.widget.Toast;
+
+public class ProcessTextActivity extends AppCompatActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Toast.makeText(this, "No Cast Target", 0).show();
+        finish();
+    }
+}
diff --git a/permissions/app/src/main/java/examples/baku/io/permissions/synchronization/SyncText.java b/permissions/app/src/main/java/examples/baku/io/permissions/synchronization/SyncText.java
index 7d25511..2dd560d 100644
--- a/permissions/app/src/main/java/examples/baku/io/permissions/synchronization/SyncText.java
+++ b/permissions/app/src/main/java/examples/baku/io/permissions/synchronization/SyncText.java
@@ -111,7 +111,7 @@
         this.mOnTextChangeListener = onTextChangeListener;
     }
 
-    public int update(String newText) {
+    public int update(String newText, int ver) {
         if (mPatchesRef == null) {
             throw new RuntimeException("database connection hasn't been initialized");
         }
@@ -121,7 +121,7 @@
         if (patches.size() > 0) {
             String patchString = diffMatchPatch.patchToText(patches);
             SyncTextPatch patch = new SyncTextPatch();
-            patch.setVer(ver + 1);
+            patch.setVer(ver);
             patch.setPatch(patchString);
             if (mLocalSource != null) {
                 patch.setSource(mLocalSource);
@@ -133,6 +133,16 @@
         return -1;
     }
 
+    public int update(String newText) {
+        return update(newText, ver + 1);
+    }
+
+    private LinkedList<SyncTextDiff> toDiffs(String text) {
+        LinkedList<SyncTextDiff> diffs = new LinkedList<>();
+        diffs.add(new SyncTextDiff(text, SyncTextDiff.EQUAL, mLocalSource, mPermissions));
+        return diffs;
+    }
+
     //TODO: this method currently waits for server confirmation to notify listeners. Ideally, it should notify immediately and revert on failure
     private void updateCurrent(final int ver, final LinkedList<SyncTextDiff> diffs) {
         final String text = getFinalText(diffs);
@@ -184,38 +194,61 @@
         mSyncRef.child(KEY_SUBSCRIBERS).child(mInstanceId).setValue(0);
 
         mPatchesRef = mSyncRef.child(KEY_PATCHES);
-        mSyncRef.child(KEY_CURRENT).addListenerForSingleValueEvent(new ValueEventListener() {
-            @Override
-            public void onDataChange(DataSnapshot dataSnapshot) {
-                if (dataSnapshot.exists()) {
-                    if (dataSnapshot.hasChild(KEY_DIFFS)) {
-                        diffs = new LinkedList<SyncTextDiff>(dataSnapshot.child(KEY_DIFFS).getValue(diffListType));
-                    }
-                    ver = dataSnapshot.child(KEY_VERSION).getValue(Integer.class);
-                } else {  //version 0, empty string
-                    updateCurrent(0, new LinkedList<>(diffs));
-                }
+        if (mOutputRef != null) {
+            mOutputRef.addListenerForSingleValueEvent(pullCurrentOutput);
+        } else {
+            mSyncRef.child(KEY_CURRENT).addListenerForSingleValueEvent(mInitValueListener);
+        }
+    }
 
-                notifyListeners(diffs, ver);
+    private ValueEventListener mInitValueListener = new ValueEventListener() {
+        @Override
+        public void onDataChange(DataSnapshot dataSnapshot) {
+            if (dataSnapshot.exists()) {
+                if (dataSnapshot.hasChild(KEY_DIFFS)) {
+                    diffs = new LinkedList<SyncTextDiff>(dataSnapshot.child(KEY_DIFFS).getValue(diffListType));
+                }
+                ver = dataSnapshot.child(KEY_VERSION).getValue(Integer.class);
+            } else {  //version 0, empty string
+                updateCurrent(0, new LinkedList<>(diffs));
+            }
+            notifyListeners(diffs, ver);
 
 //                mPatchesRef.orderByChild(KEY_VERSION).startAt(ver).addChildEventListener(mPatchListener);
-                mPatchesRef.addChildEventListener(mPatchListener);
-                mSyncRef.child(KEY_CURRENT).addValueEventListener(mCurrentValueListener);
-            }
+            mPatchesRef.addChildEventListener(mPatchListener);
+            mSyncRef.child(KEY_CURRENT).addValueEventListener(mCurrentValueListener);
+        }
 
-            @Override
-            public void onCancelled(DatabaseError databaseError) {
+        @Override
+        public void onCancelled(DatabaseError databaseError) {
 
+        }
+    };
+
+    private ValueEventListener pullCurrentOutput = new ValueEventListener() {
+        @Override
+        public void onDataChange(DataSnapshot dataSnapshot) {
+            if (dataSnapshot.exists()) {
+                String atOutput = dataSnapshot.getValue(String.class);
+                if (atOutput != null) {
+                    diffs = toDiffs(atOutput);
+                }
             }
-        });
-    }
+            mSyncRef.child(KEY_CURRENT).addListenerForSingleValueEvent(mInitValueListener);
+        }
+
+        @Override
+        public void onCancelled(DatabaseError databaseError) {
+
+        }
+    };
 
     private ValueEventListener mCurrentValueListener = new ValueEventListener() {
         @Override
         public void onDataChange(DataSnapshot dataSnapshot) {
             if (dataSnapshot.exists()) {
                 int version = dataSnapshot.child(KEY_VERSION).getValue(Integer.class);
-                if (version > ver && dataSnapshot.hasChild(KEY_DIFFS)) {
+                if (dataSnapshot.hasChild(KEY_DIFFS)) {
                     ver = version;
                     diffs = new LinkedList<SyncTextDiff>(dataSnapshot.child(KEY_DIFFS).getValue(diffListType));
                     notifyListeners(diffs, ver);
@@ -309,7 +342,7 @@
     }
 
     //TODO: bug when duplicate letter patterns in the text. The diff algorithm doesn't take source into account.
-    //TODO: this method doesn't handle delete operations on diffs with different sources (e.g. deleting a suggestion from another source), these operations are currently ignored
+    //TODO: this method doesn't handle delete operations on diffs with different sources (e.g. deleting another sources suggestion), these operations are currently ignored
     void processPatch(SyncTextPatch patch) {
         int v = patch.getVer();
         if (this.ver >= v) {  //ignore patches for previous versions
@@ -345,17 +378,14 @@
             switch (operation) {
                 case SyncTextDiff.EQUAL:
                     length = value.length();
-                    while (previousDiff.length() < length) {
+                    while (previousDiff.length() <= length) {
                         result.add(previousDiff);
                         length -= previousDiff.length();
-                        previousDiff = previousIterator.next();
-                    }
-                    if (previousDiff.length() == length) {
-                        result.add(previousDiff);
                         if (previousIterator.hasNext()) {
                             previousDiff = previousIterator.next();
                         }
-                    } else {
+                    }
+                    if (length > 0) {
                         SyncTextDiff splitDiff = previousDiff.truncate(length);
                         result.add(previousDiff);
                         previousDiff = splitDiff;
@@ -380,7 +410,6 @@
                             result.add(previousDiff);
                         }
                         length -= previousDiff.length();
-
                         if (previousIterator.hasNext()) {
                             previousDiff = previousIterator.next();
                         }
@@ -429,20 +458,25 @@
 
     public void acceptSuggestions(String source) {
         LinkedList<SyncTextDiff> result = new LinkedList<>(diffs);
+        boolean change = false;
         for (Iterator<SyncTextDiff> iterator = result.iterator(); iterator.hasNext(); ) {
             SyncTextDiff diff = iterator.next();
             if (diff.source.equals(source)) {
                 switch (diff.operation) {
                     case SyncTextDiff.DELETE:
                         iterator.remove();
+                        change = true;
                         break;
-                    default:
+                    case SyncTextDiff.INSERT:
+                        change = true;
                         diff.operation = SyncTextDiff.EQUAL;
                         break;
                 }
             }
         }
-        updateCurrent(ver + 1, result);
+        if (change) {
+            updateCurrent(ver + 1, result);
+        }
     }
 
     public void rejectSuggestions() {
diff --git a/permissions/app/src/main/java/examples/baku/io/permissions/synchronization/SyncTextDiff.java b/permissions/app/src/main/java/examples/baku/io/permissions/synchronization/SyncTextDiff.java
index 6f4487e..076d3f6 100644
--- a/permissions/app/src/main/java/examples/baku/io/permissions/synchronization/SyncTextDiff.java
+++ b/permissions/app/src/main/java/examples/baku/io/permissions/synchronization/SyncTextDiff.java
@@ -77,7 +77,7 @@
         return text == null ? 0 : text.length();
     }
 
-    public boolean compatible(SyncTextDiff other){
+    public boolean compatible(SyncTextDiff other) {
         return compatible(other.operation, other.source);
     }
 
diff --git a/permissions/app/src/main/java/examples/baku/io/permissions/util/Utils.java b/permissions/app/src/main/java/examples/baku/io/permissions/util/Utils.java
index 6fbcc31..6e1e53d 100644
--- a/permissions/app/src/main/java/examples/baku/io/permissions/util/Utils.java
+++ b/permissions/app/src/main/java/examples/baku/io/permissions/util/Utils.java
@@ -4,11 +4,20 @@
 
 package examples.baku.io.permissions.util;
 
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Build;
+import android.provider.DocumentsContract;
+import android.provider.MediaStore;
+import android.provider.OpenableColumns;
 
 import com.joanzapata.iconify.IconDrawable;
 
@@ -56,4 +65,44 @@
 
         return null;
     }
+
+    public static String getFileName(Context context, Uri uri) {
+        String result = null;
+        if (uri.getScheme().equals("content")) {
+            Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
+            try {
+                if (cursor != null && cursor.moveToFirst()) {
+                    result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
+                }
+            } finally {
+                cursor.close();
+            }
+        }
+        if (result == null) {
+            result = uri.getPath();
+            int cut = result.lastIndexOf('/');
+            if (cut != -1) {
+                result = result.substring(cut + 1);
+            }
+        }
+        return result;
+    }
+
+    public static void viewAttachment(Context context, String path) {
+        String url = "http://d39kbiy71leyho.cloudfront.net/wp-content/uploads/2016/05/09170020/cats-politics-TN.jpg";
+        Intent i = new Intent(Intent.ACTION_VIEW);
+        i.setData(Uri.parse(url));
+        context.startActivity(i);
+    }
+
+    public static void viewImage(Context context, String path) {
+        Intent intent = new Intent();
+        intent.setAction(Intent.ACTION_VIEW);
+        intent.setDataAndType(Uri.parse("file://" + "/sdcard/" + path), "image/*");
+        context.startActivity(intent);
+    }
+
+    public static boolean isEmulator() {
+        return "google_sdk".equals(Build.PRODUCT) || "sdk".equals(Build.PRODUCT) || "sdk_x86".equals(Build.PRODUCT) || "vbox86p".equals(Build.PRODUCT);
+    }
 }
diff --git a/permissions/app/src/main/res/drawable-hdpi/navigate_selector.xml b/permissions/app/src/main/res/drawable-hdpi/navigate_selector.xml
new file mode 100644
index 0000000..490400c
--- /dev/null
+++ b/permissions/app/src/main/res/drawable-hdpi/navigate_selector.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@color/common_plus_signin_btn_text_light_focused" android:state_activated="true" />
+</selector>
diff --git a/permissions/app/src/main/res/drawable-hdpi/permission_overlay_bg.xml b/permissions/app/src/main/res/drawable-hdpi/permission_overlay_bg.xml
new file mode 100644
index 0000000..e545e94
--- /dev/null
+++ b/permissions/app/src/main/res/drawable-hdpi/permission_overlay_bg.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#CCCCCC"/>
+    <stroke android:width="3dip" android:color="#B1BCBE" />
+    <corners android:radius="10dip"/>
+    <padding android:left="0dip" android:top="0dip" android:right="0dip" android:bottom="0dip" />
+</shape>
\ No newline at end of file
diff --git a/permissions/app/src/main/res/layout/content_compose.xml b/permissions/app/src/main/res/layout/content_compose.xml
index a869c90..feab722 100644
--- a/permissions/app/src/main/res/layout/content_compose.xml
+++ b/permissions/app/src/main/res/layout/content_compose.xml
@@ -1,9 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:orientation="vertical"
     android:paddingBottom="@dimen/activity_vertical_margin"
     android:paddingLeft="@dimen/activity_horizontal_margin"
     android:paddingRight="@dimen/activity_horizontal_margin"
@@ -13,39 +14,67 @@
     tools:showIn="@layout/activity_compose">
 
 
-        <examples.baku.io.permissions.PermissionedTextLayout
-            android:id="@+id/composeTo"
+    <examples.baku.io.permissions.PermissionedTextLayout
+        android:id="@+id/composeTo"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:hint="To"
+        android:inputType="textEmailAddress" />
+
+    <examples.baku.io.permissions.PermissionedTextLayout
+        android:id="@+id/composeFrom"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:hint="From"
+        android:inputType="textEmailAddress" />
+
+
+    <examples.baku.io.permissions.PermissionedTextLayout
+        android:id="@+id/composeSubject"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:hint="Subject"
+        android:inputType="textEmailSubject" />
+
+
+    <RelativeLayout
+        android:id="@+id/composeAttachment"
+        android:layout_width="match_parent"
+        android:layout_height="45dp"
+        android:background="#eee">
+
+        <ImageView
+            android:id="@+id/composeAttachmentIcon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:padding="5dp"
+            android:src="@drawable/ic_check_black_24dp" />
+
+        <TextView
+            android:id="@+id/composeAttachmentText"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:inputType="textEmailAddress"
-            android:hint="To" />
-
-        <examples.baku.io.permissions.PermissionedTextLayout
-            android:id="@+id/composeFrom"
-            android:layout_width="match_parent"
+            android:text="http://about.com"
+            android:textSize="20dp"
+            android:layout_centerVertical="true"
+            android:layout_toEndOf="@+id/composeAttachmentIcon" />
+        <ImageView
+            android:id="@+id/composeAttachmentCast"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:inputType="textEmailAddress"
-            android:layout_below="@id/composeTo"
-            android:hint="From" />
+            android:layout_centerVertical="true"
+            android:layout_alignParentRight="true"
+            android:padding="5dp"
+            android:src="@drawable/ic_cast_black_24dp" />
+    </RelativeLayout>
+
+    <examples.baku.io.permissions.PermissionedTextLayout
+        android:id="@+id/composeMessage"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:hint="Message"
+        android:inputType="textMultiLine" />
 
 
-        <examples.baku.io.permissions.PermissionedTextLayout
-            android:id="@+id/composeSubject"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:inputType="textEmailSubject"
-            android:layout_below="@id/composeFrom"
-            android:hint="Subject" />
-
-
-        <examples.baku.io.permissions.PermissionedTextLayout
-            android:id="@+id/composeMessage"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:inputType="textMultiLine"
-            android:layout_below="@id/composeSubject"
-            android:hint="Message" />
-
-
-
-</RelativeLayout>
+</LinearLayout>
diff --git a/permissions/app/src/main/res/layout/inbox_list.xml b/permissions/app/src/main/res/layout/inbox_list.xml
index a10261a..59be2dd 100644
--- a/permissions/app/src/main/res/layout/inbox_list.xml
+++ b/permissions/app/src/main/res/layout/inbox_list.xml
@@ -1,20 +1,38 @@
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
+<android.support.v4.widget.DrawerLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:paddingBottom="@dimen/activity_vertical_margin"
-    android:paddingLeft="@dimen/activity_horizontal_margin"
-    android:paddingRight="@dimen/activity_horizontal_margin"
-    android:paddingTop="@dimen/activity_vertical_margin"
+    android:id="@+id/drawer_layout"
     app:layout_behavior="@string/appbar_scrolling_view_behavior"
-    tools:context=".examples.InboxFragment"
-    tools:showIn="@layout/activity_permission">
-
-    <android.support.v7.widget.RecyclerView
-        android:id="@+id/inboxRecyclerView"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <!-- The main content view -->
+    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:tools="http://schemas.android.com/tools"
         android:layout_width="match_parent"
-        android:layout_height="match_parent">
-    </android.support.v7.widget.RecyclerView>
+        android:layout_height="match_parent"
+        android:paddingBottom="@dimen/activity_vertical_margin"
+        android:paddingLeft="@dimen/activity_horizontal_margin"
+        android:paddingRight="@dimen/activity_horizontal_margin"
+        android:paddingTop="@dimen/activity_vertical_margin"
+        tools:context=".examples.InboxFragment"
+        tools:showIn="@layout/activity_permission">
 
-</RelativeLayout>
+        <android.support.v7.widget.RecyclerView
+            android:id="@+id/inboxRecyclerView"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+        </android.support.v7.widget.RecyclerView>
+
+    </RelativeLayout>
+    <!-- The navigation drawer -->
+    <ListView android:id="@+id/left_drawer"
+        android:layout_width="240dp"
+        android:layout_height="match_parent"
+        android:layout_gravity="start"
+        android:choiceMode="singleChoice"
+        android:divider="@color/common_action_bar_splitter"
+        android:dividerHeight="1dp"
+        android:background="@color/cardview_light_background"/>
+</android.support.v4.widget.DrawerLayout>
+
+
diff --git a/permissions/app/src/main/res/layout/navigation_menu_item.xml b/permissions/app/src/main/res/layout/navigation_menu_item.xml
new file mode 100644
index 0000000..9b51944
--- /dev/null
+++ b/permissions/app/src/main/res/layout/navigation_menu_item.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2016 The Vanadium Authors. All rights reserved. -->
+<!-- Use of this source code is governed by a BSD-style -->
+<!-- license that can be found in the LICENSE file. -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@drawable/navigate_selector">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="70dp"
+        android:textSize="25dp"
+        android:text=""
+        android:id="@+id/navigationItemText"
+        android:layout_marginStart="25dp"
+        android:paddingTop="15dp"
+        android:layout_alignParentStart="true" />
+</RelativeLayout>
\ No newline at end of file
diff --git a/permissions/app/src/main/res/menu/menu_compose.xml b/permissions/app/src/main/res/menu/menu_compose.xml
index 1b1c832..d5de27a 100644
--- a/permissions/app/src/main/res/menu/menu_compose.xml
+++ b/permissions/app/src/main/res/menu/menu_compose.xml
@@ -13,11 +13,16 @@
         android:orderInCategory="100"
         android:icon="@android:drawable/ic_delete"
         app:showAsAction="never" />
-
     <item
-        android:id="@+id/action_send"
-        android:title="Send Message"
+        android:id="@+id/action_attach"
+        android:title="Attach File"
         android:orderInCategory="100"
-        android:icon="@android:drawable/ic_menu_send"
-        app:showAsAction="always" />
+        android:icon="@android:drawable/ic_input_get"
+        app:showAsAction="always"/>
+    <!--<item-->
+        <!--android:id="@+id/action_send"-->
+        <!--android:title="Send Message"-->
+        <!--android:orderInCategory="100"-->
+        <!--android:icon="@android:drawable/ic_menu_send"-->
+        <!--app:showAsAction="always" />-->
 </menu>
\ No newline at end of file
diff --git a/permissions/app/src/main/res/values/attrs.xml b/permissions/app/src/main/res/values/attrs.xml
index e3a1235..bcad62a 100644
--- a/permissions/app/src/main/res/values/attrs.xml
+++ b/permissions/app/src/main/res/values/attrs.xml
@@ -7,4 +7,4 @@
             <enum name="right" value="1" />
         </attr>
     </declare-styleable>
-</resources>
\ No newline at end of file
+</resources>