Todos: Use the new invitations API to reduce the number of scans / advertisements.
Change-Id: Ica497cdbcd0fa8da90d07b05ad221062ba360e4b
diff --git a/app/src/syncbase/java/io/v/todos/persistence/syncbase/SyncbaseMain.java b/app/src/syncbase/java/io/v/todos/persistence/syncbase/SyncbaseMain.java
index c107310..de345b3 100644
--- a/app/src/syncbase/java/io/v/todos/persistence/syncbase/SyncbaseMain.java
+++ b/app/src/syncbase/java/io/v/todos/persistence/syncbase/SyncbaseMain.java
@@ -120,7 +120,6 @@
Permissions permissions = Util.filterPermissionsByTags(
computePermissionsFromBlessings(getPersonalBlessings()),
io.v.v23.services.syncbase.Constants.ALL_COLLECTION_TAGS);
-
Futures.addCallback(listCollection.create(getVContext(), permissions),
new SyncTrappingCallback<Void>() {
@Override
diff --git a/app/src/syncbase/java/io/v/todos/persistence/syncbase/SyncbasePersistence.java b/app/src/syncbase/java/io/v/todos/persistence/syncbase/SyncbasePersistence.java
index 8622abc..88b0bfb 100644
--- a/app/src/syncbase/java/io/v/todos/persistence/syncbase/SyncbasePersistence.java
+++ b/app/src/syncbase/java/io/v/todos/persistence/syncbase/SyncbasePersistence.java
@@ -89,6 +89,10 @@
* TODO(rosswang): Move most of this to vanadium-android.
*/
public class SyncbasePersistence implements Persistence {
+ public static final String LISTS_PREFIX = "lists_";
+ public static final String
+ LIST_COLLECTION_SYNCGROUP_PREFIX = "list_";
+
private static final String
TAG = "SyncbasePersistence",
FILENAME = "syncbase",
@@ -96,11 +100,9 @@
DATABASE = "db",
BLESSINGS_KEY = "blessings",
USER_COLLECTION_SYNCGROUP_SUFFIX = "sg_",
- LIST_COLLECTION_SYNCGROUP_SUFFIX = "list_",
DEFAULT_APP_BLESSING_STRING = "dev.v" +
".io:o:608941808256-43vtfndets79kf5hac8ieujto8837660" +
".apps.googleusercontent.com";
- protected static final String LISTS_PREFIX = "lists_";
protected static final long
SHORT_TIMEOUT = 2500,
MEMBER_TIMER_DELAY = 100,
@@ -423,7 +425,7 @@
}
protected static String computeListSyncgroupName(String listId) {
- return LIST_COLLECTION_SYNCGROUP_SUFFIX + listId;
+ return LIST_COLLECTION_SYNCGROUP_PREFIX + listId;
}
private static String BLESSING_NAME_SEPARATOR = "___";
@@ -565,7 +567,7 @@
// TODO(alexfandrianto): If the cloud is dependent on me, then we must do this too.
// VFutures.sync(ensureCloudDatabaseExists); // must finish before syncgroup setup
ensureUserSyncgroupExists();
- Sharing.initDiscovery(); // requires that db and collection exist
+ Sharing.initDiscovery(sDatabase); // requires that db and collection exist
sInitialized = true;
}
diff --git a/app/src/syncbase/java/io/v/todos/sharing/Sharing.java b/app/src/syncbase/java/io/v/todos/sharing/Sharing.java
index 69f8a74..ac033a3 100644
--- a/app/src/syncbase/java/io/v/todos/sharing/Sharing.java
+++ b/app/src/syncbase/java/io/v/todos/sharing/Sharing.java
@@ -33,6 +33,8 @@
import io.v.v23.security.BlessingPattern;
import io.v.v23.services.syncbase.Id;
import io.v.v23.syncbase.ChangeType;
+import io.v.v23.syncbase.Database;
+import io.v.v23.syncbase.Invite;
import io.v.v23.syncbase.Syncbase;
import io.v.v23.syncbase.WatchChange;
import io.v.v23.verror.VException;
@@ -42,18 +44,15 @@
}
private static final String TAG = "SHARING";
- private static final String OWNER_KEY = "owner";
private static final Object sDiscoveryMutex = new Object();
private static Discovery sDiscovery;
private static VContext sScanContext;
- private static VContext sAdvertiseContext;
- private final static Map<String, VContext> sAdContextMap = new HashMap<>();
public static Discovery getDiscovery() {
return sDiscovery;
}
- public static void initDiscovery() throws VException {
+ public static void initDiscovery(Database db) throws VException {
synchronized (sDiscoveryMutex) {
if (sDiscovery == null) {
sDiscovery = V.newDiscovery(SyncbasePersistence.getAppVContext());
@@ -61,8 +60,7 @@
// Rely on the neighborhood fragment to initialize presence advertisement.
NeighborhoodFragment.initSharePresence();
- sScanContext = initScanForInvites();
- sAdvertiseContext = initAdvertiseLists();
+ sScanContext = initScanForInvites(db);
}
}
}
@@ -71,8 +69,6 @@
public static void stopDiscovery() {
synchronized (sDiscoveryMutex) {
sScanContext.cancel();
- sAdvertiseContext.cancel();
- sAdContextMap.clear();
}
}
@@ -85,57 +81,29 @@
return getRootInterface() + ".presence2";
}
- public static String getInvitationInterface() {
- return getRootInterface() + ".invitation2";
- }
-
/**
* Starts a scanner seeking advertisements that invite this user to a todo list. When an invite
* is found, the app will automatically accept it.
*/
- public static VContext initScanForInvites()
+ public static VContext initScanForInvites(Database db)
throws VException {
VContext vContext = SyncbasePersistence.getAppVContext().withCancel();
try {
- ListenableFuture<Void> scan = InputChannels.withCallback(
- Sharing.getDiscovery().scan(vContext,
- "v.InterfaceName = \"" + Sharing.getInvitationInterface() + "\""),
- new InputChannelCallback<Update>() {
- @Override
- public ListenableFuture<Void> onNext(Update result) {
- final String listName = Iterables.getOnlyElement(result.getAddresses());
- if (listName == null) {
- return null;
- }
- String owner = result.getAttribute(OWNER_KEY);
- Log.d("SHARING", "Noticed advertised list: " + listName + " by: " +
- owner);
-
- // TODO(alexfandrianto): Remove hack.
- // https://github.com/vanadium/issues/issues/1328
- if (result.getAttribute(SyncbasePersistence
- .getPersonalBlessingsString()) == null) {
- Log.d(TAG, "...but the ad was not meant for this user.");
- return null; // ignore; this isn't meant for us
- }
-
- // Never mind about losses, just handle found advertisements.
- if (!result.isLost()) {
- Log.d(TAG, "...and will accept it.");
-
- SyncbasePersistence.acceptSharedTodoList(new Id(owner, listName));
- }
- return null;
- }
- });
- Futures.addCallback(scan, new FutureCallback<Void>() {
+ db.listenForInvites(vContext, new Database.InviteHandler() {
@Override
- public void onSuccess(@Nullable Void result) {
- }
-
- @Override
- public void onFailure(Throwable t) {
- handleScanListsError(t);
+ public void handleInvite(Invite invite) {
+ String prefix = SyncbasePersistence.LIST_COLLECTION_SYNCGROUP_PREFIX +
+ SyncbasePersistence.LISTS_PREFIX;
+ String name = invite.getSyncgroupId().getName();
+ if (!name.startsWith(prefix)) {
+ // Not actually a Todo List.
+ return;
+ }
+ Log.d(TAG, "Accepting todo list invite: " + invite.getSyncgroupId().toString());
+ String blessing = invite.getSyncgroupId().getBlessing();
+ Id listId = new Id(blessing, name.substring(
+ SyncbasePersistence.LIST_COLLECTION_SYNCGROUP_PREFIX.length()));
+ SyncbasePersistence.acceptSharedTodoList(listId);
}
});
} catch (VException e) {
@@ -147,132 +115,4 @@
private static void handleScanListsError(Throwable t) {
SyncbasePersistence.getAppErrorReporter().onError(R.string.err_scan_lists, t);
}
-
- /**
- * Creates advertisements based on the todo lists this user has created thus far and those that
- * are created in the future. The advertisements will need to be targeted to the users that have
- * been invited to the list.
- *
- * @return
- * @throws VException
- */
- public static VContext initAdvertiseLists()
- throws VException {
- final VContext vContext = SyncbasePersistence.getAppVContext().withCancel();
-
- // Prepare a watch on top of the userdata collection to determine which todo lists need to
- // be tracked by this application.
- SyncbasePersistence.watchUserCollection(new InputChannelCallback<WatchChange>() {
- @Override
- public ListenableFuture<Void> onNext(WatchChange change) {
- try {
- final String listIdStr = change.getRowName();
- final Id listId = SyncbasePersistence.convertStringToId(listIdStr);
-
- if (change.getChangeType() == ChangeType.DELETE_CHANGE) {
- VContext ctx = sAdContextMap.remove(listIdStr);
- if (ctx != null) { // TODO(alexfandrianto): ctx might be null if ad failed?
- ctx.cancel(); // Stop advertising the list; it's been deleted.
- }
- } else {
- final String owner = listId.getBlessing();
- if (!owner.equals(SyncbasePersistence.getPersonalBlessingsString())) {
- return Futures.immediateFuture((Void) null);
- }
-
- // We should probably start to advertise this collection and check its spec.
- SyncbasePersistence.watchSharedTo(listId, new Function<List<BlessingPattern>,
- Void>() {
- @Override
- public Void apply(List<BlessingPattern> patterns) {
- // Make a copy of the patterns list that excludes the cloud and this
- // user's blessings.
- List<BlessingPattern> filteredPatterns = new ArrayList<>();
- for (BlessingPattern pattern : patterns) {
- String pStr = pattern.toString();
- if (pStr.equals(SyncbasePersistence.getPersonalBlessingsString()) ||
- pStr.equals(SyncbasePersistence.CLOUD_BLESSING)) {
- continue;
- }
- filteredPatterns.add(pattern);
- }
-
- // Advertise to the remaining patterns.
- if (filteredPatterns.size() > 0) {
- Log.d(TAG, "Must advertise for " + listIdStr + " to " +
- filteredPatterns.toString());
- advertiseList(vContext, listId, filteredPatterns);
- }
- return null;
- }
- });
- }
- } catch (Exception e) {
- Log.w(TAG, "Error during watch handle", e);
- }
- return null;
- }
- });
- return vContext;
- }
-
- /**
- * Advertises that this list is available to this set of people. Cancels the old advertisement
- * if one exists. Only called by initAdvertiseLists.
- *
- * @param baseContext The context for all advertisements
- * @param listId The list to be advertised
- * @param patterns Blessings that the advertisement should target
- */
- private static void advertiseList(VContext baseContext, Id listId, List<BlessingPattern>
- patterns) {
- if (baseContext.isCanceled()) {
- Log.w(TAG, "Base context was canceled; cannot advertise");
- return;
- }
- // Swap out the ad context...
- String key = SyncbasePersistence.convertIdToString(listId);
- VContext oldAdContext = sAdContextMap.remove(key);
- if (oldAdContext != null) {
- oldAdContext.cancel();
- }
- VContext newAdContext = baseContext.withCancel();
- sAdContextMap.put(key, newAdContext);
-
-
- try {
- Advertisement ad = new Advertisement();
- ad.setInterfaceName(Sharing.getInvitationInterface());
- ad.getAddresses().add(listId.getName());
- ad.getAttributes().put(OWNER_KEY, listId.getBlessing());
-
- // TODO(alexfandrianto): Remove hack. https://github.com/vanadium/issues/issues/1328
- for (BlessingPattern pattern : patterns) {
- ad.getAttributes().put(pattern.toString(), "");
- }
-
- Futures.addCallback(Sharing.getDiscovery().advertise(sAdvertiseContext, ad,
- // TODO(alexfandrianto): Crypto crash if I use patterns instead of null.
- // https://github.com/vanadium/issues/issues/1328 and
- // https://github.com/vanadium/issues/issues/1331
- null),
- //patterns),
- new FutureCallback<Void>() {
- @Override
- public void onSuccess(@android.support.annotation.Nullable Void result) {
- }
-
- @Override
- public void onFailure(@NonNull Throwable t) {
- handleAdvertiseListError(t);
- }
- });
- } catch (VException e) {
- handleAdvertiseListError(e);
- }
- }
-
- private static void handleAdvertiseListError(Throwable t) {
- SyncbasePersistence.getAppErrorReporter().onError(R.string.err_advertise_list, t);
- }
}