Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
"start": "Start",
"stop": "Stop",
"error": {
"adb": {
"trackingFailed": "Failed to auto-refresh devices"
},
"scrcpy": {
"notFound": {
"title": "Scrcpy could not be found",
Expand Down
12 changes: 6 additions & 6 deletions lib/application/adb_result_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,18 @@ class AdbResultParser {
}
}

Future<AdbInitResult> parseInitResult(Future<ProcessResult> process) async {
Future<AdbTrackDevicesResult> parseTrackResult(Future<Process> Function() processSupplier) async {
try {
final result = await process;
return AdbInitResult.right(result.exitCode);
final result = await processSupplier();
return AdbTrackDevicesResult.right(result);
} on ProcessException catch (e) {
if (e.message.toLowerCase().contains("failed to find")) {
return AdbInitResult.left(AdbNotFoundError());
return AdbTrackDevicesResult.left(AdbNotFoundError());
} else {
return AdbInitResult.left(UnknownAdbError(exception: e));
return AdbTrackDevicesResult.left(UnknownAdbError(exception: e));
}
} catch (e) {
return AdbInitResult.left(UnknownAdbError(exception: e));
return AdbTrackDevicesResult.left(UnknownAdbError(exception: e));
}
}

Expand Down
4 changes: 2 additions & 2 deletions lib/application/extension/scrcpy_error_extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ extension ScrcpyErrorExtension on ScrcpyError {
return (this as UnknownScrcpyError).exception?.toString() ?? 'Unknown error';
case const (ScrcpyNotFoundError):
return 'Scrcpy not found';
case const (ScrcpyKillError):
return "Failed to stop scrcpy: ${(this as ScrcpyKillError).message}";
case const (ScrcpyStopError):
return "Failed to stop scrcpy: ${(this as ScrcpyStopError).exception}";
default:
return "Unknown error";
}
Expand Down
4 changes: 3 additions & 1 deletion lib/application/model/adb/adb_result.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import 'dart:io';

import 'package:fpdart/fpdart.dart';
import 'package:scrcpy_buddy/application/model/adb/adb_connect_result_status.dart';
import 'package:scrcpy_buddy/application/model/adb/adb_device.dart';
import 'package:scrcpy_buddy/application/model/adb/adb_error.dart';

typedef AdbInitResult = Either<AdbError, int>;
typedef AdbTrackDevicesResult = Either<AdbError, Process>;
typedef AdbDevicesResult = Either<AdbError, List<AdbDevice>>;
typedef AdbConnectResult = Either<AdbError, AdbConnectResultStatus>;
typedef AdbVersionInfoResult = Either<AdbError, String>;
Expand Down
9 changes: 9 additions & 0 deletions lib/application/model/process/stop_result.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'package:fpdart/fpdart.dart';

class ProcessStopError {
final Object? exception;

ProcessStopError([this.exception]);
}

typedef ProcessStopResult = Either<ProcessStopError, bool>;
4 changes: 2 additions & 2 deletions lib/application/model/scrcpy/scrcpy_error.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ class UnknownScrcpyError extends ScrcpyError {

class ScrcpyNotFoundError extends ScrcpyError {}

class ScrcpyKillError extends ScrcpyError {
class ScrcpyStopError extends ScrcpyError {
final Object? exception;

const ScrcpyKillError({this.exception});
const ScrcpyStopError([this.exception]);
}

class ScrcpyListAppsError extends ScrcpyError {
Expand Down
3 changes: 1 addition & 2 deletions lib/application/model/scrcpy/scrcpy_result.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ import 'package:fpdart/fpdart.dart';
import 'package:scrcpy_buddy/application/model/scrcpy/scrcpy_error.dart';

typedef ScrcpyResult = Either<ScrcpyError, Process>;
typedef ScrcpyStopResult = Either<ScrcpyError, bool>;
typedef ScrcpyListAppsResult = Either<ScrcpyError, List<String>>;
typedef ScrcpyListAppsResult = Either<ScrcpyError, List<String>>;
8 changes: 7 additions & 1 deletion lib/application/scrcpy_bloc/scrcpy_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,13 @@ class ScrcpyBloc extends Bloc<ScrcpyEvent, ScrcpyState> {
void _stop(StopScrcpyEvent event, _Emitter emit) {
final result = _processManager.stop(event.deviceSerial);
result.mapLeft(
(left) => emit(ScrcpyStopFailedState(deviceSerial: event.deviceSerial, error: left, devices: _runningDevices)),
(left) => emit(
ScrcpyStopFailedState(
deviceSerial: event.deviceSerial,
error: ScrcpyStopError(left.exception),
devices: _runningDevices,
),
),
);
result.map((_) => emit(ScrcpyStopSuccessState(deviceSerial: event.deviceSerial, devices: _runningDevices)));
}
Expand Down
2 changes: 1 addition & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class _MyAppState extends State<MyApp> {
providers: [
BlocProvider(create: (_) => ProfilesBloc(_settings, _objectBox.profileBox, _argsMap)),
BlocProvider(create: (context) => ScrcpyBloc(context.read(), context.read(), context.read())),
BlocProvider(create: (context) => DevicesBloc(context.read(), _settings.adbExecutable)),
BlocProvider(create: (context) => DevicesBloc(context.read(), context.read(), _settings.adbExecutable)),
],
child: child!,
);
Expand Down
66 changes: 46 additions & 20 deletions lib/presentation/devices/bloc/devices_bloc.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import 'dart:async';
import 'dart:io';

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:scrcpy_buddy/application/model/adb/adb_device.dart';
import 'package:scrcpy_buddy/application/model/adb/adb_error.dart';
import 'package:scrcpy_buddy/service/adb_service.dart';
import 'package:scrcpy_buddy/service/running_process_manager.dart';
import 'package:streaming_shared_preferences/streaming_shared_preferences.dart';

part 'devices_event.dart';
Expand All @@ -13,37 +16,60 @@ part 'devices_state.dart';
typedef _Emitter = Emitter<DevicesState>;

class DevicesBloc extends Bloc<DevicesEvent, DevicesState> {
DevicesBloc(this._adbService, this._adbPath) : super(const DevicesInitial()) {
DevicesBloc(this._runningProcessManager, this._adbService, this._adbPath) : super(const DevicesInitial()) {
on<InitDeviceTracking>((_, emit) => _startTracking(emit));
on<RestartTracking>(_restartTracking);
on<LoadDevices>(_onLoadDevices);
on<ToggleDeviceSelection>(_onToggleDeviceSelection);
}

bool _initDone = false;
bool _isTracking = false;
final AdbService _adbService;
final RunningProcessManager _runningProcessManager;
final List<AdbDevice> _devices = [];
final Set<String> _selectedDeviceSerials = {};
final Preference<String> _adbPath;

Future<void> _onLoadDevices(LoadDevices event, _Emitter emit) async {
static const String adbTrackDevicesKey = "adb-track-devices";

@override
Future<void> close() {
_stopTracking();
return super.close();
}

void _stopTracking() {
_runningProcessManager
.stop(adbTrackDevicesKey)
.mapLeft((error) => debugPrint(error.exception?.toString() ?? 'Unknown stop error'))
.map((stopped) => kDebugMode ? debugPrint("Stopped: $stopped") : null);

_isTracking = false;
}

Future<void> _restartTracking(RestartTracking _, _Emitter emit) async {
if (_isTracking) _stopTracking();
await _startTracking(emit);
}

Future<void> _startTracking(_Emitter emit) async {
if (_isTracking) {
emit(InitDeviceTrackingSuccess());
return;
}
final trackDevicesResult = await _adbService.startTrackDevices(_adbPath.getValue());
trackDevicesResult.fold((error) => emit(InitDeviceTrackingFailed(adbError: error, message: error.toString())), (
process,
) {
_isTracking = true;
_runningProcessManager.add(adbTrackDevicesKey, process);
emit(InitDeviceTrackingSuccess());
});
}

Future<void> _onLoadDevices(_, _Emitter emit) async {
try {
emit(DevicesLoading());
if (!_initDone) {
final initResult = await _adbService.init(_adbPath.getValue());
if (initResult.isLeft()) {
final error = initResult.fold((l) => l, (r) => null);
emit(
DevicesUpdateError(
devices: _devices,
selectedDeviceSerials: _selectedDeviceSerials,
adbError: error,
message: error.toString(),
),
);
return;
} else {
_initDone = true;
}
}
final devicesResult = await _adbService.devices(_adbPath.getValue());
devicesResult.fold(
(error) => emit(
Expand Down
4 changes: 4 additions & 0 deletions lib/presentation/devices/bloc/devices_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ sealed class DevicesEvent extends Equatable {
List<Object> get props => [];
}

final class InitDeviceTracking extends DevicesEvent {}

final class RestartTracking extends DevicesEvent {}

final class LoadDevices extends DevicesEvent {}

final class ToggleDeviceSelection extends DevicesEvent {
Expand Down
18 changes: 17 additions & 1 deletion lib/presentation/devices/bloc/devices_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ final class DevicesInitial extends DevicesState {
List<Object> get props => [];
}

final class InitDeviceTrackingSuccess extends DevicesState {
const InitDeviceTrackingSuccess();

@override
List<Object?> get props => [];
}

final class InitDeviceTrackingFailed extends DevicesState {
final AdbError? adbError;
final String? message;

const InitDeviceTrackingFailed({this.adbError, this.message});

@override
List<Object?> get props => [adbError, message];
}

final class DevicesLoading extends DevicesState {
const DevicesLoading();

Expand All @@ -31,7 +48,6 @@ final class DevicesUpdateSuccess extends DevicesBaseUpdateState {

@override
List<Object?> get props => [devices, selectedDeviceSerials];

}

final class DevicesUpdateError extends DevicesBaseUpdateState {
Expand Down
Loading