Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,6 @@
android:exported="false"
tools:node="remove"/>

<provider
android:name="vn.hunghd.flutterdownloader.DownloadedFileProvider"
android:authorities="${applicationId}.flutter_downloader.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>

<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
Expand Down
6 changes: 0 additions & 6 deletions android/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
<resources>
<string name="app_name">Twake Mail</string>
<string name="flutter_downloader_notification_started">Started</string>
<string name="flutter_downloader_notification_in_progress">In Progress</string>
<string name="flutter_downloader_notification_canceled">Canceled</string>
<string name="flutter_downloader_notification_failed">Failed</string>
<string name="flutter_downloader_notification_complete">Completed</string>
<string name="flutter_downloader_notification_paused">Paused</string>
</resources>
55 changes: 55 additions & 0 deletions docs/adr/0079-reduce-twake-mail-mobile-memory-usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# 0079 - Reduce Twake Mail mobile memory usage

Date: 2026-04-09

## Status

Accepted

## Context

Twake Mail is allocating a lot of memory on mobile.
- ~375 MB on Android (Oneplus 8T)

<img src="../images/android-before-4435.png">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add alt text to all ADR images.

All embedded images are missing alt text (MD045), which hurts accessibility and fails current lint checks.

Suggested patch
-<img src="../images/android-before-4435.png">
+<img src="../images/android-before-4435.png" alt="Android memory profile before optimization">

-<img src="../images/ios-before-4435.png">
+<img src="../images/ios-before-4435.png" alt="iOS memory profile before optimization">

-<img src="../images/android-after-4435.png">
+<img src="../images/android-after-4435.png" alt="Android memory profile after optimization">

-<img src="../images/ios-after-4435.png">
+<img src="../images/ios-after-4435.png" alt="iOS memory profile after optimization">

-| <img src="../images/web-before-4435.png"> | <img src="../images/web-after-4435.png"> |
+| <img src="../images/web-before-4435.png" alt="Web memory profile before optimization"> | <img src="../images/web-after-4435.png" alt="Web memory profile after optimization"> |

Also applies to: 18-18, 45-45, 49-49, 55-55

🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 14-14: Images should have alternate text (alt text)

