mirror of
https://github.com/johrpan/musicus_mobile.git
synced 2025-10-26 10:47:25 +01:00
Add basic files selector
This required to add a platform channel with a method to get the toplevel storage devices. The app will need to request the WRITE_EXTERNAL_STORAGE permission later. Also the dependency on "path" was introduced.
This commit is contained in:
parent
02e283b8cc
commit
24a4911665
4 changed files with 213 additions and 2 deletions
|
|
@ -1,6 +1,8 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="de.johrpan.musicus">
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<application
|
||||
android:name="io.flutter.app.FlutterApplication"
|
||||
android:label="Musicus"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,39 @@
|
|||
package de.johrpan.musicus
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugins.GeneratedPluginRegistrant
|
||||
|
||||
class MainActivity: FlutterActivity() {
|
||||
class MainActivity : FlutterActivity() {
|
||||
private val CHANNEL = "de.johrpan.musicus/platform"
|
||||
|
||||
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
|
||||
GeneratedPluginRegistrant.registerWith(flutterEngine);
|
||||
GeneratedPluginRegistrant.registerWith(flutterEngine)
|
||||
|
||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
|
||||
if (call.method == "getStorageRoots") {
|
||||
result.success(getStorageRoots())
|
||||
} else {
|
||||
result.notImplemented()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getStorageRoots(): ArrayList<String> {
|
||||
val result = ArrayList<String>()
|
||||
|
||||
ContextCompat.getExternalFilesDirs(this, null).forEach {
|
||||
val path = it.absolutePath;
|
||||
val index = path.lastIndexOf("/Android/data/")
|
||||
|
||||
if (index > 0) {
|
||||
result.add(path.substring(0, index))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
|
|
|||
181
lib/selectors/files.dart
Normal file
181
lib/selectors/files.dart
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
class FilesSelector extends StatefulWidget {
|
||||
@override
|
||||
_FilesSelectorState createState() => _FilesSelectorState();
|
||||
}
|
||||
|
||||
class _FilesSelectorState extends State<FilesSelector> {
|
||||
static const platform = MethodChannel('de.johrpan.musicus/platform');
|
||||
|
||||
List<Directory> storageRoots = [];
|
||||
List<Directory> directories = [];
|
||||
List<FileSystemEntity> contents = [];
|
||||
Set<String> selectedPaths = {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
platform.invokeListMethod<String>('getStorageRoots').then((sr) {
|
||||
setState(() {
|
||||
storageRoots = sr.map((path) => Directory(path)).toList();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget body;
|
||||
|
||||
if (directories.isEmpty) {
|
||||
if (storageRoots != null) {
|
||||
body = ListView(
|
||||
children: storageRoots
|
||||
.map((dir) => ListTile(
|
||||
leading: const Icon(Icons.storage),
|
||||
title: Text(dir.path),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
directories.add(dir);
|
||||
});
|
||||
|
||||
openDirectory(dir);
|
||||
},
|
||||
))
|
||||
.toList(),
|
||||
);
|
||||
} else {
|
||||
body = Container();
|
||||
}
|
||||
} else {
|
||||
if (contents != null) {
|
||||
body = ListView(
|
||||
children: contents.map((fse) {
|
||||
Widget result;
|
||||
|
||||
if (fse is Directory) {
|
||||
result = ListTile(
|
||||
leading: const Icon(Icons.folder),
|
||||
title: Text(path.basename(fse.path)),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
directories.add(fse);
|
||||
});
|
||||
|
||||
openDirectory(fse);
|
||||
},
|
||||
);
|
||||
} else if (fse is File) {
|
||||
result = CheckboxListTile(
|
||||
value: selectedPaths.contains(fse.path),
|
||||
secondary: Icon(Icons.insert_drive_file),
|
||||
title: Text(path.basename(fse.path)),
|
||||
onChanged: (selected) {
|
||||
setState(() {
|
||||
if (selected) {
|
||||
selectedPaths.add(fse.path);
|
||||
} else {
|
||||
selectedPaths.remove(fse.path);
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}).toList(),
|
||||
);
|
||||
} else {
|
||||
body = Container();
|
||||
}
|
||||
}
|
||||
|
||||
return WillPopScope(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Choose files'),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
child: Text('DONE'),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, selectedPaths);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
Material(
|
||||
elevation: 2.0,
|
||||
child: ListTile(
|
||||
leading: directories.isNotEmpty ? IconButton(
|
||||
icon: const Icon(Icons.arrow_upward),
|
||||
onPressed: up,
|
||||
) : null,
|
||||
title: Text(directories.isEmpty
|
||||
? 'Storage devices'
|
||||
: directories.last.path),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: body,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
onWillPop: () {
|
||||
if (directories.isNotEmpty) {
|
||||
up();
|
||||
return Future.value(false);
|
||||
} else {
|
||||
return Future.value(true);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> openDirectory(Directory directory) async {
|
||||
setState(() {
|
||||
contents.clear();
|
||||
});
|
||||
|
||||
final fses = await directory.list().toList();
|
||||
fses.sort((fse1, fse2) {
|
||||
int compareBasenames() =>
|
||||
path.basename(fse1.path).compareTo(path.basename(fse2.path));
|
||||
|
||||
if (fse1 is Directory) {
|
||||
if (fse2 is Directory) {
|
||||
return compareBasenames();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else if (fse2 is Directory) {
|
||||
return 1;
|
||||
} else {
|
||||
return compareBasenames();
|
||||
}
|
||||
});
|
||||
|
||||
setState(() {
|
||||
contents = fses;
|
||||
});
|
||||
}
|
||||
|
||||
void up() {
|
||||
if (directories.isNotEmpty) {
|
||||
setState(() {
|
||||
directories.removeLast();
|
||||
});
|
||||
|
||||
if (directories.isNotEmpty) {
|
||||
openDirectory(directories.last);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ dependencies:
|
|||
flutter:
|
||||
sdk: flutter
|
||||
moor_flutter:
|
||||
path:
|
||||
rxdart:
|
||||
|
||||
dev_dependencies:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue