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();