Skip to content

Commit

Permalink
Use new DeviceChangeObserver in AdbDataSource
Browse files Browse the repository at this point in the history
Issue: #75
  • Loading branch information
mlopatkin committed Jan 6, 2022
1 parent f17b864 commit 9b256a9
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 36 deletions.
46 changes: 46 additions & 0 deletions src/name/mlopatkin/andlogview/device/DeviceChangeObserver.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package name.mlopatkin.andlogview.device;

import java.util.Objects;

/**
* The device change observer.
*/
Expand Down Expand Up @@ -43,4 +45,48 @@ default void onDeviceDisconnected(AdbDevice device) {
*/
default void onDeviceChanged(AdbDevice device) {
}

/**
* Creates a new observer that forwards events from only the given device to this observer.
*
* @param trackedDevice the device to get events from
* @return the new observer that only forwards events for the given device
*/
default DeviceChangeObserver scopeToSingleDevice(AdbDevice trackedDevice) {
DeviceChangeObserver parent = this;
return new DeviceChangeObserver() {
@Override
public void onDeviceConnected(AdbDevice device) {
if (isTrackedDevice(device)) {
parent.onDeviceConnected(device);
}
}

@Override
public void onDeviceDisconnected(AdbDevice device) {
if (isTrackedDevice(device)) {
parent.onDeviceDisconnected(device);
}
}

@Override
public void onDeviceChanged(AdbDevice device) {
if (isTrackedDevice(device)) {
parent.onDeviceChanged(device);
}
}

@Override
public DeviceChangeObserver scopeToSingleDevice(AdbDevice anotherTrackedDevice) {
if (!isTrackedDevice(anotherTrackedDevice)) {
throw new IllegalArgumentException("Can't scope an already scoped observer");
}
return this;
}

private boolean isTrackedDevice(AdbDevice device) {
return Objects.equals(trackedDevice.getSerialNumber(), device.getSerialNumber());
}
};
}
}
59 changes: 25 additions & 34 deletions src/name/mlopatkin/andlogview/liblogcat/ddmlib/AdbDataSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,58 +17,71 @@

import name.mlopatkin.andlogview.config.Configuration;
import name.mlopatkin.andlogview.device.AdbDevice;
import name.mlopatkin.andlogview.device.AdbDeviceList;
import name.mlopatkin.andlogview.device.DeviceChangeObserver;
import name.mlopatkin.andlogview.liblogcat.DataSource;
import name.mlopatkin.andlogview.liblogcat.Field;
import name.mlopatkin.andlogview.liblogcat.LogRecord;
import name.mlopatkin.andlogview.liblogcat.LogRecord.Buffer;
import name.mlopatkin.andlogview.liblogcat.RecordListener;
import name.mlopatkin.andlogview.liblogcat.SourceMetadata;
import name.mlopatkin.andlogview.liblogcat.ddmlib.AdbBuffer.BufferReceiver;

import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
import com.android.ddmlib.IDevice;
import name.mlopatkin.andlogview.utils.events.ScopedObserver;

import org.apache.log4j.Logger;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.util.EnumSet;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

