-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
files: Add drive detection support with udisks
Very barebones but at least support is there hooray!
- Loading branch information
Showing
7 changed files
with
287 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import 'dart:async'; | ||
|
||
import 'package:flutter/material.dart'; | ||
import 'package:udisks/udisks.dart'; | ||
|
||
class DriveProvider with ChangeNotifier { | ||
final UDisksClient _client = UDisksClient(); | ||
late final StreamSubscription _blockDeviceAddSub; | ||
late final StreamSubscription _blockDeviceRemoveSub; | ||
late final StreamSubscription _driveAddSub; | ||
late final StreamSubscription _driveRemoveSub; | ||
|
||
final List<UDisksBlockDevice> _blockDevices = []; | ||
final List<UDisksDrive> _drives = []; | ||
|
||
List<UDisksBlockDevice> get blockDevices => List.of(_blockDevices); | ||
List<UDisksDrive> get drives => List.of(_drives); | ||
|
||
Future<void> init() async { | ||
await _client.connect(); | ||
|
||
_blockDevices.addAll(_client.blockDevices); | ||
_drives.addAll(_client.drives); | ||
|
||
_blockDeviceAddSub = _client.blockDeviceAdded.listen(_onBlockDeviceAdded); | ||
_blockDeviceRemoveSub = | ||
_client.blockDeviceRemoved.listen(_onBlockDeviceRemoved); | ||
|
||
_driveAddSub = _client.driveAdded.listen(_onDriveAdded); | ||
_driveRemoveSub = _client.driveRemoved.listen(_onDriveRemoved); | ||
} | ||
|
||
@override | ||
Future<void> dispose() async { | ||
await _blockDeviceAddSub.cancel(); | ||
await _blockDeviceRemoveSub.cancel(); | ||
await _driveAddSub.cancel(); | ||
await _driveRemoveSub.cancel(); | ||
|
||
await _client.close(); | ||
|
||
super.dispose(); | ||
} | ||
|
||
void _onBlockDeviceAdded(UDisksBlockDevice event) { | ||
_blockDevices.add(event); | ||
notifyListeners(); | ||
} | ||
|
||
void _onBlockDeviceRemoved(UDisksBlockDevice event) { | ||
_blockDevices.removeWhere((e) => event.id == e.id); | ||
notifyListeners(); | ||
} | ||
|
||
void _onDriveAdded(UDisksDrive event) { | ||
_drives.add(event); | ||
notifyListeners(); | ||
} | ||
|
||
void _onDriveRemoved(UDisksDrive event) { | ||
_drives.removeWhere((e) => event.id == e.id); | ||
notifyListeners(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import 'dart:async'; | ||
import 'dart:convert'; | ||
|
||
import 'package:files/backend/providers.dart'; | ||
import 'package:filesize/filesize.dart'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:udisks/udisks.dart'; | ||
|
||
class DriveList extends StatelessWidget { | ||
final ValueChanged<String>? onDriveTap; | ||
|
||
const DriveList({this.onDriveTap, super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return AnimatedBuilder( | ||
animation: driveProvider, | ||
builder: (context, _) { | ||
return Column( | ||
children: driveProvider.blockDevices | ||
.where( | ||
(e) => | ||
!e.userspaceMountOptions.contains("x-gdu.hide") && | ||
!e.userspaceMountOptions.contains("x-gvfs-hide"), | ||
) | ||
.where((e) => !e.hintIgnore && e.filesystem != null) | ||
.map( | ||
(e) => _DriveTile( | ||
blockDevice: e, | ||
onTap: onDriveTap, | ||
), | ||
) | ||
.toList(), | ||
); | ||
}, | ||
); | ||
} | ||
} | ||
|
||
class _DriveTile extends StatefulWidget { | ||
final UDisksBlockDevice blockDevice; | ||
final ValueChanged<String>? onTap; | ||
|
||
const _DriveTile({ | ||
required this.blockDevice, | ||
this.onTap, | ||
}); | ||
|
||
@override | ||
State<_DriveTile> createState() => _DriveTileState(); | ||
} | ||
|
||
class _DriveTileState extends State<_DriveTile> { | ||
late Timer _pollingTimer; | ||
|
||
late String? mountPoint; | ||
|
||
@override | ||
void initState() { | ||
super.initState(); | ||
mountPoint = getMountPoint(); | ||
_pollingTimer = Timer.periodic(const Duration(milliseconds: 100), _onPoll); | ||
} | ||
|
||
@override | ||
void dispose() { | ||
_pollingTimer.cancel(); | ||
super.dispose(); | ||
} | ||
|
||
void _onPoll(Timer ref) { | ||
final String? currentMountPoint = getMountPoint(); | ||
|
||
if (mountPoint != currentMountPoint) { | ||
mountPoint = currentMountPoint; | ||
setState(() {}); | ||
} | ||
} | ||
|
||
String? getMountPoint() { | ||
return widget.blockDevice.filesystem!.mountPoints.isNotEmpty | ||
? widget.blockDevice.filesystem!.mountPoints.first.decode() | ||
: null; | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
String? mountPoint = widget.blockDevice.filesystem!.mountPoints.isNotEmpty | ||
? widget.blockDevice.filesystem!.mountPoints.first.decode() | ||
: null; | ||
|
||
final String? idLabel = widget.blockDevice.idLabel.isNotEmpty | ||
? widget.blockDevice.idLabel | ||
: null; | ||
final String? hintName = widget.blockDevice.hintName.isNotEmpty | ||
? widget.blockDevice.hintName | ||
: null; | ||
|
||
return ListTile( | ||
dense: true, | ||
leading: Icon( | ||
widget.blockDevice.drive?.ejectable == true ? Icons.usb : Icons.storage, | ||
size: 20, | ||
), | ||
title: Text( | ||
idLabel ?? hintName ?? "${filesize(widget.blockDevice.size, 1)} drive", | ||
), | ||
subtitle: mountPoint != null ? Text(mountPoint) : null, | ||
trailing: mountPoint != null | ||
? IconButton( | ||
onPressed: () async { | ||
await widget.blockDevice.filesystem!.unmount(); | ||
setState(() {}); | ||
}, | ||
icon: const Icon(Icons.eject), | ||
iconSize: 16, | ||
splashRadius: 16, | ||
) | ||
: null, | ||
onTap: () async { | ||
if (mountPoint == null) { | ||
mountPoint = await widget.blockDevice.filesystem!.mount(); | ||
setState(() {}); | ||
} | ||
|
||
widget.onTap?.call(mountPoint!); | ||
}, | ||
); | ||
} | ||
} | ||
|
||
extension on List<int> { | ||
String decode() { | ||
return utf8.decode(sublist(0, length - 1)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters