blob: beab3d187d40fecea60a889739a41e16cffb32dc [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.db;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.google.common.collect.Lists;
import java.util.List;
import io.v.impl.google.naming.NamingUtil;
import io.v.v23.context.CancelableVContext;
import io.v.v23.context.VContext;
import io.v.v23.syncbase.nosql.BatchDatabase;
import io.v.v23.syncbase.nosql.ChangeType;
import io.v.v23.syncbase.nosql.Database;
import io.v.v23.syncbase.nosql.Stream;
import io.v.v23.syncbase.nosql.Table;
import io.v.v23.syncbase.nosql.WatchChange;
import io.v.v23.verror.VException;
import io.v.v23.vom.VomUtil;
/**
* Watches for changes to the CurrentSlide row.
*/
class CurrentSlideWatcher {
private static final String TAG = "CurrentSlideWatcher";
private final CancelableVContext mVContext;
private final Database mDB;
private final String mDeckId;
private final String mPresentationId;
private final List<DB.CurrentSlideListener> mListeners;
private final Handler mHandler;
private VCurrentSlide mCurrentSlide;
private Thread mThread;
public CurrentSlideWatcher(VContext vContext, Database db, String deckId,
String presentationId) {
mVContext = vContext.withCancel();
mDB = db;
mDeckId = deckId;
mPresentationId = presentationId;
mListeners = Lists.newArrayList();
mHandler = new Handler(Looper.getMainLooper());
mCurrentSlide = new VCurrentSlide(0);
mThread = new Thread(new Runnable() {
@Override
public void run() {
watchCurrentSlide();
}
});
}
/**
* Adds a listener for this deckId/presentationId.
*
* @param listener notified when the current slide changes
*/
public void addListener(final DB.CurrentSlideListener listener) {
mHandler.post(new Runnable() {
@Override
public void run() {
listener.onChange(mCurrentSlide.getNum());
}
});
mListeners.add(listener);
if (mListeners.size() == 1) {
// The first listener was just added.
Log.i(TAG, "Starting thread");
mThread.start();
}
}
/**
* Removes a listener previously added with addListener. If this was the last
* listener, no more listeners can be added and the CurrentSlideWatcher
* should be discarded.
*/
public void removeListener(DB.CurrentSlideListener listener) {
mListeners.remove(listener);
if (mListeners.isEmpty()) {
mVContext.cancel();
mThread = null;
mHandler.removeCallbacksAndMessages(null);
}
}
/**
* Returns true if the number of listeners is greater than zero.
*/
public boolean hasListeners() {
return !mListeners.isEmpty();
}
private void notifyListeners(VCurrentSlide slide) {
Log.i(TAG, "notifyListeners " + slide);
mCurrentSlide = slide;
for (DB.CurrentSlideListener listener : mListeners) {
Log.i(TAG, "notifying listener " + listener);
listener.onChange(mCurrentSlide.getNum());
}
}
private void watchCurrentSlide() {
try {
Log.i(TAG, "watchCurrentSlide");
String rowKey = NamingUtil.join(mDeckId, mPresentationId, SyncbaseDB.CURRENT_SLIDE);
BatchDatabase batch = mDB.beginBatch(mVContext, null);
Table presentations = batch.getTable(SyncbaseDB.PRESENTATIONS_TABLE);
if (presentations.getRow(rowKey).exists(mVContext)) {
final VCurrentSlide slide = (VCurrentSlide) presentations.get(
mVContext, rowKey, VCurrentSlide.class);
mHandler.post(new Runnable() {
@Override
public void run() {
notifyListeners(slide);
}
});
}
Stream<WatchChange> changeStream;
changeStream = mDB.watch(mVContext, SyncbaseDB.PRESENTATIONS_TABLE, rowKey,
batch.getResumeMarker(mVContext));
for (WatchChange change : changeStream) {
if (!change.getTableName().equals(SyncbaseDB.PRESENTATIONS_TABLE)) {
Log.e(TAG, "Wrong change table name: " + change.getTableName() + ", wanted: " +
SyncbaseDB.PRESENTATIONS_TABLE);
continue;
}
final String key = change.getRowName();
Log.i(TAG, "Found change " + key);
if (!key.equals(rowKey)) {
Log.d(TAG, "Ignoring change: " + key);
continue;
}
if (change.getChangeType().equals(ChangeType.PUT_CHANGE)) {
final VCurrentSlide slide2 = (VCurrentSlide) VomUtil.decode(
change.getVomValue(), VCurrentSlide.class);
mHandler.post(new Runnable() {
@Override
public void run() {
notifyListeners(slide2);
}
});
}
}
} catch (VException e) {
Log.e(TAG, "Watching failed " + e);
}
}
}