diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index e93a31e..6c621f0 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -33,7 +33,7 @@ android:enableOnBackInvokedCallback="true" android:icon="@mipmap/ic_launcher"> + android:targetActivity="ru.komet.app.MainActivity"> diff --git a/android/app/src/main/kotlin/ru/komet/app/MainActivity.kt b/android/app/src/main/kotlin/ru/komet/app/MainActivity.kt index 1257145..30f6d86 100644 --- a/android/app/src/main/kotlin/ru/komet/app/MainActivity.kt +++ b/android/app/src/main/kotlin/ru/komet/app/MainActivity.kt @@ -52,7 +52,11 @@ import java.util.concurrent.atomic.AtomicBoolean class MainActivity : FlutterActivity() { private val channelName = "ru.komet.app/vpn_bypass" - private val iconComponents = listOf("MainActivity", "MinimalIcon") + private val iconPackage = MainActivity::class.java.name.substringBeforeLast('.') + private val iconComponents = mapOf( + "MainActivity" to "$iconPackage.MainActivity", + "MinimalIcon" to "$iconPackage.MinimalIcon", + ) private var nfcAdapter: NfcAdapter? = null private var nfcEvents: EventChannel.EventSink? = null @@ -86,8 +90,8 @@ class MainActivity : FlutterActivity() { private fun applyIcon(name: String) { val pm = packageManager - for (alias in iconComponents) { - val component = ComponentName(packageName, "$packageName.$alias") + for ((alias, className) in iconComponents) { + val component = ComponentName(packageName, className) val state = if (alias == name) { PackageManager.COMPONENT_ENABLED_STATE_ENABLED } else { @@ -165,7 +169,7 @@ class MainActivity : FlutterActivity() { when (call.method) { "setAppIcon" -> { val name = call.argument("name") - if (name == null || !iconComponents.contains(name)) { + if (name == null || !iconComponents.containsKey(name)) { result.error("INVALID_ICON", "Unknown icon: $name", null) return@setMethodCallHandler } diff --git a/lib/backend/modules/account.dart b/lib/backend/modules/account.dart index 395e0c3..3a8265f 100644 --- a/lib/backend/modules/account.dart +++ b/lib/backend/modules/account.dart @@ -396,10 +396,16 @@ class AccountModule { } Future logout() async { + final accountId = await TokenStorage.getActiveAccountId(); + try { + await _logoutOnServer(accountId); + } on SessionExpiredException catch (e) { + logger.w('logout: сервер отклонил сессию: ${e.message}'); + } + _loggedIn = false; try { await _api.disconnect(); } catch (_) {} - final accountId = await TokenStorage.getActiveAccountId(); if (accountId != null) { await removeAccount(accountId); } @@ -409,6 +415,33 @@ class AccountModule { chats.resetForAccountSwitch(); } + Future _logoutOnServer(int? accountId) async { + await _ensureLogoutSession(accountId); + await _api.sendRequestOrThrow(Opcode.logout, {}); + } + + Future _ensureLogoutSession(int? accountId) async { + if (_api.state == SessionState.disconnected) { + await _api.connect(); + } + if (_api.state != SessionState.online) { + await _api.stateStream + .firstWhere((state) => state == SessionState.online) + .timeout(const Duration(seconds: 20)); + } + if (_loggedIn) return; + if (accountId == null) return; + final token = await TokenStorage.readToken(accountId); + if (token == null || token.isEmpty) { + throw StateError('logout: нет токена для серверного выхода'); + } + await _api.sendRequestOrThrow( + Opcode.login, + buildLoginPayload(token, interactive: false), + ); + _loggedIn = true; + } + Future checkPassword({ required String password, required String trackId, diff --git a/lib/backend/modules/account/privacy_module.dart b/lib/backend/modules/account/privacy_module.dart index 37803e3..af9e543 100644 --- a/lib/backend/modules/account/privacy_module.dart +++ b/lib/backend/modules/account/privacy_module.dart @@ -91,15 +91,9 @@ class PrivacyModule extends AccountApiBase { } } - Future unregisterPushToken(String pushToken) async { + Future unregisterPushToken(String _) async { if (api.state != SessionState.online) return; - final accountId = await TokenStorage.getActiveAccountId(); - if (accountId == null) return; - final authToken = await TokenStorage.readToken(accountId); - if (authToken == null) return; - await api.sendRequest(Opcode.logout, { - 'token': authToken, - 'pushToken': pushToken, - }); + final packet = await api.sendRequest(Opcode.logout, {}); + throwIfPacketError(packet); } } diff --git a/lib/core/config/app_icon.dart b/lib/core/config/app_icon.dart index 80d5454..8c503fa 100644 --- a/lib/core/config/app_icon.dart +++ b/lib/core/config/app_icon.dart @@ -36,12 +36,12 @@ class AppIconConfig { static Future apply(AppIcon icon) async { if (!isSupported) return; if (current.value == icon) return; - final prefs = await SharedPreferences.getInstance(); - await prefs.setString(prefKey, icon.id); - current.value = icon; await _channel.invokeMethod('setAppIcon', { 'name': icon.platformName, }); + final prefs = await SharedPreferences.getInstance(); + await prefs.setString(prefKey, icon.id); + current.value = icon; } static AppIcon _parse(String? val) { diff --git a/lib/frontend/screens/profile/settings_tab.dart b/lib/frontend/screens/profile/settings_tab.dart index 5691271..624bba2 100644 --- a/lib/frontend/screens/profile/settings_tab.dart +++ b/lib/frontend/screens/profile/settings_tab.dart @@ -17,6 +17,7 @@ import '../../widgets/info_action_sheet.dart'; import '../../widgets/komet_avatar.dart'; import '../../widgets/settings_card.dart'; import '../../widgets/sheet_helpers.dart'; +import '../../widgets/custom_notification.dart'; import '../auth/login_screen.dart'; import '../auth/proxy_settings_sheet.dart'; import '../../../core/config/app_digital_id_mode.dart'; @@ -198,7 +199,12 @@ class _SettingsTabState extends State { Future _doLogout() async { final navState = KometApp.navigatorKey.currentState; - await accountModule.logout(); + try { + await accountModule.logout(); + } catch (e) { + if (mounted) showCustomNotification(context, 'Не удалось выйти: $e'); + return; + } await resetDigitalIdSession(); try { await api.connect();