Skip to content

Commit

Permalink
Implement background tasks (#152)
Browse files Browse the repository at this point in the history
  • Loading branch information
Airyzz authored Nov 15, 2023
1 parent 92ca11c commit c3a2774
Show file tree
Hide file tree
Showing 21 changed files with 735 additions and 12 deletions.
2 changes: 2 additions & 0 deletions commet/android/app/src/debug/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
</manifest>
2 changes: 2 additions & 0 deletions commet/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="chat.commet.commetapp">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<application
android:label="Commet"
android:name="${applicationName}"
Expand Down
2 changes: 2 additions & 0 deletions commet/android/app/src/profile/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
</manifest>
9 changes: 9 additions & 0 deletions commet/lib/config/preferences.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class Preferences {
static const String _developerMode = "developer_mode";
static const String _tenorGifSearch = "enable_tenor_gif_search";
static const String _gifSearchProxyUrl = "gif_search_proxy_url";
static const String _lastDownloadLocation = "last_download_location";
final StreamController _onSettingChanged = StreamController.broadcast();
Stream get onSettingChanged => _onSettingChanged.stream;

Expand Down Expand Up @@ -115,4 +116,12 @@ class Preferences {
await _preferences!.setBool(_tenorGifSearch, value);
_onSettingChanged.add(null);
}

String? get lastDownloadLocation =>
_preferences!.getString(_lastDownloadLocation);

Future<void> setLastDownloadLocation(String value) async {
await _preferences!.setString(_lastDownloadLocation, value);
_onSettingChanged.add(null);
}
}
2 changes: 2 additions & 0 deletions commet/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:commet/config/preferences.dart';
import 'package:commet/diagnostic/diagnostics.dart';
import 'package:commet/ui/pages/login/login_page.dart';
import 'package:commet/ui/pages/main/main_page.dart';
import 'package:commet/utils/background_tasks/background_task_manager.dart';
import 'package:commet/utils/emoji/unicode_emoji.dart';
import 'package:commet/utils/notification/notification_manager.dart';
import 'package:commet/utils/notification/notifier.dart';
Expand All @@ -30,6 +31,7 @@ final GlobalKey<NavigatorState> navigator = GlobalKey();
FileCacheInstance fileCache = FileCacheInstance();
Preferences preferences = Preferences();
NotificationManager notificationManager = NotificationManager();
BackgroundTaskManager backgroundTaskManager = BackgroundTaskManager();
Diagnostics diagnostics = Diagnostics();
ClientManager? clientManager;

Expand Down
189 changes: 189 additions & 0 deletions commet/lib/ui/atoms/floating_tile.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';

class FloatingTile extends StatefulWidget {
const FloatingTile(
{super.key,
required this.child,
this.initialPosition = Alignment.topRight});
final Alignment initialPosition;
final Widget child;

@override
State<FloatingTile> createState() => _FloatingTileState();
}

class _FloatingTileState extends State<FloatingTile> {
late OverlayEntry entry;

@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) => addOverlay());
super.initState();
}

@override
void dispose() {
if (kDebugMode) {
print("Floating tile is being diposed!");
}
entry.remove();
super.dispose();
}

void addOverlay() {
if (kDebugMode) {
print("added floating tile overlay");
}
entry = OverlayEntry(builder: buildOverlay);
Overlay.of(context).insert(entry);
}

Widget buildOverlay(BuildContext context) {
var size = MediaQuery.of(context).size;
var padding = MediaQuery.of(context).padding;
return TileOverlay(
initialLayoutSize: Rect.fromLTWH(0, 0, size.width, size.height),
safeAreaPadding: padding,
child: widget.child,
);
}

@override
Widget build(BuildContext context) {
return Container();
}
}

class TileOverlay extends StatefulWidget {
const TileOverlay(
{super.key,
required this.child,
this.initialPosition = Alignment.topRight,
this.initialLayoutSize = Rect.zero,
this.safeAreaPadding = EdgeInsets.zero});
final Rect initialLayoutSize;
final EdgeInsets safeAreaPadding;
final Alignment initialPosition;
final Widget child;
@override
State<TileOverlay> createState() => _TileOverlayState();
}

