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>