diff --git a/src/name/mlopatkin/andlogview/liblogcat/ddmlib/AdbDataSource.java b/src/name/mlopatkin/andlogview/liblogcat/ddmlib/AdbDataSource.java index 19682980..50e2292d 100644 --- a/src/name/mlopatkin/andlogview/liblogcat/ddmlib/AdbDataSource.java +++ b/src/name/mlopatkin/andlogview/liblogcat/ddmlib/AdbDataSource.java @@ -38,7 +38,7 @@ import java.util.Set; import java.util.concurrent.Executor; -public final class AdbDataSource implements DataSource, BufferReceiver { +public class AdbDataSource implements DataSource, BufferReceiver { /** * The reason for the data source to become invalid. */ @@ -57,13 +57,12 @@ public interface StateObserver { * * @param reason the reason for invalidation */ - void onDataSourceInvalidated(InvalidationReason reason); + default void onDataSourceInvalidated(InvalidationReason reason) {} /** * Called when the data source is closed whether normally or because of invalidation. */ - default void onDataSourceClosed() { - } + default void onDataSourceClosed() {} } private static final Logger logger = Logger.getLogger(AdbDataSource.class); diff --git a/src/name/mlopatkin/andlogview/ui/status/SourceStatusPresenter.java b/src/name/mlopatkin/andlogview/ui/status/SourceStatusPresenter.java index 1b31e26f..31eea155 100644 --- a/src/name/mlopatkin/andlogview/ui/status/SourceStatusPresenter.java +++ b/src/name/mlopatkin/andlogview/ui/status/SourceStatusPresenter.java @@ -17,16 +17,15 @@ package name.mlopatkin.andlogview.ui.status; import name.mlopatkin.andlogview.DataSourceHolder; +import name.mlopatkin.andlogview.liblogcat.ddmlib.AdbDataSource; import name.mlopatkin.andlogview.logmodel.DataSource; import name.mlopatkin.andlogview.ui.device.AdbServicesStatus; import name.mlopatkin.andlogview.ui.mainframe.MainFrameScoped; import name.mlopatkin.andlogview.utils.CommonChars; -import name.mlopatkin.andlogview.utils.UiThreadScheduler; import name.mlopatkin.andlogview.utils.events.Observable; import org.checkerframework.checker.nullness.qual.Nullable; -import java.time.Duration; import java.util.function.Consumer; import javax.inject.Inject; @@ -39,7 +38,12 @@ public class SourceStatusPresenter { private final DataSourceHolder dataSourceHolder; private final AdbServicesStatus adbServicesStatus; private final View view; - private final UiThreadScheduler updateScheduler; + private final AdbDataSource.StateObserver adbDataSourceObserver = new AdbDataSource.StateObserver() { + @Override + public void onDataSourceClosed() { + updateSourceStatus(); + } + }; interface View { void showWaitingStatus(String statusText); @@ -54,12 +58,10 @@ interface View { DataSourceHolder dataSourceHolder, AdbServicesStatus adbServicesStatus, View view, - SourcePopupMenuPresenter popupMenuPresenter, - UiThreadScheduler updateScheduler) { + SourcePopupMenuPresenter popupMenuPresenter) { this.dataSourceHolder = dataSourceHolder; this.adbServicesStatus = adbServicesStatus; this.view = view; - this.updateScheduler = updateScheduler; view.popupMenuAction().addObserver(popupMenuPresenter::showPopupMenuIfNeeded); } @@ -68,8 +70,8 @@ interface View { void init() { adbServicesStatus.asObservable().addObserver(this::onAdbServicesStatusChanged); dataSourceHolder.asObservable().addObserver(this::onDataSourceChanged); - if (dataSourceHolder.getDataSource() != null) { - scheduleUpdates(); + if (dataSourceHolder.getDataSource() instanceof AdbDataSource adbDataSource) { + adbDataSource.asStateObservable().addObserver(adbDataSourceObserver); } updateSourceStatus(); } @@ -79,7 +81,7 @@ private void onAdbServicesStatusChanged(AdbServicesStatus.StatusValue statusValu } private void updateSourceStatus() { - DataSource dataSource = dataSourceHolder.getDataSource(); + var dataSource = dataSourceHolder.getDataSource(); if (dataSource != null) { view.showSourceStatus(dataSource.toString()); } else { @@ -102,13 +104,12 @@ private String evaluateSourceStatus() { } private void onDataSourceChanged(@Nullable DataSource oldSource, DataSource newSource) { - if (oldSource == null) { - scheduleUpdates(); + if (oldSource instanceof AdbDataSource adbDataSource) { + adbDataSource.asStateObservable().removeObserver(adbDataSourceObserver); + } + if (newSource instanceof AdbDataSource adbDataSource) { + adbDataSource.asStateObservable().addObserver(adbDataSourceObserver); } updateSourceStatus(); } - - private void scheduleUpdates() { - updateScheduler.postRepeatableTask(this::updateSourceStatus, Duration.ofSeconds(2)); - } } diff --git a/test/name/mlopatkin/andlogview/ui/status/SourceStatusPresenterTest.java b/test/name/mlopatkin/andlogview/ui/status/SourceStatusPresenterTest.java index 0d8470a0..3979dd92 100644 --- a/test/name/mlopatkin/andlogview/ui/status/SourceStatusPresenterTest.java +++ b/test/name/mlopatkin/andlogview/ui/status/SourceStatusPresenterTest.java @@ -16,6 +16,7 @@ package name.mlopatkin.andlogview.ui.status; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.contains; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; @@ -24,9 +25,9 @@ import static org.mockito.Mockito.when; import name.mlopatkin.andlogview.DataSourceHolder; +import name.mlopatkin.andlogview.liblogcat.ddmlib.AdbDataSource; import name.mlopatkin.andlogview.logmodel.DataSource; import name.mlopatkin.andlogview.ui.device.AdbServicesStatus; -import name.mlopatkin.andlogview.utils.MockUiThreadScheduler; import name.mlopatkin.andlogview.utils.events.Subject; import org.junit.jupiter.api.BeforeEach; @@ -39,9 +40,7 @@ @ExtendWith(MockitoExtension.class) class SourceStatusPresenterTest { - - @Mock - DataSourceHolder dataSourceHolder; + DataSourceHolder dataSourceHolder = new DataSourceHolder(); @Mock AdbServicesStatus adbServicesStatus; @@ -52,16 +51,12 @@ class SourceStatusPresenterTest { @Mock SourcePopupMenuPresenter popupMenuPresenter; - final MockUiThreadScheduler uiScheduler = new MockUiThreadScheduler(); - final Subject adbServicesStatusObservers = new Subject<>(); - final Subject dataSourceHolderObservers = new Subject<>(); final Subject> viewObservers = new Subject<>(); @BeforeEach void setUp() { lenient().when(adbServicesStatus.asObservable()).thenReturn(adbServicesStatusObservers.asObservable()); - lenient().when(dataSourceHolder.asObservable()).thenReturn(dataSourceHolderObservers.asObservable()); lenient().when(view.popupMenuAction()).thenReturn(viewObservers.asObservable()); lenient().when(adbServicesStatus.getStatus()).thenReturn(AdbServicesStatus.StatusValue.notInitialized()); @@ -172,20 +167,65 @@ void openedDataSourceReplacesPrevious() { verify(view).showSourceStatus("new DS"); } + @Test + void closedAdbDataSourceIsNotified() { + var observers = withAdbDataSource("adb", "disconnected"); + + createPresenter(); + + verify(view).showSourceStatus("adb"); + + observers.forEach(AdbDataSource.StateObserver::onDataSourceClosed); + + verify(view).showSourceStatus("disconnected"); + } + + @Test + void unsubscribesFromAdbDataSource() { + var observers = withAdbDataSource("1", "1"); + + createPresenter(); + onDataSourceOpened("other"); + + assertThat(observers).isEmpty(); + } + + @Test + void subscribesToOpenedAdbDataSource() { + withDataSource("initial"); + + createPresenterNotTrackingView(); + var observers = onAdbDataSourceOpened("adb", "disconnected"); + + verify(view).showSourceStatus("adb"); + + observers.forEach(AdbDataSource.StateObserver::onDataSourceClosed); + + verify(view).showSourceStatus("disconnected"); + } + private void createPresenter() { var presenter = - new SourceStatusPresenter(dataSourceHolder, adbServicesStatus, view, popupMenuPresenter, uiScheduler); + new SourceStatusPresenter(dataSourceHolder, adbServicesStatus, view, popupMenuPresenter); presenter.init(); } - private DataSource withDataSource(String name) { var dataSource = mock(DataSource.class); when(dataSource.toString()).thenReturn(name); - when(dataSourceHolder.getDataSource()).thenReturn(dataSource); + dataSourceHolder.setDataSource(dataSource); return dataSource; } + private Subject withAdbDataSource(String name, String disconnectedName) { + var stateObservers = new Subject(); + var dataSource = mock(AdbDataSource.class); + when(dataSource.toString()).thenReturn(name, disconnectedName); + when(dataSource.asStateObservable()).thenReturn(stateObservers.asObservable()); + dataSourceHolder.setDataSource(dataSource); + return stateObservers; + } + private void withAdbInitializing() { when(adbServicesStatus.getStatus()).thenReturn(AdbServicesStatus.StatusValue.initializing()); } @@ -213,10 +253,10 @@ private void onAdbFailure(String failure) { } private void onDataSourceOpened(String name) { - var newDataSource = withDataSource(name); + withDataSource(name); + } - for (DataSourceHolder.Observer obs : dataSourceHolderObservers) { - obs.onDataSourceChanged(null, newDataSource); - } + private Subject onAdbDataSourceOpened(String name, String disconnectedName) { + return withAdbDataSource(name, disconnectedName); } }