All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.spotify.mobius.extras.ToggledEventSource Maven / Gradle / Ivy

/*
 * -\-\-
 * Mobius
 * --
 * Copyright (c) 2017-2020 Spotify AB
 * --
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * -/-/-
 */
package com.spotify.mobius.extras;

import static com.spotify.mobius.internal_util.Preconditions.checkNotNull;

import com.spotify.mobius.EventSource;
import com.spotify.mobius.disposables.Disposable;
import com.spotify.mobius.functions.Consumer;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
 * An event source that forwards the emitted values of a target Event Source, but whose emissions
 * are toggled on/off by a secondary Boolean-emitting Event Source
 *
 * @param  The Event class
 */
public final class ToggledEventSource implements EventSource {
  @Nonnull private final EventSource targetEventSource;
  @Nonnull private final List> consumers = new ArrayList<>(1);
  @Nullable private Disposable disposable = null;
  private Boolean active;

  /**
   * Returns a new EventSource that re-emits events from the targetEventSource
   * depending on the value emitted by the togglingEventSource
* When the state is set to not re-emit values, this Event Source unsubscribed from the * targetEventSource completely, and re-subscribes when the state is set to emit values * again. * * @param The class of the events being re-emitted * @param targetEventSource The Event Source whose events will be conditionally re-emitted * @param togglingSource The Event Source whose emitted values control whether the * targetEventSource's values are re-emitted (when togglingSource emits True) or * discarded (when goggling Source emits False) * @param initialToggleState The initial state of whether to re-emit values or not. True to allow * re-emitting, false to stop it. */ @Nonnull public static EventSource from( @Nonnull EventSource targetEventSource, @Nonnull EventSource togglingSource, boolean initialToggleState) { final ToggledEventSource toggledEventSource = new ToggledEventSource<>(targetEventSource, initialToggleState); new ToggledEventSourceWeakReferenceConnection( togglingSource, new WeakReference<>(toggledEventSource)); return toggledEventSource; } private ToggledEventSource(@Nonnull EventSource targetEventSource, boolean isActive) { this.targetEventSource = checkNotNull(targetEventSource); this.active = isActive; } private void setActive(Boolean active) { this.active = active; checkToUpdateSubscription(); } private void checkToUpdateSubscription() { synchronized (consumers) { if (!active || consumers.size() == 0) { if (disposable != null) { disposable.dispose(); } disposable = null; } else { if (disposable == null) { disposable = targetEventSource.subscribe(this::onValueEmitted); } } } } private void onValueEmitted(T f) { final List> toSendTo; synchronized (consumers) { toSendTo = new ArrayList<>(consumers); } for (Consumer consumer : toSendTo) { consumer.accept(f); } } @Nonnull @Override public Disposable subscribe(Consumer eventConsumer) { synchronized (consumers) { consumers.add(eventConsumer); checkToUpdateSubscription(); } return new Disposer<>(this, eventConsumer); } private void dispose(Consumer eventConsumer) { synchronized (consumers) { consumers.remove(eventConsumer); checkToUpdateSubscription(); } } private static class Disposer implements Disposable { private ToggledEventSource eventSource; private Consumer eventConsumer; private Disposer(ToggledEventSource eventSource, Consumer eventConsumer) { this.eventSource = eventSource; this.eventConsumer = eventConsumer; } @Override public void dispose() { if (eventSource != null) { eventSource.dispose(eventConsumer); eventSource = null; eventConsumer = null; } } } /** * This class observes the given togglingSource and uses its state to set the given * ToggledEventSource as either active (forwarding events) or inactive (not * forwarding events). This class only keeps a Weak Reference to the ToggledEventSource * , and thus will not keep the object alive, and the reference is nulled, this object will * unsubscribe from the togglingSource. */ private static class ToggledEventSourceWeakReferenceConnection implements Consumer { private final WeakReference> toggledEventSourceRef; private final Disposable togglingEventSourceDisposable; ToggledEventSourceWeakReferenceConnection( EventSource togglingSource, WeakReference> toggledEventSourceWeakReference) { toggledEventSourceRef = toggledEventSourceWeakReference; togglingEventSourceDisposable = togglingSource.subscribe(this); } @Override public void accept(Boolean value) { final ToggledEventSource toggledEventSource = toggledEventSourceRef.get(); if (toggledEventSource != null) { toggledEventSource.setActive(value); } else { togglingEventSourceDisposable.dispose(); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy