Merge "java: default to vom version 81"
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 0d8e3e5..ec0881a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -13,8 +13,8 @@
## Contributing code
Please read the [contribution
-guidelines](https://github.com/vanadium/docs/blob/master/contributing/README.md)
-before sending patches.
+guidelines](https://vanadium.github.io/community/contributing.html) before
+sending patches.
**We do not accept GitHub pull requests.** (We use
[Gerrit](https://www.gerritcodereview.com/) instead for code reviews.)
@@ -25,7 +25,7 @@
## Testing changes
Typical users of the Vanadium for Java libraries will use a dependency manager
-such as Maven or Gradle to bring Vanadium into their projects.
+such as Maven or Gradle to bring Vanadium into their projects.
For example, in an Android project, the user may do something like:
@@ -134,5 +134,3 @@
```
(cd ${yourProject}; ./gradlew assembleDebug)
```
-
-
diff --git a/android-lib/build.gradle b/android-lib/build.gradle
index 9b21120..1f0511b 100644
--- a/android-lib/build.gradle
+++ b/android-lib/build.gradle
@@ -86,7 +86,7 @@
if (!System.getenv().containsKey('JIRI_ROOT')) {
throw new InvalidUserDataException("JIRI_ROOT is not set. "
+ "Please follow the Vanadium installation instructions at "
- + "https://github.com/vanadium/docs/blob/master/installation.md")
+ + "https://vanadium.github.io/installation/")
}
result.jiriRoot = new File(System.getenv()['JIRI_ROOT'])
@@ -95,7 +95,7 @@
throw new InvalidUserDataException(
result.jiriBin.toString() + " does not exist or is not an executable file. "
+ "Please follow the Vanadium installation instructions at "
- + "https://github.com/vanadium/docs/blob/master/installation.md")
+ + "https://vanadium.github.io/installation/")
}
return result
}
diff --git a/gradle-plugin/README.md b/gradle-plugin/README.md
index f25c5b1..339d20d 100644
--- a/gradle-plugin/README.md
+++ b/gradle-plugin/README.md
@@ -3,7 +3,7 @@
This plugin provides users of [Gradle](https://gradle.org/) with a convenient
way to generate Java
-[VDL](https://github.com/vanadium/docs/blob/master/designdocs/vdl-spec.md)
+[VDL](https://vanadium.github.io/designdocs/vdl-spec.html)
wrappers and to use those wrappers in a Java or Android project. It automates
several tasks that projects using VDL commonly have to perform.
diff --git a/lib/build.gradle b/lib/build.gradle
index 4209e7a..72d3cb4 100644
--- a/lib/build.gradle
+++ b/lib/build.gradle
@@ -96,7 +96,7 @@
if (!System.getenv().containsKey('JIRI_ROOT')) {
throw new InvalidUserDataException("JIRI_ROOT is not set. "
+ "Please follow the Vanadium installation instructions at "
- + "https://github.com/vanadium/docs/blob/master/installation.md")
+ + "https://vanadium.github.io/installation/")
}
result.jiriRoot = new File(System.getenv()['JIRI_ROOT'])
@@ -105,7 +105,7 @@
throw new InvalidUserDataException(
result.jiriBin.toString() + " does not exist or is not an executable file. "
+ "Please follow the Vanadium installation instructions at "
- + "https://github.com/vanadium/docs/blob/master/installation.md")
+ + "https://vanadium.github.io/installation/")
}
return result
}
diff --git a/lib/src/main/java/io/v/v23/discovery/VDiscovery.java b/lib/src/main/java/io/v/v23/discovery/VDiscovery.java
index d2a06dd..234e952 100644
--- a/lib/src/main/java/io/v/v23/discovery/VDiscovery.java
+++ b/lib/src/main/java/io/v/v23/discovery/VDiscovery.java
@@ -75,7 +75,7 @@
* v.Attrs["a"] = "v1" OR v.Attrs["a"] = "v2"
* </pre></blockquote><p>
* You can find the {@code SyncQL} tutorial at:
- * https://github.com/vanadium/docs/blob/master/tutorials/syncql-tutorial.md
+ * https://vanadium.github.io/tutorials/syncbase/syncql-tutorial.html
*
* @param context a context that will be used to stop the scan; scan will end when the context
* is cancelled or timed out
diff --git a/lib/src/main/java/io/v/v23/namespace/Namespace.java b/lib/src/main/java/io/v/v23/namespace/Namespace.java
index 1329385..2513516 100644
--- a/lib/src/main/java/io/v/v23/namespace/Namespace.java
+++ b/lib/src/main/java/io/v/v23/namespace/Namespace.java
@@ -50,10 +50,10 @@
* {@code context} gets canceled.
*
* @param context a client context
- * @param name a Vanadium name, see also <a href="https://github.com/vanadium/docs/blob/master/glossary.md#object-name">the
+ * @param name a Vanadium name, see also <a href="https://vanadium.github.io/glossary.html#object-name">the
* Name entry</a> in the glossary
* @param server an object address, see also
- * <a href="https://github.com/vanadium/docs/blob/master/concepts/naming.md#object-names">the Object names</a>
+ * <a href="https://vanadium.github.io/concepts/naming.html#object-names">the Object names</a>
* section of the Naming Concepts document
* @param ttl the duration for which the mount should live
* @param options options to pass to the implementation as described above, or {@code null}
@@ -86,10 +86,10 @@
* {@code context} gets canceled.
*
* @param context a client context
- * @param name a Vanadium name, see also <a href="https://github.com/vanadium/docs/blob/master/glossary.md#object-name">the
+ * @param name a Vanadium name, see also <a href="https://vanadium.github.io/glossary.html#object-name">the
* Name entry</a> in the glossary
* @param server an object address, see also
- * <a href="https://github.com/vanadium/docs/blob/master/concepts/naming.md#object-names">the Object names</a>
+ * <a href="https://vanadium.github.io/concepts/naming.html#object-names">the Object names</a>
* section of the Naming Concepts document
* @param options options to pass to the implementation as described above, or {@code null}
*/
@@ -121,7 +121,7 @@
*
* @param context a client context
* @param name the Vanadium name to delete, see also
- * <a href="https://github.com/vanadium/docs/blob/master/glossary.md#object-name">the Name entry</a> in the
+ * <a href="https://vanadium.github.io/glossary.html#object-name">the Name entry</a> in the
* glossary
* @param deleteSubtree whether the entire tree rooted at {@code name} should be deleted
* @param options options to pass to the implementation as described above, or {@code null}
@@ -154,7 +154,7 @@
*
* @param context a client context
* @param name the Vanadium name to resolve, see also
- * <a href="https://github.com/vanadium/docs/blob/master/glossary.md#object-name">the Name entry</a> in the
+ * <a href="https://vanadium.github.io/glossary.html#object-name">the Name entry</a> in the
* glossary
* @param options options to pass to the implementation as described above, or {@code null}
* @return a new {@link ListenableFuture} whose result is the {@link MountEntry} to which the
@@ -187,7 +187,7 @@
*
* @param context a client context
* @param name the Vanadium name to resolve, see also
- * <a href="https://github.com/vanadium/docs/blob/master/glossary.md#object-name">the Name entry</a> in the
+ * <a href="https://vanadium.github.io/glossary.html#object-name">the Name entry</a> in the
* glossary
* @param options options to pass to the implementation as described above, or {@code null}
* @return a new {@link ListenableFuture} whose result is the {@link MountEntry} of the
@@ -205,7 +205,7 @@
* This is a non-blocking method.
*
* @param context a client context
- * @param name a Vanadium name, see also <a href="https://github.com/vanadium/docs/blob/master/glossary.md#object-name">the
+ * @param name a Vanadium name, see also <a href="https://vanadium.github.io/glossary.html#object-name">the
* Name entry</a> in the glossary
* @return {@code true} iff resolution information for the name was successfully flushed
*/
@@ -251,7 +251,7 @@
* @param roots the roots that will be used to turn relative paths into absolute paths, or
* {@code null} to clear the currently configured set of roots. Each entry should
* be a Vanadium name, see also
- * <a href="https://github.com/vanadium/docs/blob/master/glossary.md#object-name">
+ * <a href="https://vanadium.github.io/glossary.html#object-name">
* the Name entry</a> in the glossary
*/
void setRoots(List<String> roots) throws VException;
diff --git a/lib/src/main/java/io/v/v23/rpc/package-info.java b/lib/src/main/java/io/v/v23/rpc/package-info.java
index 0b2036d..7060e5b 100644
--- a/lib/src/main/java/io/v/v23/rpc/package-info.java
+++ b/lib/src/main/java/io/v/v23/rpc/package-info.java
@@ -5,7 +5,7 @@
/**
* Package rpc defines interfaces for communication via remote procedure call.
* <p><ul>
- * <li>Concept: <a href="https://github.com/vanadium/docs/blob/master/concepts/rpc.md">https://github.com/vanadium/docs/blob/master/concepts/rpc.md</a>.</li>
+ * <li>Concept: <a href="https://vanadium.github.io/concepts/rpc.html">https://vanadium.github.io/concepts/rpc.html</a>.</li>
* <li>Tutorial: (forthcoming)</li>
* </ul><p>
* There are two actors in the system, clients and servers. {@link io.v.v23.rpc.Client}s invoke
diff --git a/lib/src/main/java/io/v/v23/security/BlessingRoots.java b/lib/src/main/java/io/v/v23/security/BlessingRoots.java
index 75bd0cf..ba43846 100644
--- a/lib/src/main/java/io/v/v23/security/BlessingRoots.java
+++ b/lib/src/main/java/io/v/v23/security/BlessingRoots.java
@@ -12,7 +12,7 @@
/**
* The set of authoritative public keys for roots of blessings.
* <p>
- * See also: <a href="https://github.com/vanadium/docs/blob/master/glossary.md#blessing-root">https://github.com/vanadium/docs/blob/master/glossary.md#blessing-root</a>.
+ * See also: <a href="https://vanadium.github.io/glossary.html#blessing-root">https://vanadium.github.io/glossary.html#blessing-root</a>.
*/
public interface BlessingRoots {
/**
diff --git a/lib/src/main/java/io/v/v23/security/Blessings.java b/lib/src/main/java/io/v/v23/security/Blessings.java
index a900c18..1076480 100644
--- a/lib/src/main/java/io/v/v23/security/Blessings.java
+++ b/lib/src/main/java/io/v/v23/security/Blessings.java
@@ -28,7 +28,7 @@
* {@link Blessings} objects are immutable and multiple threads may invoke methods on
* them simultaneously.
* <p>
- * See also: <a href="https://github.com/vanadium/docs/blob/master/glossary.md#blessing">https://github.com/vanadium/docs/blob/master/glossary.md#blessing</a>.
+ * See also: <a href="https://vanadium.github.io/glossary.html#blessing">https://vanadium.github.io/glossary.html#blessing</a>.
*/
public final class Blessings implements Serializable {
private static final long serialVersionUID = 1L;
diff --git a/lib/src/main/java/io/v/v23/security/Discharge.java b/lib/src/main/java/io/v/v23/security/Discharge.java
index bdb23cd..9fcc9ed 100644
--- a/lib/src/main/java/io/v/v23/security/Discharge.java
+++ b/lib/src/main/java/io/v/v23/security/Discharge.java
@@ -12,7 +12,7 @@
* <p>
* {@code Discharge} objects are immutable and are therefore threadsafe.
* <p>
- * @see <a href="https://github.com/vanadium/docs/blob/master/glossary.md#discharge">the Discharge glossary entry</a>
+ * @see <a href="https://vanadium.github.io/glossary.html#discharge">the Discharge glossary entry</a>
*/
public class Discharge {
private final WireDischarge wire;
diff --git a/lib/src/main/java/io/v/v23/security/VPrincipal.java b/lib/src/main/java/io/v/v23/security/VPrincipal.java
index d0d3eec..ad6f794 100644
--- a/lib/src/main/java/io/v/v23/security/VPrincipal.java
+++ b/lib/src/main/java/io/v/v23/security/VPrincipal.java
@@ -14,7 +14,7 @@
* <p>
* Multiple goroutines may invoke methods on a {@link VPrincipal} simultaneously.
* <p>
- * See also: <a href="https://github.com/vanadium/docs/blob/master/glossary.md#principal">https://github.com/vanadium/docs/blob/master/glossary.md#principal</a>.
+ * See also: <a href="https://vanadium.github.io/glossary.html#principal">https://vanadium.github.io/glossary.html#principal</a>.
*/
public interface VPrincipal {
/**
diff --git a/lib/src/main/java/io/v/v23/security/package-info.java b/lib/src/main/java/io/v/v23/security/package-info.java
index 57e1681..4c1961e 100644
--- a/lib/src/main/java/io/v/v23/security/package-info.java
+++ b/lib/src/main/java/io/v/v23/security/package-info.java
@@ -5,7 +5,7 @@
/**
* Package security defines types and utilities associated with security.
* <p><ul>
- * <li>Concept: <a href="https://github.com/vanadium/docs/blob/master/concepts/security.md">https://github.com/vanadium/docs/blob/master/concepts/security.md</a></li>
+ * <li>Concept: <a href="https://vanadium.github.io/concepts/security.html">https://vanadium.github.io/concepts/security.html</a></li>
* <li>Tutorial: (forthcoming)</li>
* </ul><p>
* The primitives and APIs defined in this package enable bi-directional,
diff --git a/projects/moments/app/src/main/java/io/v/moments/ifc/Advertiser.java b/projects/moments/app/src/main/java/io/v/moments/ifc/Advertiser.java
index 3c25a03..36b70b0 100644
--- a/projects/moments/app/src/main/java/io/v/moments/ifc/Advertiser.java
+++ b/projects/moments/app/src/main/java/io/v/moments/ifc/Advertiser.java
@@ -4,14 +4,19 @@
package io.v.moments.ifc;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.ListenableFuture;
+
/**
* Something needing to advertise itself will want an implementation of this.
*/
public interface Advertiser {
/**
- * Synchronously start advertising.
+ * Asynchronously start advertising. Callback executed on success or
+ * failure of advertising startup. The future returned on successful
+ * startup should be given a callback to handle advertising shutdown.
*/
- void advertiseStart();
+ void advertiseStart(FutureCallback<ListenableFuture<Void>> callback);
/**
* True if advertiseStop could usefully be called.
diff --git a/projects/moments/app/src/main/java/io/v/moments/lib/V23Manager.java b/projects/moments/app/src/main/java/io/v/moments/lib/V23Manager.java
index ba3a4d9..9d13e03 100644
--- a/projects/moments/app/src/main/java/io/v/moments/lib/V23Manager.java
+++ b/projects/moments/app/src/main/java/io/v/moments/lib/V23Manager.java
@@ -14,6 +14,7 @@
import org.joda.time.Duration;
+import java.util.ArrayList;
import java.util.List;
import io.v.android.libs.security.BlessingsManager;
@@ -25,7 +26,6 @@
import io.v.v23.discovery.Service;
import io.v.v23.discovery.Update;
import io.v.v23.discovery.VDiscovery;
-import io.v.v23.rpc.ListenSpec;
import io.v.v23.rpc.Server;
import io.v.v23.security.BlessingPattern;
import io.v.v23.security.Blessings;
@@ -38,6 +38,7 @@
public class V23Manager {
private static final String TAG = "V23Manager";
private static final String BLESSINGS_KEY = "BlessingsKey";
+ private static final List<BlessingPattern> NO_PATTERNS = new ArrayList<>();
private Context mAndroidCtx;
private VContext mV23Ctx = null;
private VDiscovery mDiscovery = null;
@@ -45,42 +46,16 @@
// Singleton.
private V23Manager() {
}
-
- public VContext advertise(final Service service, List<BlessingPattern> patterns) {
+
+ public VContext advertise(final Service service, FutureCallback<ListenableFuture<Void>> callback) {
if (mDiscovery == null) {
- Log.d(TAG, "Discovery not ready.");
+ callback.onFailure(new IllegalStateException("Discovery not ready."));
return null;
}
- VContext context = mV23Ctx.withCancel();
+ VContext context = mV23Ctx.withTimeout(Duration.standardMinutes(5));
final ListenableFuture<ListenableFuture<Void>> fStart =
- mDiscovery.advertise(context, service, patterns);
- Futures.addCallback(fStart, new FutureCallback<ListenableFuture<Void>>() {
- @Override
- public void onSuccess(ListenableFuture<Void> result) {
- Log.d(TAG, "Started advertising with ID = " +
- service.getInstanceId());
- Futures.addCallback(
- result, new FutureCallback<Void>() {
- @Override
- public void onSuccess(Void result) {
- Log.d(TAG, "Stopped advertising.");
- }
-
- @Override
- public void onFailure(Throwable t) {
- if (!(t instanceof java.util.concurrent.CancellationException)) {
- Log.d(TAG, "Failure to gracefully stop advertising.", t);
- }
- }
- }
- );
- }
-
- @Override
- public void onFailure(Throwable t) {
- Log.d(TAG, "Failure to start advertising.", t);
- }
- });
+ mDiscovery.advertise(context, service, NO_PATTERNS);
+ Futures.addCallback(fStart, callback);
Log.d(TAG, "Back from V.getDiscovery.advertise");
return context;
}
@@ -93,14 +68,14 @@
VContext context = mV23Ctx.withCancel();
Log.d(TAG, "Calling V.getDiscovery.scan with q=" + query);
final ListenableFuture<Void> fStart =
- InputChannels.withCallback(mDiscovery.scan(context, query),
- new InputChannelCallback<Update>() {
- @Override
- public ListenableFuture<Void> onNext(Update result) {
- listener.scanUpdateReceived(result);
- return Futures.immediateFuture(null);
- }
- });
+ InputChannels.withCallback(mDiscovery.scan(context, query),
+ new InputChannelCallback<Update>() {
+ @Override
+ public ListenableFuture<Void> onNext(Update result) {
+ listener.scanUpdateReceived(result);
+ return Futures.immediateFuture(null);
+ }
+ });
Futures.addCallback(fStart, new FutureCallback<Void>() {
@Override
public void onSuccess(Void result) {
@@ -151,20 +126,9 @@
mAndroidCtx = null;
}
- private VContext getListenContext() throws VException {
- final boolean useProxy = false;
- // Disabled while debugging network performance / visibility issues.
- if (useProxy) {
- ListenSpec spec = V.getListenSpec(mV23Ctx).withProxy("proxy");
- Log.d(TAG, "listenSpec = " + spec.toString() + " p=" + spec.getProxy());
- return V.withListenSpec(mV23Ctx, spec);
- }
- return mV23Ctx;
- }
-
public VContext makeServerContext(String mountName, Object server) throws VException {
return V.withNewServer(
- getListenContext(),
+ mV23Ctx.withCancel(),
mountName,
server,
VSecurity.newAllowEveryoneAuthorizer());
diff --git a/projects/moments/app/src/main/java/io/v/moments/model/AdvertiserFactory.java b/projects/moments/app/src/main/java/io/v/moments/model/AdvertiserFactory.java
index b23ef86..522ce76 100644
--- a/projects/moments/app/src/main/java/io/v/moments/model/AdvertiserFactory.java
+++ b/projects/moments/app/src/main/java/io/v/moments/model/AdvertiserFactory.java
@@ -9,15 +9,14 @@
import io.v.moments.ifc.Advertiser;
import io.v.moments.ifc.IdSet;
-import io.v.moments.lib.Id;
import io.v.moments.ifc.Moment;
-import io.v.moments.ifc.MomentFactory;
+import io.v.moments.lib.Id;
import io.v.moments.lib.V23Manager;
/**
* Makes advertisers. Keeps a record of all of them for the life of the app.
- * Can use this record to reject local advertisements when scanning, or to
- * shut down all advertising.
+ * Can use this record to reject local advertisements when scanning, or to shut
+ * down all advertising.
*/
public class AdvertiserFactory implements IdSet {
private final V23Manager mV23Manager;
diff --git a/projects/moments/app/src/main/java/io/v/moments/model/AdvertiserImpl.java b/projects/moments/app/src/main/java/io/v/moments/model/AdvertiserImpl.java
index 3efa5c5..f178285 100644
--- a/projects/moments/app/src/main/java/io/v/moments/model/AdvertiserImpl.java
+++ b/projects/moments/app/src/main/java/io/v/moments/model/AdvertiserImpl.java
@@ -7,6 +7,7 @@
import android.graphics.Bitmap;
import android.util.Log;
+import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@@ -25,7 +26,6 @@
import io.v.v23.discovery.Service;
import io.v.v23.naming.Endpoint;
import io.v.v23.rpc.ServerCall;
-import io.v.v23.security.BlessingPattern;
import io.v.v23.verror.VException;
/**
@@ -34,7 +34,6 @@
public class AdvertiserImpl implements Advertiser {
static final String NO_MOUNT_NAME = "";
private static final String TAG = "AdvertiserImpl";
- private static final List<BlessingPattern> NO_PATTERNS = new ArrayList<>();
private final V23Manager mV23Manager;
private final Moment mMoment;
@@ -54,7 +53,7 @@
@Override
public boolean isAdvertising() {
- return mAdvCtx != null;
+ return mAdvCtx != null || mServerCtx != null;
}
@Override
@@ -63,16 +62,20 @@
}
@Override
- public void advertiseStart() {
+ public void advertiseStart(FutureCallback<ListenableFuture<Void>> callback) {
+ Log.d(TAG, "Entering advertiseStart.");
if (isAdvertising()) {
- throw new IllegalStateException("Already advertising.");
+ callback.onFailure(new IllegalStateException("Already advertising."));
+ return;
}
Log.d(TAG, "Starting service for moment " + mMoment);
try {
mServerCtx = mV23Manager.makeServerContext(
NO_MOUNT_NAME, new MomentServer());
} catch (VException e) {
- throw new IllegalStateException("Unable to start service.", e);
+ mServerCtx = null;
+ callback.onFailure(new IllegalStateException("Unable to start service.", e));
+ return;
}
List<String> addresses = new ArrayList<>();
Endpoint[] points = mV23Manager.getServer(mServerCtx).getStatus().getEndpoints();
@@ -81,24 +84,25 @@
}
Attributes attrs = mMoment.makeAttributes();
Log.d(TAG, "Starting advertisement of moment " + mMoment);
- mAdvCtx = mV23Manager.advertise(
- makeAdvertisement(attrs, addresses),
- NO_PATTERNS);
+ Service service = makeAdvertisement(attrs, addresses);
+ mAdvCtx = mV23Manager.advertise(service, callback);
+ Log.d(TAG, "Exiting advertiseStart.");
}
@Override
public void advertiseStop() {
- if (!isAdvertising()) {
- throw new IllegalStateException("Not advertising.");
+ Log.d(TAG, "Entering advertiseStop");
+ if (mAdvCtx != null) {
+ Log.d(TAG, "Cancelling advertising.");
+ mAdvCtx.cancel();
+ mAdvCtx = null;
}
- Log.d(TAG, "Stopping advertisement of " + mMoment);
- Log.d(TAG, "Cancelling advertising context.");
- mAdvCtx.cancel();
- mAdvCtx = null;
- Log.d(TAG, "Cancelling server context.");
- mServerCtx.cancel();
- mServerCtx = null;
- Log.d(TAG, "Advertising stopped.");
+ if (mServerCtx != null) {
+ Log.d(TAG, "Cancelling service.");
+ mServerCtx.cancel();
+ mServerCtx = null;
+ }
+ Log.d(TAG, "Exiting advertiseStop");
}
/**
diff --git a/projects/moments/app/src/main/java/io/v/moments/ux/MainActivity.java b/projects/moments/app/src/main/java/io/v/moments/ux/MainActivity.java
index dbc7e89..2e6e21b 100644
--- a/projects/moments/app/src/main/java/io/v/moments/ux/MainActivity.java
+++ b/projects/moments/app/src/main/java/io/v/moments/ux/MainActivity.java
@@ -218,7 +218,7 @@
// serialExecutor is used to start/stop advertisements in the UX.
MomentAdapter adapter = new MomentAdapter(
mRemoteMoments, mLocalMoments,
- mAdvertiserFactory, mSerialExecutor, mHandler);
+ mAdvertiserFactory, mHandler);
// Lets the adapter speed up a bit.
adapter.setHasStableIds(true);
@@ -329,23 +329,6 @@
Log.d(TAG, "Loading moments from prefs.");
mStateStore.prefsLoad(mLocalMoments);
}
- for (final Moment moment : mLocalMoments) {
- if (moment.getDesiredAdState().equals(Moment.AdState.ON)) {
- mSerialExecutor.submit(new Runnable() {
- @Override
- public void run() {
- try {
- mAdvertiserFactory.getOrMake(moment).advertiseStart();
- Log.d(TAG, "Started advertising " + moment.getCaption());
- } catch (Exception e) {
- e.printStackTrace();
- toast("Unable to advertise - see log.");
- }
- }
- });
-
- }
- }
if (mShouldBeScanning && !isScanning()) {
startScanning();
}
diff --git a/projects/moments/app/src/main/java/io/v/moments/ux/MomentAdapter.java b/projects/moments/app/src/main/java/io/v/moments/ux/MomentAdapter.java
index 0101fe9..4edd069 100644
--- a/projects/moments/app/src/main/java/io/v/moments/ux/MomentAdapter.java
+++ b/projects/moments/app/src/main/java/io/v/moments/ux/MomentAdapter.java
@@ -11,12 +11,9 @@
import android.view.View;
import android.view.ViewGroup;
-import java.util.concurrent.ExecutorService;
-
import io.v.moments.R;
import io.v.moments.ifc.ListObserver;
import io.v.moments.ifc.Moment;
-import io.v.moments.ifc.Moment.Kind;
import io.v.moments.lib.ObservedList;
import io.v.moments.model.AdvertiserFactory;
@@ -28,18 +25,15 @@
private final ObservedList<Moment> mRemoteMoments;
private final ObservedList<Moment> mLocalMoments;
private final AdvertiserFactory mAdvertiserFactory;
- private final ExecutorService mExecutor;
private final Handler mHandler;
public MomentAdapter(ObservedList<Moment> remoteMoments,
ObservedList<Moment> localMoments,
AdvertiserFactory advertiserFactory,
- ExecutorService executor,
Handler handler) {
mRemoteMoments = remoteMoments;
mLocalMoments = localMoments;
mAdvertiserFactory = advertiserFactory;
- mExecutor = executor;
mHandler = handler;
}
@@ -73,7 +67,7 @@
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.item_moment, parent, false);
- return new MomentHolder(view, context, mExecutor, mHandler);
+ return new MomentHolder(view, context, mHandler);
}
private boolean isRemote(int position) {
diff --git a/projects/moments/app/src/main/java/io/v/moments/ux/MomentHolder.java b/projects/moments/app/src/main/java/io/v/moments/ux/MomentHolder.java
index 96abfef..1227608 100644
--- a/projects/moments/app/src/main/java/io/v/moments/ux/MomentHolder.java
+++ b/projects/moments/app/src/main/java/io/v/moments/ux/MomentHolder.java
@@ -16,15 +16,18 @@
import android.widget.TextView;
import android.widget.Toast;
-import java.util.concurrent.ExecutorService;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
import io.v.moments.R;
import io.v.moments.ifc.Advertiser;
import io.v.moments.ifc.Moment;
-import static io.v.moments.ifc.Moment.AdState;
import io.v.moments.ifc.Moment.Kind;
import io.v.moments.ifc.Moment.Style;
+import static io.v.moments.ifc.Moment.AdState;
+
/**
* Holds the views comprising a Moment for a RecyclerView.
*/
@@ -35,15 +38,11 @@
private final SwitchCompat advertiseButton;
private final ImageView imageView;
private final Context mContext;
- private final ExecutorService mExecutor;
private final Handler mHandler;
- public MomentHolder(
- View itemView, Context context,
- ExecutorService executor, Handler handler) {
+ public MomentHolder(View itemView, Context context, Handler handler) {
super(itemView);
mContext = context;
- mExecutor = executor;
mHandler = handler;
authorTextView = (TextView) itemView.findViewById(R.id.moment_author);
captionTextView = (TextView) itemView.findViewById(R.id.moment_caption);
@@ -56,22 +55,18 @@
*/
public void bind(Moment moment, Advertiser advertiser) {
final Kind kind = advertiser == null ? Kind.REMOTE : Kind.LOCAL;
- Log.d(TAG, "Holder: binding " + kind + " " + moment);
+ Log.d(TAG, "Binding " + kind + " " + moment);
authorTextView.setText(moment.getAuthor());
captionTextView.setText(moment.getCaption());
if (moment.hasPhoto(kind, Style.THUMB)) {
imageView.setImageBitmap(moment.getPhoto(kind, Style.THUMB));
imageView.setOnClickListener(showPhoto(moment, kind));
- } else {
- Log.d(TAG, kind.toString() + " " + Style.THUMB + " not ready.");
}
if (moment.hasPhoto(kind, Style.FULL)) {
if (!moment.hasPhoto(kind, Style.THUMB)) {
throw new IllegalStateException(Style.THUMB.toString() +
" should be ready if " + Style.FULL + " is ready.");
}
- } else {
- Log.d(TAG, kind.toString() + " " + Style.FULL + " not ready.");
}
if (kind.equals(Kind.REMOTE)) {
advertiseButton.setVisibility(View.INVISIBLE);
@@ -79,9 +74,10 @@
advertiseButton.setText("");
advertiseButton.setVisibility(View.VISIBLE);
advertiseButton.setEnabled(true);
- advertiseButton.setChecked(moment.getDesiredAdState().equals(AdState.ON));
advertiseButton.setOnCheckedChangeListener(
toggleAdvertising(moment, advertiser));
+ advertiseButton.setChecked(
+ moment.getDesiredAdState().equals(AdState.ON));
}
}
@@ -94,12 +90,10 @@
return new View.OnClickListener() {
@Override
public void onClick(View v) {
- Log.d(TAG, "Clicked moment.");
Intent intent = ShowPhotoActivity.makeIntent(
mContext, moment.getOrdinal(), kind);
mContext.startActivity(intent);
}
-
};
}
@@ -109,34 +103,89 @@
@Override
public void onCheckedChanged(CompoundButton button, boolean isChecked) {
if (isChecked) {
- moment.setDesiredAdState(AdState.ON);
- mExecutor.submit(new Runnable() {
- @Override
- public void run() {
- try {
- advertiser.advertiseStart();
- toast("Started advertising " + moment.getCaption());
- } catch (Exception e) {
- e.printStackTrace();
- toast("Had problem starting advertising.");
- }
- }
- });
+ handleStartAdvertising(moment, advertiser);
} else {
- moment.setDesiredAdState(AdState.OFF);
- mExecutor.submit(new Runnable() {
- @Override
- public void run() {
- try {
- advertiser.advertiseStop();
- toast("Stopped advertising " + moment.getCaption());
- } catch (Exception e) {
- e.printStackTrace();
- toast("Had problem stopping advertising.");
+ handleStopAdvertising(moment, advertiser);
+ }
+ }
+ };
+ }
+
+ private void handleStartAdvertising(final Moment moment, final Advertiser advertiser) {
+ moment.setDesiredAdState(AdState.ON);
+ advertiser.advertiseStart(makeAdvertiseCallback(moment));
+ }
+
+ private void handleStopAdvertising(final Moment moment, final Advertiser advertiser) {
+ moment.setDesiredAdState(AdState.OFF);
+ if (advertiser.isAdvertising()) {
+ advertiser.advertiseStop();
+ toast("Stopped advertising " + moment.getCaption());
+ } else {
+ Log.d(TAG, "handleStopAdvertising called, but not advertising.");
+ }
+ }
+
+ private FutureCallback<ListenableFuture<Void>> makeAdvertiseCallback(final Moment moment) {
+ return new FutureCallback<ListenableFuture<Void>>() {
+ private void assureStopped() {
+ moment.setDesiredAdState(AdState.OFF);
+ if (advertiseButton.isChecked()) {
+ advertiseButton.setChecked(false);
+ }
+ }
+
+ @Override
+ public void onSuccess(ListenableFuture<Void> result) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ moment.setDesiredAdState(AdState.ON);
+ advertiseButton.setChecked(true);
+ }
+ });
+ Futures.addCallback(
+ result, new FutureCallback<Void>() {
+ @Override
+ public void onSuccess(Void result) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ assureStopped();
+ }
+ });
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ assureStopped();
+ if (t instanceof java.util.concurrent.CancellationException) {
+ // At the time of writing, the only way advertising ends
+ // is by throwing this exception, so this is actually
+ // a non-exceptional success case.
+ } else {
+ Log.d(TAG, "Failure to gracefully stop advertising.", t);
+
+ }
+ }
+ });
}
}
- });
- }
+ );
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ assureStopped();
+ Log.d(TAG, "Failure to start advertising " + moment, t);
+ }
+ });
}
};
}