class _TileOverlayState extends State<TileOverlay> {
Offset offset = const Offset(0, 0);
Offset startPosition = const Offset(0, 0);

GlobalKey childKey = GlobalKey();

@override
void initState() {
super.initState();

switch (widget.initialPosition) {
case Alignment.topRight:
offset = Offset(widget.initialLayoutSize.width, 0);
break;
case Alignment.bottomLeft:
offset = Offset(0, widget.initialLayoutSize.height);
break;
case Alignment.bottomRight:
offset = Offset(
widget.initialLayoutSize.width, widget.initialLayoutSize.height);
break;
case Alignment.topLeft:
offset = Offset.zero;
break;
}

offset = getNearestPoint(
offset, Offset.zero, widget.initialLayoutSize, widget.safeAreaPadding);

SchedulerBinding.instance.addPostFrameCallback(postFrameCallback);
}

void postFrameCallback(_) {
updatePoint(offset);
}

Offset getNearestPoint(
Offset currentPos, Offset size, Rect bounds, EdgeInsets safeAreaPadding) {
var p = 10.0;

var rect = Rect.fromLTRB(
bounds.left + p + safeAreaPadding.left,
bounds.top + p + safeAreaPadding.top,
bounds.right - p - size.dx - safeAreaPadding.right,
bounds.bottom - p - size.dy - safeAreaPadding.bottom);

var points = [
Offset(rect.left, rect.top),
Offset(rect.right, rect.top),
Offset(rect.left, rect.bottom),
Offset(rect.right, rect.bottom)
];

var minDistance = 9999999999.0;
var pos = currentPos;
for (var point in points) {
var dist = (point - currentPos).distance;
if (dist < minDistance) {
minDistance = dist;
pos = point;
}
}

return pos;
}

void updatePoint(Offset currentPoint) {
var size = MediaQuery.of(context).size;

var childSize = childKey.currentContext?.size;
if (childSize == null) {
return;
}

var point = getNearestPoint(
currentPoint,
Offset(childSize.width, childSize.height),
Rect.fromLTWH(0, 0, size.width, size.height),
widget.safeAreaPadding);

if ((point - offset).distance > 1) {
setState(() {
offset = point;
});
}
}

@override
Widget build(BuildContext context) {
SchedulerBinding.instance.addPostFrameCallback(postFrameCallback);
return NotificationListener(
onNotification: (SizeChangedLayoutNotification notification) {
return false;
},
child: AnimatedPositioned(
left: offset.dx,
top: offset.dy,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOutCubic,
child: GestureDetector(
onPanUpdate: (details) {
setState(() {
offset = details.globalPosition - startPosition;
});
},
onPanStart: (details) {
startPosition = details.localPosition;
},
onPanEnd: (details) {
var pos = offset + details.velocity.pixelsPerSecond;
updatePoint(pos);
},
child: Container(key: childKey, child: widget.child))),
);
}
}
42 changes: 35 additions & 7 deletions commet/lib/ui/atoms/message_attachment.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import 'package:commet/client/attachment.dart';
import 'package:commet/config/build_config.dart';
import 'package:commet/main.dart';
import 'package:commet/ui/atoms/lightbox.dart';
import 'package:commet/ui/molecules/video_player/video_player.dart';
import 'package:commet/utils/background_tasks/background_task_manager.dart';
import 'package:commet/utils/file_utils.dart';
import 'package:commet/utils/mime.dart';
import 'package:commet/utils/text_utils.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:tiamat/config/style/theme_extensions.dart';
import 'package:tiamat/tiamat.dart';
import 'package:tiamat/tiamat.dart' as tiamat;
import 'package:url_launcher/url_launcher.dart';

import 'package:path/path.dart' as p;

class MessageAttachment extends StatefulWidget {
const MessageAttachment(this.attachment,
Expand Down Expand Up @@ -169,12 +174,7 @@ class _MessageAttachmentState extends State<MessageAttachment> {
icon: Icons.download,
onPressed: () async {
if (widget.attachment is FileAttachment) {
var attachment = widget.attachment as FileAttachment;
var result = await FilePicker.platform
.saveFile(fileName: widget.attachment.name);
if (result != null) {
attachment.provider.save(result);
}
downloadAttachment(widget.attachment as FileAttachment);
}
},
),
Expand All @@ -184,4 +184,32 @@ class _MessageAttachmentState extends State<MessageAttachment> {
),
);
}

Future<void> downloadAttachment(FileAttachment attachment) async {
var path = await FileUtils.getSaveFilePath(fileName: attachment.name);
if (path == null) return;

backgroundTaskManager.addTask(AsyncTask(
downloadTask(attachment, path),
"Downloading: ${widget.attachment.name}",
action: () {
var openPath = path;
if (BuildConfig.DESKTOP) {
openPath = p.dirname(path);
}
launchUrl(Uri.file(openPath), mode: LaunchMode.platformDefault);
},
isActionReady: () => true,
));
}

Future<BackgroundTaskStatus> downloadTask(
FileAttachment attachment, String path) async {
try {
await attachment.provider.save(path);
} catch (_) {
return BackgroundTaskStatus.failed;
}
return BackgroundTaskStatus.completed;
}
}
3 changes: 2 additions & 1 deletion commet/lib/ui/molecules/timeline_event.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:commet/config/build_config.dart';
import 'package:commet/main.dart';
import 'package:commet/ui/atoms/generic_room_event.dart';
import 'package:commet/ui/molecules/message.dart';
import 'package:flutter/material.dart' as m;
Expand Down Expand Up @@ -229,7 +230,7 @@ class _TimelineEventState extends State<TimelineEventView> {
break;
}

if (BuildConfig.DEBUG) {
if (BuildConfig.DEBUG && preferences.developerMode) {
return m.Padding(
padding: const EdgeInsets.all(8.0),
child: Placeholder(
Expand Down
9 changes: 7 additions & 2 deletions commet/lib/ui/molecules/timeline_viewer.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:async';
import 'package:commet/config/build_config.dart';
import 'package:commet/main.dart';
import 'package:commet/ui/molecules/message_popup_menu/message_popup_menu.dart';
import 'package:commet/ui/molecules/timeline_event.dart';
import 'package:flutter/material.dart';
Expand Down Expand Up @@ -280,14 +281,18 @@ class TimelineViewerState extends State<TimelineViewer> {
Widget? buildHistoryItem(BuildContext context, int index) {
var displayIndex = recentItemsCount + index;
return Container(
color: BuildConfig.DEBUG ? Colors.red.withAlpha(15) : null,
color: BuildConfig.DEBUG && preferences.developerMode
? Colors.red.withAlpha(15)
: null,
child: buildEvent(displayIndex, index));
}

Widget? buildRecentItem(BuildContext context, int index) {
int displayIndex = recentItemsCount - index - 1;
return Container(
color: BuildConfig.DEBUG ? Colors.blue.withAlpha(15) : null,
color: BuildConfig.DEBUG && preferences.developerMode
? Colors.blue.withAlpha(15)
: null,
child: buildEvent(displayIndex, index));
}

Expand Down
Loading

0 comments on commit c3a2774

Please sign in to comment.