blob: ff7e70c821a60f714f939350196887097b234dfb [file] [log] [blame]
// Copyright 2015 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 io.v.android.apps.syncslides.discovery;
import android.graphics.Bitmap;
import android.util.Log;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import java.io.ByteArrayOutputStream;
import io.v.android.apps.syncslides.db.VDeck;
import io.v.android.apps.syncslides.misc.V23Manager;
import io.v.android.apps.syncslides.model.Deck;
import io.v.android.apps.syncslides.model.DeckFactory;
import io.v.android.apps.syncslides.model.Participant;
import io.v.v23.context.VContext;
import io.v.v23.rpc.ServerCall;
import io.v.v23.verror.VException;
/**
* Someone taking part in a presentation.
*
* A deck presenter (i.e. a human speaker) is represented by one of these, to
* let others know information about the deck being presented (title, author,
* etc.).
*
* When someone runs syncslides, they'll hold N of these, one for each live talk
* out there that they can join. Those instances won't run as servers; they'll
* just be parcelable data blobs.
*
* When that someone wants to give a presentation, they'll create one of these
* and run it as a server, using the (public) userName as part of the mount
* name.
*/
public class ParticipantPeer implements Participant {
private static final String TAG = "ParticipantPeer";
private static final DateTimeFormatter TIME_FMT =
DateTimeFormat.forPattern("hh_mm_ss_SSSS");
// V23 name of the V23 service representing the participant.
private final String mServiceName;
// Visible name of human presenter.
// TODO(jregan): Switch to VPerson or the model equivalent.
private String mUserName;
// When did we last grab data from the endPoint?
private DateTime mRefreshTime;
// Deck the user is presenting. Can only present one at a time.
private Deck mDeck;
// Used to make decks after RPCs.
private final DeckFactory mDeckFactory;
private ParticipantPeer(
String userName, Deck deck, String serviceName, DeckFactory deckFactory) {
mUserName = userName;
mDeck = deck;
mServiceName = serviceName;
mDeckFactory = deckFactory;
}
public static ParticipantPeer makeWithServiceName(
String serviceName, DeckFactory deckFactory) {
return new ParticipantPeer(Unknown.USER_NAME, null, serviceName, deckFactory);
}
public static ParticipantPeer makeWithKnownDeck(String userName, Deck deck) {
return new ParticipantPeer(userName, deck, Unknown.SERVER_NAME, null);
}
@Override
public String getServiceName() {
return (mServiceName != null && !mServiceName.isEmpty()) ?
mServiceName : Unknown.SERVER_NAME;
}
@Override
public String getUserName() {
return mUserName;
}
@Override
public Deck getDeck() {
return mDeck;
}
@Override
public String toString() {
return "[userName=\"" + mUserName +
"\", deck=" + mDeck +
", time=" + getStringRefreshtime() + "]";
}
private String getStringRefreshtime() {
return mRefreshTime == null ?
"never" : mRefreshTime.toString(TIME_FMT);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ParticipantPeer)) {
return false;
}
ParticipantPeer p = (ParticipantPeer) obj;
boolean deckEqual = (mDeck == null) ? true : mDeck.equals(p.mDeck);
return deckEqual && getServiceName().equals(p.getServiceName());
}
@Override
public int hashCode() {
int deckCode = (mDeck == null) ? 0 : mDeck.hashCode();
return deckCode + getServiceName().hashCode();
}
/**
* Make an RPC on the mServiceName to get title, snapshot, etc.
*/
@Override
public boolean refreshData() {
if (mServiceName.equals(Unknown.SERVER_NAME)) {
// Don't attempt refresh.
return true;
}
Log.d(TAG, "refreshData");
// Flush, since the server might have died and restarted, invalidating
// cached endpoints.
Log.d(TAG, "Flushing cache for service " + mServiceName);
V23Manager.Singleton.get().flushServerFromCache(mServiceName);
ParticipantClient client =
ParticipantClientFactory.getParticipantClient(mServiceName);
Log.d(TAG, "Got client = " + client.toString());
try {
Log.d(TAG, "Calling get...");
VDeck vDeck = client.get(
V23Manager.Singleton.get().getVContext());
Log.d(TAG, "Back with vDeck = "+ vDeck.toString());
byte[] bytes = vDeck.getThumbnail();
if (bytes != null && bytes.length > 0) {
Log.d(TAG, " Seem to have a thumb");
} else {
Log.d(TAG, " No thumb");
}
Deck newDeck = mDeckFactory.make(vDeck, "whatShouldTheIdBe");
mRefreshTime = DateTime.now();
mDeck = newDeck;
Log.d(TAG, " Got deck = " + mDeck);
return true;
} catch (VException e) {
Log.d(TAG, "RPC failed, leaving current deck in place.");
e.printStackTrace();
}
return false;
}
private static class Unknown {
static final String SERVER_NAME = "unknownServerName";
static final String USER_NAME = "unknownUserName";
}
/**
* Serves data used in deck discovery.
*/
public static class Server implements ParticipantServer {
private static final String TAG = "ParticipantServer";
private final Deck mDeck;
public Server(Deck d) {
mDeck = d;
}
public VDeck get(VContext ctx, ServerCall call)
throws VException {
Log.d(TAG, "Responding to Get RPC.");
Log.d(TAG, " Sending mDeck = " + mDeck);
VDeck d = new VDeck();
d.setTitle(mDeck.getTitle());
if (mDeck.getThumb() == null) {
Log.d(TAG, " The response deck has no thumb.");
} else {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
Bitmap bitmap = mDeck.getThumb();
bitmap.compress(Bitmap.CompressFormat.JPEG, 60, stream);
d.setThumbnail(stream.toByteArray());
}
return d;
}
}
}