(MD045, no-alt-text)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/adr/0079-reduce-twake-mail-mobile-memory-usage.md` at line 14, The
embedded <img> tag referencing "android-before-4435.png" (and the other images
noted at lines 18, 45, 49, 55) is missing alt text; update each image tag in the
ADR (e.g., the <img src="...android-before-4435.png"> entry and the other listed
image tags) to include meaningful alt attributes (alt="...") describing the
image content for accessibility and to satisfy MD045 linting.


- ~297 MB on iOS (iPhone 11 Pro)

<img src="../images/ios-before-4435.png">

## Findings

### `worker_manager` library's inefficient memory allocation (Android & iOS)
- Version `5.0.3` counts the number of processors (x) on the device, create forever-live x - 1 isolates
- Version `7.2.7` adds an ability to create isolate on-demand, and dispose when done. However, it still leaks 1 isolate when init.
### `firebase_messaging` library's eager background Dart isolate (Android)
- FirebaseMessaging.onBackgroundMessage() creates a forever-live isolate even when the app is in foreground.
- Related: https://github.com/firebase/flutterfire/issues/17163
### Unused `flutter_downloader` library's worker (iOS)
- The only usage was deleted in https://github.com/linagora/tmail-flutter/commit/be8eaf625818b17e60ca65846053cb8c26a71a15#diff-451741ba5146e6ad711c77e4c2fe34958a36595e4926cd43c2ddb97586ef6d88, but the library and initialization process remained, causing 1 forever-live isolate.

## Decision

### `worker_manager`
- Upgrade to `7.2.7`
- Create an upstream fix for init's isolate leak
### `firebase_messaging`
- Wait for https://github.com/firebase/flutterfire/pull/18122, update when merged
### `flutter_downloader`
- Remove the library

## Consequences

- Android: ~118 MB

<img src="../images/android-after-4435.png">

- iOS: ~78 MB

<img src="../images/ios-after-4435.png">

- No changes for web

| Before | After |
| :--- | :--- |
| <img src="../images/web-before-4435.png"> | <img src="../images/web-after-4435.png"> |
Binary file added docs/images/android-after-4435.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/android-before-4435.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/ios-after-4435.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/ios-before-4435.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/web-after-4435.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/web-before-4435.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 8 additions & 20 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,6 @@ PODS:
- Flutter
- FlutterMacOS
- UniversalDetector2 (= 2.0.1)
- flutter_downloader (0.0.1):
- Flutter
- flutter_file_dialog (0.0.1):
- Flutter
- flutter_image_compress_common (1.0.0):
Expand Down Expand Up @@ -164,7 +162,7 @@ PODS:
- libwebp/sharpyuv (1.5.0)
- libwebp/webp (1.5.0):
- libwebp/sharpyuv
- lottie-ios (4.4.1)
- lottie-ios (4.4.3)
- lottie_native (0.0.1):
- Flutter
- lottie-ios (~> 4.4.1)
Expand Down Expand Up @@ -206,10 +204,10 @@ PODS:
- ReachabilitySwift (5.2.4)
- receive_sharing_intent (1.8.1):
- Flutter
- SDWebImage (5.21.1):
- SDWebImage/Core (= 5.21.1)
- SDWebImage/Core (5.21.1)
- SDWebImageWebPCoder (0.14.6):
- SDWebImage (5.21.7):
- SDWebImage/Core (= 5.21.7)
- SDWebImage/Core (5.21.7)
- SDWebImageWebPCoder (0.15.0):
- libwebp (~> 1.0)
- SDWebImage/Core (~> 5.17)
- Sentry/HybridSDK (8.56.2)
Expand All @@ -228,8 +226,6 @@ PODS:
- UniversalDetector2 (2.0.1)
- url_launcher_ios (0.0.1):
- Flutter
- workmanager_apple (0.0.1):
- Flutter

DEPENDENCIES:
- app_links (from `.symlinks/plugins/app_links/ios`)
Expand All @@ -246,7 +242,6 @@ DEPENDENCIES:
- Flutter (from `Flutter`)
- flutter_appauth (from `.symlinks/plugins/flutter_appauth/ios`)
- flutter_charset_detector_darwin (from `.symlinks/plugins/flutter_charset_detector_darwin/darwin`)
- flutter_downloader (from `.symlinks/plugins/flutter_downloader/ios`)
- flutter_file_dialog (from `.symlinks/plugins/flutter_file_dialog/ios`)
- flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`)
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
Expand All @@ -272,7 +267,6 @@ DEPENDENCIES:
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- super_dns_client (from `.symlinks/plugins/super_dns_client/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- workmanager_apple (from `.symlinks/plugins/workmanager_apple/ios`)

SPEC REPOS:
trunk:
Expand Down Expand Up @@ -329,8 +323,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_appauth/ios"
flutter_charset_detector_darwin:
:path: ".symlinks/plugins/flutter_charset_detector_darwin/darwin"
flutter_downloader:
:path: ".symlinks/plugins/flutter_downloader/ios"
flutter_file_dialog:
:path: ".symlinks/plugins/flutter_file_dialog/ios"
flutter_image_compress_common:
Expand Down Expand Up @@ -381,8 +373,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/super_dns_client/ios"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
workmanager_apple:
:path: ".symlinks/plugins/workmanager_apple/ios"

SPEC CHECKSUMS:
app_links: 3da4c36b46cac3bf24eb897f1a6ce80bda109874
Expand All @@ -408,7 +398,6 @@ SPEC CHECKSUMS:
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_appauth: d4abcf54856e5d8ba82ed7646ffc83245d4aa448
flutter_charset_detector_darwin: 14f055ebeed6896144cc96b046749df51127a0a3
flutter_downloader: 78da0da1084e709cbfd3b723c7ea349c71681f09
flutter_file_dialog: ca8d7fbd1772d4f0c2777b4ab20a7787ef4e7dd8
flutter_image_compress_common: 1697a328fd72bfb335507c6bca1a65fa5ad87df1
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
Expand All @@ -420,7 +409,7 @@ SPEC CHECKSUMS:
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
lottie-ios: e047b1d2e6239b787cc5e9755b988869cf190494
lottie-ios: fcb5e73e17ba4c983140b7d21095c834b3087418
lottie_native: c2e590a297861fc32a0188cf8dab39aa97f86d81
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
nanopb: d4d75c12cd1316f4a64e3c6963f879ecd4b5e0d5
Expand All @@ -438,8 +427,8 @@ SPEC CHECKSUMS:
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
ReachabilitySwift: 32793e867593cfc1177f5d16491e3a197d2fccda
receive_sharing_intent: 222384f00ffe7e952bbfabaa9e3967cb87e5fe00
SDWebImage: f29024626962457f3470184232766516dee8dfea
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
SDWebImage: e9fc87c1aab89a8ab1bbd74eba378c6f53be8abf
SDWebImageWebPCoder: 0e06e365080397465cc73a7a9b472d8a3bd0f377
Sentry: b53951377b78e21a734f5dc8318e333dbfc682d7
sentry_flutter: 4c33648b7e83310aa1fdb1b10c5491027d9643f0
share_plus: de6030e33b4e106470e09322d87cf2a4258d2d1d
Expand All @@ -448,7 +437,6 @@ SPEC CHECKSUMS:
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
UniversalDetector2: 7c9ffd935cf050eeb19edf7e90f6febe3743a1af
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
workmanager_apple: 904529ae31e97fc5be632cf628507652294a0778

PODFILE CHECKSUM: 40b12ce0bc437886ee4f4050970375d7d253708d

Expand Down
7 changes: 0 additions & 7 deletions ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import UIKit
import Flutter
import flutter_downloader
import receive_sharing_intent
import flutter_local_notifications

Expand Down Expand Up @@ -34,12 +33,6 @@ import flutter_local_notifications
GeneratedPluginRegistrant.register(with: registry)
}

FlutterDownloaderPlugin.setPluginRegistrantCallback { registry in
if (!registry.hasPlugin("FlutterDownloaderPlugin")) {
FlutterDownloaderPlugin.register(with: registry.registrar(forPlugin: "FlutterDownloaderPlugin")!)
}
}

let sharingIntent = SwiftReceiveSharingIntentPlugin.instance
if let url = launchOptions?[UIApplication.LaunchOptionsKey.url] as? URL {
if url.scheme == "mailto" {
Expand Down
11 changes: 0 additions & 11 deletions lib/features/home/presentation/home_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:model/account/personal_account.dart';
Expand Down Expand Up @@ -68,7 +66,6 @@ class HomeController extends ReloadableController {
@override
void onInit() {
if (PlatformInfo.isMobile) {
_initFlutterDownloader();
_registerReceivingFileSharing();
_registerDeepLinks();
}
Expand Down Expand Up @@ -96,14 +93,6 @@ class HomeController extends ReloadableController {
clearDataAndGoToLoginPage();
}

void _initFlutterDownloader() {
FlutterDownloader
.initialize(debug: kDebugMode)
.then((_) => FlutterDownloader.registerCallback(downloadCallback));
}

static void downloadCallback(String id, int status, int progress) {}

Future<void> _handleNavigateToScreen() async {
await Future.delayed(2.seconds);
final arguments = Get.arguments;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:io';

import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
Expand Down Expand Up @@ -172,7 +173,7 @@ class MailboxDataSourceImpl extends MailboxDataSource {
StreamController<dartz.Either<Failure, Success>>? onProgressController,
}) {
return Future.sync(() async {
if (PlatformInfo.isWeb) {
if (PlatformInfo.isWeb || Platform.numberOfProcessors == 1) {
return await mailboxAPI.moveFolderContent(
session: session,
accountId: accountId,
Expand Down
Loading
Loading