Use sqlite async for fetching files for gallery (#1397)

## Description

- Migrated from `sqflite` to `sqlite_async` when fetching files for
gallery (Home gallery, Collections page, Archive page & Add photos sheet
gallery ). This has made gallery load significantly faster and has
gotten rid of logs like `NativeAlloc concurrent copying GC freed
392872(19MB) AllocSpace objects, 139(4024KB) LOS objects, 38% free,
38MB/62MB, paused 2.868ms,93us total 324.898ms`. The initial load(100
files) and final load completes instantaneously even before the user
starts interacting with the app which has gotten rid of the jank which
was present before on the final load.
- In the 'Add Photos sheet', it takes time for the gallery to load when
using `sqflite` and images are rendered after this delay. After
migrating to `sqlite_async`, since the gallery loads instantly and
images are rendered right after, the animation of the bottom sheet
coming up became a bit janky. To make it smooth, have added a delay to
load gallery only after the bottom sheet is up fully.
 

## Tests
- [x] Did basic testing to make sure the migrations are working fine.
This commit is contained in:
Vishnu Mohandas 2024-04-10 15:34:06 +05:30 committed by GitHub
commit b8be65862f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 120 additions and 72 deletions

View file

@ -573,31 +573,34 @@ class FilesDB {
bool applyOwnerCheck = false,
}) async {
final stopWatch = EnteWatch('getAllPendingOrUploadedFiles')..start();
late String whereQuery;
late List<Object?>? whereArgs;
final order = (asc ?? false ? 'ASC' : 'DESC');
late String query;
late List<Object?>? args;
if (applyOwnerCheck) {
whereQuery = '$columnCreationTime >= ? AND $columnCreationTime <= ? '
query =
'SELECT * FROM $filesTable WHERE $columnCreationTime >= ? AND $columnCreationTime <= ? '
'AND ($columnOwnerID IS NULL OR $columnOwnerID = ?) '
'AND ($columnCollectionID IS NOT NULL AND $columnCollectionID IS NOT -1)'
' AND $columnMMdVisibility = ?';
whereArgs = [startTime, endTime, ownerID, visibility];
' AND $columnMMdVisibility = ? ORDER BY $columnCreationTime $order, $columnModificationTime $order';
args = [startTime, endTime, ownerID, visibility];
} else {
whereQuery =
'$columnCreationTime >= ? AND $columnCreationTime <= ? AND ($columnCollectionID IS NOT NULL AND $columnCollectionID IS NOT -1)'
' AND $columnMMdVisibility = ?';
whereArgs = [startTime, endTime, visibility];
query =
'SELECT * FROM $filesTable WHERE $columnCreationTime >= ? AND $columnCreationTime <= ? '
'AND ($columnCollectionID IS NOT NULL AND $columnCollectionID IS NOT -1)'
' AND $columnMMdVisibility = ? ORDER BY $columnCreationTime $order, $columnModificationTime $order';
args = [startTime, endTime, visibility];
}
final db = await instance.database;
final order = (asc ?? false ? 'ASC' : 'DESC');
final results = await db.query(
filesTable,
where: whereQuery,
whereArgs: whereArgs,
orderBy:
'$columnCreationTime ' + order + ', $columnModificationTime ' + order,
limit: limit,
);
if (limit != null) {
query += ' LIMIT ?';
args.add(limit);
}
final db = await instance.sqliteAsyncDB;
final results = await db.getAll(query, args);
_logger.info("message");
stopWatch.log('queryDone');
final files = convertToFiles(results);
stopWatch.log('convertDone');
@ -609,23 +612,25 @@ class FilesDB {
Future<FileLoadResult> getAllLocalAndUploadedFiles(
int startTime,
int endTime,
int ownerID, {
int endTime, {
int? limit,
bool? asc,
required DBFilterOptions filterOptions,
}) async {
final db = await instance.database;
final db = await instance.sqliteAsyncDB;
final order = (asc ?? false ? 'ASC' : 'DESC');
final results = await db.query(
filesTable,
where:
'$columnCreationTime >= ? AND $columnCreationTime <= ? AND ($columnMMdVisibility IS NULL OR $columnMMdVisibility = ?)'
' AND ($columnLocalID IS NOT NULL OR ($columnCollectionID IS NOT NULL AND $columnCollectionID IS NOT -1))',
whereArgs: [startTime, endTime, visibleVisibility],
orderBy:
'$columnCreationTime ' + order + ', $columnModificationTime ' + order,
limit: limit,
final args = [startTime, endTime, visibleVisibility];
String query =
'SELECT * FROM $filesTable WHERE $columnCreationTime >= ? AND $columnCreationTime <= ? AND ($columnMMdVisibility IS NULL OR $columnMMdVisibility = ?)'
' AND ($columnLocalID IS NOT NULL OR ($columnCollectionID IS NOT NULL AND $columnCollectionID IS NOT -1))'
' ORDER BY $columnCreationTime $order, $columnModificationTime $order';
if (limit != null) {
query += ' LIMIT ?';
args.add(limit);
}
final results = await db.getAll(
query,
args,
);
final files = convertToFiles(results);
final List<EnteFile> filteredFiles =
@ -658,19 +663,18 @@ class FilesDB {
bool? asc,
int visibility = visibleVisibility,
}) async {
final db = await instance.database;
final db = await instance.sqliteAsyncDB;
final order = (asc ?? false ? 'ASC' : 'DESC');
const String whereClause =
'$columnCollectionID = ? AND $columnCreationTime >= ? AND $columnCreationTime <= ?';
final List<Object> whereArgs = [collectionID, startTime, endTime];
final results = await db.query(
filesTable,
where: whereClause,
whereArgs: whereArgs,
orderBy:
'$columnCreationTime ' + order + ', $columnModificationTime ' + order,
limit: limit,
String query =
'SELECT * FROM $filesTable WHERE $columnCollectionID = ? AND $columnCreationTime >= ? AND $columnCreationTime <= ? ORDER BY $columnCreationTime $order, $columnModificationTime $order';
final List<Object> args = [collectionID, startTime, endTime];
if (limit != null) {
query += ' LIMIT ?';
args.add(limit);
}
final results = await db.getAll(
query,
args,
);
final files = convertToFiles(results);
return FileLoadResult(files, files.length == limit);
@ -1601,7 +1605,6 @@ class FilesDB {
bool dedupeByUploadId = true,
}) async {
final db = await instance.sqliteAsyncDB;
final result = await db.getAll(
'SELECT * FROM $filesTable ORDER BY $columnCreationTime DESC',
);

View file

@ -46,7 +46,6 @@ class HomeGalleryWidget extends StatelessWidget {
result = await FilesDB.instance.getAllLocalAndUploadedFiles(
creationStartTime,
creationEndTime,
ownerID!,
limit: limit,
asc: asc,
filterOptions: filterOptions,

View file

@ -108,6 +108,7 @@ class GalleryState extends State<Gallery> {
@override
void initState() {
super.initState();
_logTag =
"Gallery_${widget.tagPrefix}${kDebugMode ? "_" + widget.albumName! : ""}";
_logger = Logger(_logTag);
@ -172,7 +173,6 @@ class GalleryState extends State<Gallery> {
_setFilesAndReload(result.files);
}
});
super.initState();
}
void _setFilesAndReload(List<EnteFile> files) {

View file

@ -2,6 +2,7 @@ import "dart:math";
import "package:flutter/foundation.dart";
import "package:flutter/material.dart";
import "package:flutter_animate/flutter_animate.dart";
import "package:modal_bottom_sheet/modal_bottom_sheet.dart";
import "package:photos/core/configuration.dart";
import "package:photos/db/files_db.dart";
@ -15,6 +16,7 @@ import "package:photos/theme/colors.dart";
import "package:photos/theme/ente_theme.dart";
import "package:photos/ui/actions/collection/collection_file_actions.dart";
import "package:photos/ui/actions/collection/collection_sharing_actions.dart";
import "package:photos/ui/common/loading_widget.dart";
import "package:photos/ui/components/bottom_of_title_bar_widget.dart";
import "package:photos/ui/components/buttons/button_widget.dart";
import "package:photos/ui/components/models/button_type.dart";
@ -91,33 +93,9 @@ class AddPhotosPhotoWidget extends StatelessWidget {
showCloseButton: true,
),
Expanded(
child: Gallery(
inSelectionMode: true,
asyncLoader: (
creationStartTime,
creationEndTime, {
limit,
asc,
}) {
return FilesDB.instance
.getAllPendingOrUploadedFiles(
creationStartTime,
creationEndTime,
Configuration.instance.getUserID()!,
limit: limit,
asc: asc,
filterOptions: DBFilterOptions(
hideIgnoredForUpload: true,
dedupeUploadID: true,
ignoredCollectionIDs: hiddenCollectionIDs,
),
applyOwnerCheck: true,
);
},
tagPrefix: "pick_add_photos_gallery",
child: DelayedGallery(
hiddenCollectionIDs: hiddenCollectionIDs,
selectedFiles: selectedFiles,
showSelectAllByDefault: true,
sortAsyncFn: () => false,
),
),
],
@ -227,3 +205,71 @@ class AddPhotosPhotoWidget extends StatelessWidget {
}
}
}
class DelayedGallery extends StatefulWidget {
const DelayedGallery({
super.key,
required this.hiddenCollectionIDs,
required this.selectedFiles,
});
final Set<int> hiddenCollectionIDs;
final SelectedFiles selectedFiles;
@override
State<DelayedGallery> createState() => _DelayedGalleryState();
}
class _DelayedGalleryState extends State<DelayedGallery> {
bool _showGallery = false;
@override
void initState() {
super.initState();
Future.delayed(const Duration(milliseconds: 500), () {
if (mounted) {
setState(() {
_showGallery = true;
});
}
});
}
@override
Widget build(BuildContext context) {
if (_showGallery) {
return Gallery(
inSelectionMode: true,
asyncLoader: (
creationStartTime,
creationEndTime, {
limit,
asc,
}) {
return FilesDB.instance.getAllPendingOrUploadedFiles(
creationStartTime,
creationEndTime,
Configuration.instance.getUserID()!,
limit: limit,
asc: asc,
filterOptions: DBFilterOptions(
hideIgnoredForUpload: true,
dedupeUploadID: true,
ignoredCollectionIDs: widget.hiddenCollectionIDs,
),
applyOwnerCheck: true,
);
},
tagPrefix: "pick_add_photos_gallery",
selectedFiles: widget.selectedFiles,
showSelectAllByDefault: true,
sortAsyncFn: () => false,
).animate().fadeIn(
duration: const Duration(milliseconds: 175),
curve: Curves.easeOutCirc,
);
} else {
return const EnteLoadingWidget();
}
}
}