blob: c5187f4cbdc2f9681e03ebe5313f96e3a8ffbcbb [file] [log] [blame]
// Copyright 2016 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.moments.ux;
import android.app.Activity;
import android.support.v7.widget.SwitchCompat;
import android.util.Log;
import android.widget.CompoundButton;
import android.widget.Toast;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.concurrent.CancellationException;
import io.v.moments.ifc.Moment;
import io.v.moments.ifc.Scanner;
import io.v.moments.lib.DiscoveredList;
import io.v.moments.model.Toaster;
import io.v.v23.InputChannelCallback;
import io.v.v23.discovery.Update;
/**
* Manages scanning UX.
*
* The callbacks provided will be run on the UX thread, so its safe to perform
* UX operations in the callbacks.
*/
public class ScanSwitchHolder implements CompoundButton.OnCheckedChangeListener {
private static final String TAG = "ScanSwitchHolder";
private final Scanner mScanner;
private final Toaster mToaster;
private final DiscoveredList<Moment> mRemoteMoments;
private SwitchCompat mSwitch;
public ScanSwitchHolder(
Toaster toaster, Scanner scanner,
DiscoveredList<Moment> remoteMoments) {
if (toaster == null) {
throw new IllegalArgumentException("Null activity.");
}
if (scanner == null) {
throw new IllegalArgumentException("Null scanner.");
}
if (remoteMoments == null) {
throw new IllegalArgumentException("Null remoteMoments.");
}
mToaster = toaster;
mScanner = scanner;
mRemoteMoments = remoteMoments;
}
@Override
public void onCheckedChanged(CompoundButton button, boolean isChecked) {
Log.d(TAG, "onCheckedChanged with checked = " + isChecked);
if (button.getId() != mSwitch.getId()) {
throw new IllegalStateException("Bad scan wiring.");
}
if (isChecked) {
if (mScanner.isScanning()) {
Log.d(TAG, "Asked to start scanning, but already scanning.");
return;
}
mScanner.start(makeStartupCallback(), makeUpdateCallback(), makeCompletionCallback());
} else {
if (!mScanner.isScanning()) {
Log.d(TAG, "Asked to stop scanning, but already not scanning.");
return;
}
mScanner.stop();
}
}
public void setSwitch(SwitchCompat theSwitch) {
mSwitch = theSwitch;
theSwitch.setOnCheckedChangeListener(this);
}
private FutureCallback<Void> makeStartupCallback() {
return new FutureCallback<Void>() {
@Override
public void onSuccess(Void result) {
Log.d(TAG, "Started scanning.");
mToaster.toast("Scanning.");
}
@Override
public void onFailure(Throwable t) {
Log.d(TAG, "Unable to scan.", t);
if (mSwitch.isChecked()) {
Log.d(TAG, "On failure settings switch to off.");
mSwitch.setChecked(false);
}
mToaster.toast("Unable to scan.");
}
};
}
private InputChannelCallback<Update> makeUpdateCallback() {
return new InputChannelCallback<Update>() {
@Override
public ListenableFuture<Void> onNext(Update result) {
mRemoteMoments.scanUpdateReceived(result);
return Futures.immediateFuture(null);
}
};
}
/** Verify that scanning is off and that the UX reflects that fact. */
private void cleanUpPostStop() {
Log.d(TAG, "cleanUpPostStop");
if (mScanner.isScanning()) {
throw new IllegalStateException("Scanning should be off.");
}
if (mToaster.isDestroyed()) {
Log.d(TAG, "The activity is dead, no UX to fix.");
return;
}
mRemoteMoments.dropAll();
if (mSwitch.isChecked()) {
// The button might be checked if the scan expired.
// The following call will trigger onCheckedChanged(false), but
// shouldn't do more than change the UX because everything is
// already shutdown.
mSwitch.setChecked(false);
}
}
private FutureCallback<Void> makeCompletionCallback() {
return new FutureCallback<Void>() {
/** Likely this is never called. */
@Override
public void onSuccess(Void result) {
cleanUpPostStop();
}
@Override
public void onFailure(Throwable t) {
if (t instanceof CancellationException) {
// At the time of writing, the only way scanning ends
// is by throwing this exception, so this is actually
// a non-exceptional success case.
cleanUpPostStop();
} else {
Log.d(TAG, "Failure to gracefully stop scanning.", t);
}
}
};
}
}