add ci build and caching of sftp files

This commit is contained in:
lukas-heiligenbrunner 2022-09-27 00:30:13 +02:00
parent 7a95780b5b
commit 84d34336ca
6 changed files with 91 additions and 41 deletions

39
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,39 @@
image: cirrusci/flutter
stages:
- build
flutter_build_android: #Job name
stage: build # kind of job
before_script:
- flutter packages get
- flutter clean
script:
- flutter build apk --target-platform android-arm64
artifacts:
paths:
- build/app/outputs/apk/release/app-release.apk
linux_build:
stage: build
script:
- apt-get update
- apt-get install -y --no-install-recommends cmake ninja-build clang build-essential pkg-config libgtk-3-dev liblzma-dev lcov libvlc-dev vlc libsecret-1-dev libjsoncpp-dev
- flutter config --enable-linux-desktop
- flutter packages get
- flutter build linux
artifacts:
paths:
- build/linux/x64/release/bundle/*
flutter_lint:
stage: build
script:
- flutter format . --output none --set-exit-if-changed
flutter_analyze:
stage: build
script:
- flutter analyze ./lib

View File

@ -16,7 +16,6 @@ class Folder {
Folder(this.items, this.self, this.parent); Folder(this.items, this.self, this.parent);
} }
abstract class DataProvider { abstract class DataProvider {
final List<String> validSuffix = [".jpg", ".jpeg", ".png"]; final List<String> validSuffix = [".jpg", ".jpeg", ".png"];

View File

@ -1,5 +1,4 @@
import 'dart:io'; import 'dart:io';
import 'dart:typed_data';
import 'package:dartssh2/dartssh2.dart'; import 'package:dartssh2/dartssh2.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
@ -8,6 +7,8 @@ import 'package:gallery/data_provider/data_provider.dart';
import 'dart:ui' as ui show Codec, ImmutableBuffer; import 'dart:ui' as ui show Codec, ImmutableBuffer;
import 'package:path_provider/path_provider.dart';
class SSHDataProvider extends DataProvider { class SSHDataProvider extends DataProvider {
final String host; final String host;
final int port; final int port;
@ -48,6 +49,8 @@ class SSHDataProvider extends DataProvider {
final items = await sftpClient!.listdir(dir.path); final items = await sftpClient!.listdir(dir.path);
List<Item> res = []; List<Item> res = [];
for (final val in items) { for (final val in items) {
if (val.filename == "." || val.filename == "..") continue;
if (validSuffix.any((suff) => val.filename.endsWith(suff))) { if (validSuffix.any((suff) => val.filename.endsWith(suff))) {
res.add(Item(false, Uri.file(dir.path + val.filename), val.filename)); res.add(Item(false, Uri.file(dir.path + val.filename), val.filename));
} else if (val.attr.isDirectory) { } else if (val.attr.isDirectory) {
@ -56,6 +59,10 @@ class SSHDataProvider extends DataProvider {
} }
} }
res.sort(
(a, b) => b.isFolder ? 1 : -1,
);
return Folder(res, dir.uri, dir.parent.uri); return Folder(res, dir.uri, dir.parent.uri);
} }
@ -66,13 +73,8 @@ class SSHDataProvider extends DataProvider {
} }
class _SSHImageProvider extends ImageProvider<_SSHImageProvider> { class _SSHImageProvider extends ImageProvider<_SSHImageProvider> {
/// Creates an object that decodes a [File] as an image. const _SSHImageProvider(this.uri, this.sftpClient, {this.scale = 1.0});
///
/// The arguments must not be null.
const _SSHImageProvider(this.uri, this.sftpClient, {this.scale = 1.0})
: assert(scale != null);
/// The file to decode into an image.
final Uri uri; final Uri uri;
final SftpClient sftpClient; final SftpClient sftpClient;
@ -113,14 +115,27 @@ class _SSHImageProvider extends ImageProvider<_SSHImageProvider> {
DecoderBufferCallback? decode, DecoderCallback? decodeDeprecated) async { DecoderBufferCallback? decode, DecoderCallback? decodeDeprecated) async {
assert(key == this); assert(key == this);
final file = await sftpClient.open(uri.toFilePath()); Directory tempDir = await getTemporaryDirectory();
// todo do not load whole image in ram, create tempfile instead. String tempPath = "${tempDir.path}/gallery";
final Uint8List bytes = await file.readBytes(); // check if temp file exists
Uint8List bytes;
final File tmpPic = File(tempPath + uri.toFilePath());
if (await tmpPic.exists()) {
// use temp file
bytes = await tmpPic.readAsBytes();
} else {
final file = await sftpClient.open(uri.toFilePath());
// todo do not load whole image in ram, create tempfile instead.
bytes = await file.readBytes();
await tmpPic.create(recursive: true);
await tmpPic.writeAsBytes(bytes);
}
if (bytes.lengthInBytes == 0) { if (bytes.lengthInBytes == 0) {
// The file may become available later. // The file may become available later.
PaintingBinding.instance.imageCache.evict(key); PaintingBinding.instance.imageCache.evict(key);
throw StateError('$file is empty and cannot be loaded as an image.'); throw StateError(
'bytes are empty is empty and cannot be loaded as an image.');
} }
if (decode != null) { if (decode != null) {

View File

@ -1,5 +1,3 @@
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';

View File

@ -1,10 +1,10 @@
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gallery/data_provider/data_provider.dart'; import 'package:gallery/data_provider/data_provider.dart';
class ImageTile extends StatelessWidget { class ImageTile extends StatelessWidget {
const ImageTile({Key? key, this.onClick, this.child, this.imageUri, this.dtaProvider}) : super(key: key); const ImageTile(
{Key? key, this.onClick, this.child, this.imageUri, this.dtaProvider})
: super(key: key);
final Function? onClick; final Function? onClick;
final Widget? child; final Widget? child;
final Uri? imageUri; final Uri? imageUri;
@ -16,26 +16,26 @@ class ImageTile extends StatelessWidget {
onTap: () { onTap: () {
onClick?.call(); onClick?.call();
}, },
child: Container(padding: const EdgeInsets.all(5), child: Container( child: Container(
decoration: imageUri == null padding: const EdgeInsets.all(5),
? const BoxDecoration( child: Container(
color: Colors.white, decoration: imageUri == null
borderRadius: ? const BoxDecoration(
BorderRadius.all(Radius.circular(10.0)), color: Colors.white,
boxShadow: [ borderRadius: BorderRadius.all(Radius.circular(10.0)),
BoxShadow( boxShadow: [
color: Colors.black12, BoxShadow(
spreadRadius: 2.0, color: Colors.black12,
blurRadius: 5.0), spreadRadius: 2.0,
]) blurRadius: 5.0),
: BoxDecoration( ])
image: DecorationImage( : BoxDecoration(
fit: BoxFit.cover, image: DecorationImage(
image: dtaProvider!.getImageProvider(imageUri!), fit: BoxFit.cover,
), image: dtaProvider!.getImageProvider(imageUri!),
), ),
child: child ),
)), child: child)),
); );
} }
} }

View File

@ -11,9 +11,9 @@ void main() {
class AppScrollBehavior extends MaterialScrollBehavior { class AppScrollBehavior extends MaterialScrollBehavior {
@override @override
Set<PointerDeviceKind> get dragDevices => { Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch, PointerDeviceKind.touch,
PointerDeviceKind.mouse, PointerDeviceKind.mouse,
}; };
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
@ -31,4 +31,3 @@ class MyApp extends StatelessWidget {
); );
} }
} }