public final class AdbDataSource implements DataSource, BufferReceiver {
private static final Logger logger = Logger.getLogger(AdbDataSource.class);

private @Nullable RecordListener<LogRecord> listener;

private final AdbDeviceManager deviceManager;
private final AdbDevice device;

private final AdbPidToProcessConverter converter;
private final EnumSet<Buffer> availableBuffers = EnumSet.noneOf(Buffer.class);
private final SourceMetadata sourceMetadata;
private final ScopedObserver deviceChangeObserver;

private @Nullable RecordListener<LogRecord> listener;
private boolean closed = false;

public AdbDataSource(AdbDeviceManager deviceManager, AdbDevice device) {
this.deviceManager = deviceManager;
public AdbDataSource(AdbDevice device, AdbDeviceList deviceList) {
assert device != null;
assert device.isOnline();
this.device = device;
converter = new AdbPidToProcessConverter(this.device.getIDevice());
for (Buffer buffer : Buffer.values()) {
setUpStream(buffer);
}
deviceManager.addDeviceChangeListener(deviceListener);
sourceMetadata = new AdbSourceMetadata(device.getIDevice());
}
deviceChangeObserver = deviceList.asObservable().addScopedObserver(new DeviceChangeObserver() {
@Override
public void onDeviceDisconnected(AdbDevice device) {
logger.debug("Device " + device.getSerialNumber() + " was disconnected, closing the source");
close();
}

private boolean closed = false;
@Override
public void onDeviceChanged(AdbDevice device) {
if (!device.isOnline()) {
logger.debug("Device " + device.getSerialNumber() + " is offline, closing the source");
close();
}
}
}.scopeToSingleDevice(device));
}

@Override
public void close() {
for (AdbBuffer stream : buffers) {
stream.close();
}
converter.close();
deviceChangeObserver.close();
closed = true;
}

Expand Down Expand Up @@ -161,26 +174,4 @@ public String toString() {
public SourceMetadata getMetadata() {
return sourceMetadata;
}

private IDeviceChangeListener deviceListener = new AdbDeviceManager.AbstractDeviceListener() {
@Override
public void deviceDisconnected(IDevice device) {
if (isTrackedDevice(device)) {
close();
deviceManager.removeDeviceChangeListener(this);
}
}

@Override
public void deviceChanged(IDevice device, int changeMask) {
if (isTrackedDevice(device) && (changeMask & IDevice.CHANGE_STATE) != 0 && device.isOffline()) {
close();
deviceManager.removeDeviceChangeListener(this);
}
}

private boolean isTrackedDevice(IDevice device) {
return Objects.equals(device.getSerialNumber(), AdbDataSource.this.device.getSerialNumber());
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import name.mlopatkin.andlogview.DeviceDisconnectedHandler;
import name.mlopatkin.andlogview.MainFrame;
import name.mlopatkin.andlogview.device.AdbDevice;
import name.mlopatkin.andlogview.device.AdbDeviceList;
import name.mlopatkin.andlogview.liblogcat.ddmlib.AdbDataSource;
import name.mlopatkin.andlogview.liblogcat.ddmlib.AdbDeviceManager;
import name.mlopatkin.andlogview.preferences.AdbConfigurationPref;
Expand All @@ -31,15 +32,17 @@ public class AdbDataSourceFactory {
// TODO(mlopatkin) get rid of MainFrame injection here.
private final MainFrame mainFrame;
private final SelectDeviceDialog.Factory selectDeviceDialogFactory;
private final AdbDeviceList adbDeviceList;
// TODO(mlopatkin) get rid of AdbDeviceManager injection here.
private final AdbDeviceManager adbDeviceManager;
private final AdbConfigurationPref adbConfigurationPref;

@Inject
AdbDataSourceFactory(MainFrame mainFrame, SelectDeviceDialog.Factory selectDeviceDialogFactory,
AdbDeviceManager adbDeviceManager, AdbConfigurationPref adbConfigurationPref) {
AdbDeviceList adbDeviceList, AdbDeviceManager adbDeviceManager, AdbConfigurationPref adbConfigurationPref) {
this.mainFrame = mainFrame;
this.selectDeviceDialogFactory = selectDeviceDialogFactory;
this.adbDeviceList = adbDeviceList;
this.adbDeviceManager = adbDeviceManager;
this.adbConfigurationPref = adbConfigurationPref;
}
Expand All @@ -54,6 +57,6 @@ public void selectDeviceAndOpenAsDataSource(Consumer<AdbDataSource> callback) {

public void openDeviceAsDataSource(AdbDevice device, Consumer<AdbDataSource> callback) {
DeviceDisconnectedHandler.startWatching(mainFrame, adbConfigurationPref, adbDeviceManager, device);
callback.accept(new AdbDataSource(adbDeviceManager, device));
callback.accept(new AdbDataSource(device, adbDeviceList));
}
}
11 changes: 11 additions & 0 deletions src/name/mlopatkin/andlogview/utils/events/Observable.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,15 @@ public interface Observable<T> {
* @param observer the observer to remove
*/
void removeObserver(@Nullable T observer);

/**
* Adds an observer and returns a {@link ScopedObserver} instance that can be used to remove the added observer.
*
* @param observer the observer to add
* @return the scoped observer that removes the added observer upon {@link ScopedObserver#close()} call
*/
default ScopedObserver addScopedObserver(T observer) {
addObserver(observer);
return () -> removeObserver(observer);
}
}
28 changes: 28 additions & 0 deletions src/name/mlopatkin/andlogview/utils/events/ScopedObserver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2022 Mikhail Lopatkin
*
* 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 name.mlopatkin.andlogview.utils.events;

/**
* A handle to remove some registered observer. Can be used with "try-with-resources" block.
*/
public interface ScopedObserver extends AutoCloseable {
/**
* Removes the previously registered observer. Does nothing if the observer is already removed.
*/
@Override
void close();
}

0 comments on commit 9b256a9

Please sign in to comment.