| // 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; |
| } |
| } |
| } |