3fda5649c3
Build Docker images with Sentry / Build release image with Sentry (push) Failing after 1m59s
Release / Release (android, ubuntu-latest) (push) Failing after 38m31s
Release / Release (ios, macos-14) (push) Has been cancelled
Integration tests / Run integration tests for mobile apps (push) Failing after 15s
Build dev binaries / Build app (ios, macos-14) (push) Waiting to run
Build dev binaries / Build app (android, ubuntu-latest) (push) Failing after 34m13s
Tier 1+2 - web/UI user-facing:
- Replace all visible "Twake Mail" / "Linagora" with "Workavia Mail"
- Swap 3 logo SVGs (icon kept, wordmark replaced via SVG <text>)
- Splash: replace icon_twp.png with HTML <div>Workavia</div>
- Remove power_by_linagora.svg, replace with AGPL SourceLinkWidget
- Patch web/index.html, manifest.json, login/logout-callback HTMLs
- Patch all 16 ARB locales + scribe ARB + app_localizations.dart
- Patch 4 web/i18n/*.json (FR/EN/DE/VI smart banner titles)
- App grid reduced to "Mail" + "Calendar"
- User-Agent: Twake-Mail -> Workavia-Mail
Tier 3+4 - mobile native:
- Bundle ID: com.linagora.{android,ios}.teammail -> com.workavia.mail
- URL schemes consolidated: teammail.mobile/twakemail.mobile -> workaviamail.mobile
- Android: applicationId, namespace, AndroidManifest, strings.xml
- Android: move Kotlin sources com.linagora.android.tmail -> com.workavia.mail
- Android: empty branding.xml + android12branding.xml (vector text not patchable)
- iOS: Info.plist (CFBundleDisplayName/Name, schemes), Runner.entitlements
- iOS: TwakeMailNSE + TeamMailShareExtension entitlements + Info.plist
- iOS: Runner.xcodeproj (12x PRODUCT_BUNDLE_IDENTIFIER, 9x APP_GROUP_ID)
- iOS: TokenRefreshManager.swift redirect URLs
- iOS: fastlane Appfile + Matchfile
- Dart: oidc_constant, app_config keychain, notification MethodChannel
Known follow-ups (require user action):
- Apple Team ID KUT463DS29 (still Linagora) -> needs Workavia team for App Store
- Android branding.xml emptied: need PNG/path-converted wordmark for splash
- iOS sub-bundle paths (TwakeMailNSE/, TeamMailShareExtension/) on disk unchanged
- iOS App Store URL emptied; Play Store ID points to com.workavia.mail
165 lines
4.1 KiB
Dart
165 lines
4.1 KiB
Dart
import 'package:core/utils/app_logger.dart';
|
|
import 'package:core/utils/platform_info.dart';
|
|
import 'package:device_info_plus/device_info_plus.dart';
|
|
import 'package:fk_user_agent/fk_user_agent.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:package_info_plus/package_info_plus.dart';
|
|
|
|
class ApplicationManager {
|
|
ApplicationManager._internal();
|
|
|
|
static final ApplicationManager _instance = ApplicationManager._internal();
|
|
|
|
factory ApplicationManager() => _instance;
|
|
|
|
@visibleForTesting
|
|
static DeviceInfoPlugin? debugDeviceInfoOverride;
|
|
|
|
DeviceInfoPlugin? _deviceInfoCache;
|
|
|
|
DeviceInfoPlugin get _deviceInfo =>
|
|
debugDeviceInfoOverride ?? (_deviceInfoCache ??= DeviceInfoPlugin());
|
|
|
|
PackageInfo? _packageInfoCache;
|
|
String? _versionCache;
|
|
String? _cachedWebUserAgent;
|
|
String? _cachedMobileUserAgent;
|
|
bool _isMobileUserAgentInitialized = false;
|
|
|
|
@visibleForTesting
|
|
void clearCache() {
|
|
_packageInfoCache = null;
|
|
_versionCache = null;
|
|
_cachedWebUserAgent = null;
|
|
_cachedMobileUserAgent = null;
|
|
_isMobileUserAgentInitialized = false;
|
|
_deviceInfoCache = null;
|
|
}
|
|
|
|
Future<PackageInfo> getPackageInfo() async {
|
|
if (_packageInfoCache != null) {
|
|
return _packageInfoCache!;
|
|
}
|
|
|
|
final info = await PackageInfo.fromPlatform();
|
|
_packageInfoCache = info;
|
|
|
|
log('ApplicationManager:getPackageInfo -> cached: $info');
|
|
return info;
|
|
}
|
|
|
|
Future<String> getAppVersion() async {
|
|
if (_versionCache != null) {
|
|
return _versionCache!;
|
|
}
|
|
|
|
try {
|
|
final version = (await getPackageInfo()).version;
|
|
_versionCache = version;
|
|
|
|
log('ApplicationManager:getAppVersion -> cached: $version');
|
|
return version;
|
|
} catch (e) {
|
|
logWarning('ApplicationManager:getAppVersion failed, Exception = $e');
|
|
return '';
|
|
}
|
|
}
|
|
|
|
Future<void> initUserAgent() async {
|
|
if (!PlatformInfo.isMobile) return;
|
|
|
|
try {
|
|
await FkUserAgent.init();
|
|
_isMobileUserAgentInitialized = true;
|
|
_cachedMobileUserAgent = null;
|
|
|
|
log('ApplicationManager:initUserAgent -> initialized');
|
|
} catch (e) {
|
|
logWarning('ApplicationManager:initUserAgent failed, Exception $e');
|
|
}
|
|
}
|
|
|
|
Future<void> releaseUserAgent() async {
|
|
if (!PlatformInfo.isMobile) return;
|
|
|
|
try {
|
|
FkUserAgent.release();
|
|
_isMobileUserAgentInitialized = false;
|
|
_cachedMobileUserAgent = null;
|
|
|
|
log('ApplicationManager:releaseUserAgent -> released & cache cleared');
|
|
} catch (e) {
|
|
logWarning('ApplicationManager:releaseUserAgent failed, Exception $e');
|
|
}
|
|
}
|
|
|
|
Future<String> getUserAgent() async {
|
|
try {
|
|
if (PlatformInfo.isWeb) {
|
|
return _getWebUserAgent();
|
|
}
|
|
|
|
if (PlatformInfo.isMobile) {
|
|
return _getMobileUserAgent();
|
|
}
|
|
|
|
return '';
|
|
} catch (e) {
|
|
logWarning('ApplicationManager:getUserAgent failed');
|
|
return '';
|
|
}
|
|
}
|
|
|
|
Future<String> _getWebUserAgent() async {
|
|
if (_cachedWebUserAgent != null) {
|
|
return _cachedWebUserAgent!;
|
|
}
|
|
|
|
WebBrowserInfo webInfo;
|
|
|
|
try {
|
|
webInfo = await _deviceInfo.webBrowserInfo;
|
|
} catch (e) {
|
|
logWarning('ApplicationManager:_getWebUserAgent failed, Exception $e');
|
|
return '';
|
|
}
|
|
|
|
final userAgent = webInfo.userAgent ?? '';
|
|
_cachedWebUserAgent = userAgent;
|
|
|
|
log('ApplicationManager:_getWebUserAgent -> cached');
|
|
return userAgent;
|
|
}
|
|
|
|
Future<String> _getMobileUserAgent() async {
|
|
if (!_isMobileUserAgentInitialized) {
|
|
logWarning(
|
|
'ApplicationManager:_getMobileUserAgent called before initUserAgent',
|
|
);
|
|
return '';
|
|
}
|
|
|
|
if (_cachedMobileUserAgent != null) {
|
|
return _cachedMobileUserAgent!;
|
|
}
|
|
|
|
final userAgent = FkUserAgent.userAgent;
|
|
|
|
if (userAgent == null || userAgent.isEmpty) {
|
|
return '';
|
|
}
|
|
|
|
_cachedMobileUserAgent = userAgent;
|
|
log('ApplicationManager:_getMobileUserAgent -> cached');
|
|
|
|
return userAgent;
|
|
}
|
|
|
|
Future<String> generateApplicationUserAgent() async {
|
|
final version = await getAppVersion();
|
|
final userAgent = await getUserAgent();
|
|
|
|
return 'Workavia-Mail/$version $userAgent'.trim();
|
|
}
|
|
}
|