Compare commits

..

No commits in common. "966b1a6b84cd2210e60b50b4d292fd00c34210ab" and "4b306f3ceef54b88bb73fa4130a0e501641e6ca8" have entirely different histories.

8 changed files with 91 additions and 180 deletions

View File

@ -34,8 +34,6 @@ android {
buildTypes { buildTypes {
release { release {
isMinifyEnabled = true
isShrinkResources = true
// TODO: Add your own signing config for the release build. // TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works. // Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug") signingConfig = signingConfigs.getByName("debug")
@ -51,4 +49,13 @@ dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3") coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3")
implementation(platform("com.google.firebase:firebase-bom:34.12.0")) implementation(platform("com.google.firebase:firebase-bom:34.12.0"))
implementation("com.google.firebase:firebase-messaging") implementation("com.google.firebase:firebase-messaging")
}
configurations.all {
resolutionStrategy.eachDependency {
if (requested.group == "com.arthenica" && requested.name.startsWith("ffmpeg-kit")) {
useVersion("6.0.3")
because("Фикс падения сборки на версии 6.0.3+2-LTS")
}
}
} }

View File

@ -1,15 +0,0 @@
# Защита ffmpeg-kit от R8
-keep class com.arthenica.ffmpegkit.** { *; }
-keep interface com.arthenica.ffmpegkit.** { *; }
# Защита от удаления нативных методов JNI
-keepclassmembers class * {
native <methods>;
}
# Если используете конкретно ваш форк, добавим и его:
-keep class com.antonkarpenko.ffmpegkit.** { *; }
# Защита Firebase (если падает Firebase, когда включен R8)
-keep class com.google.firebase.** { *; }
-keep class com.google.android.gms.** { *; }

View File

@ -1,45 +1,5 @@
package ru.chepuhagram.app package ru.chepuhagram.app
import android.app.AlertDialog
import android.os.Bundle
import io.flutter.embedding.android.FlutterFragmentActivity import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
class MainActivity : FlutterFragmentActivity() { class MainActivity : FlutterFragmentActivity()
override fun onCreate(savedInstanceState: Bundle?) {
try {
super.onCreate(savedInstanceState)
} catch (e: UnsatisfiedLinkError) {
handleFatalStartupError("Ошибка загрузки нативных библиотек: ${e.message}")
} catch (e: Exception) {
handleFatalStartupError("Произошла системная ошибка при запуске: ${e.message}")
}
}
// Дополнительная защита при создании движка, если инициализация происходит там
override fun provideFlutterEngine(context: android.content.Context): FlutterEngine? {
return try {
super.provideFlutterEngine(context)
} catch (e: UnsatisfiedLinkError) {
handleFatalStartupError("Ошибка инициализации движка (FFmpegKit): ${e.message}")
null // Возвращаем null, чтобы предотвратить дальнейший краш
}
}
private fun handleFatalStartupError(message: String) {
// Мы не можем использовать стандартные диалоги из темы Activity,
// так как они могут быть повреждены, используем чистый Android AlertDialog
runOnUiThread {
AlertDialog.Builder(this)
.setTitle("Критическая ошибка")
.setMessage("Приложение не может быть запущено на данном устройстве.\n\nТехническая информация: $message")
.setPositiveButton("Закрыть") { _, _ ->
finishAffinity() // Полностью закрывает приложение
}
.setCancelable(false)
.create()
.show()
}
}
}

View File

@ -36,10 +36,10 @@ class ApiService extends ChangeNotifier {
Future<Contact?> getUserByUsername(String username) async { Future<Contact?> getUserByUsername(String username) async {
try { try {
// Подставляй свой эндпоинт, например: /users/by-username/ // Подставляй свой эндпоинт, например: /users/by-username/
final response = await Dio().get('/users/by-username/$username'); final response = await Dio().get('/users/by-username/$username');
if (response.statusCode == 200 && response.data != null) { if (response.statusCode == 200 && response.data != null) {
// Парсим полученные данные в модель контакта. // Парсим полученные данные в модель контакта.
// Убедись, что метод Contact.fromJson или Contact.fromMap корректно обрабатывает поле public_key // Убедись, что метод Contact.fromJson или Contact.fromMap корректно обрабатывает поле public_key
return Contact.fromJson(response.data); return Contact.fromJson(response.data);
} }
@ -288,14 +288,7 @@ class ApiService extends ChangeNotifier {
} }
Future<String?> getAccessToken() async { Future<String?> getAccessToken() async {
String? token; String? token = await _storage.read(key: 'access_token');
try {
token = await _storage.read(key: 'access_token');
} catch (_) {
throw Exception(
'Критическая ошибка инициализации внутренних библиотек.\n Приложение не может продолжить работу. \n Обратитесь к разработчику. \n Код ошибки: _apis_gat_1',
);
}
if (token != null) { if (token != null) {
bool isExpiredSoon = bool isExpiredSoon =

View File

@ -94,11 +94,7 @@ class AuthProvider extends ChangeNotifier {
SocketService get socketService => _socketService; SocketService get socketService => _socketService;
Future<bool> login( Future<bool> login(String username, String password, {String? totpCode}) async {
String username,
String password, {
String? totpCode,
}) async {
_isLoading = true; _isLoading = true;
notifyListeners(); notifyListeners();
@ -184,14 +180,7 @@ class AuthProvider extends ChangeNotifier {
} }
Future<bool> tryAutoLogin() async { Future<bool> tryAutoLogin() async {
String? token; final token = await _apiService.getAccessToken();
try {
token = await _apiService.getAccessToken();
} catch (e) {
throw Exception(
'$e+_aup_tal_1',
);
}
if (token == null) return false; if (token == null) return false;
// Загружаем currentUserId из хранилища // Загружаем currentUserId из хранилища
@ -292,9 +281,7 @@ class AuthProvider extends ChangeNotifier {
_email = data['email']?.toString(); _email = data['email']?.toString();
_about = data['about']?.toString(); _about = data['about']?.toString();
final avatarFileId = data['avatar_file_id']?.toString(); final avatarFileId = data['avatar_file_id']?.toString();
_avatarUrl = avatarFileId != null _avatarUrl = avatarFileId != null ? '${AppConstants.baseUrl}/media/$avatarFileId' : null;
? '${AppConstants.baseUrl}/media/$avatarFileId'
: null;
// Загружаем локальные настройки // Загружаем локальные настройки
_avatarPath = await _storage.read(key: 'avatar_path'); _avatarPath = await _storage.read(key: 'avatar_path');

View File

@ -124,101 +124,94 @@ void _navigateToChat(int senderId) {
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
try { await Firebase.initializeApp();
await Firebase.initializeApp();
initialMessage = await FirebaseMessaging.instance.getInitialMessage(); await Future.delayed(const Duration(milliseconds: 500));
print('Initial message from main() after delay: $initialMessage'); initialMessage = await FirebaseMessaging.instance.getInitialMessage();
// Сохраняем информацию в SharedPreferences для надежности print('Initial message from main() after delay: $initialMessage');
final prefs = await SharedPreferences.getInstance(); // Сохраняем информацию в SharedPreferences для надежности
if (initialMessage != null) { final prefs = await SharedPreferences.getInstance();
print('App launched from notification: ${initialMessage!.data}'); if (initialMessage != null) {
print('Message type: ${initialMessage!.data['type']}'); print('App launched from notification: ${initialMessage!.data}');
print('Sender ID: ${initialMessage!.data['sender_id']}'); print('Message type: ${initialMessage!.data['type']}');
print('Sender ID: ${initialMessage!.data['sender_id']}');
final payloadString = jsonEncode(initialMessage!.data); final payloadString = jsonEncode(initialMessage!.data);
final lastHandled = prefs.getString( final lastHandled = prefs.getString(
_lastHandledNotificationLaunchPayloadKey,
);
if (lastHandled != payloadString) {
// Сохраняем данные уведомления
await prefs.setString(_notificationLaunchKey, payloadString);
await prefs.setString(
_lastHandledNotificationLaunchPayloadKey, _lastHandledNotificationLaunchPayloadKey,
payloadString,
); );
if (lastHandled != payloadString) { print('Saved notification data to SharedPreferences');
// Сохраняем данные уведомления
await prefs.setString(_notificationLaunchKey, payloadString);
await prefs.setString(
_lastHandledNotificationLaunchPayloadKey,
payloadString,
);
print('Saved notification data to SharedPreferences');
} else {
print('InitialMessage payload already handled earlier, skipping');
}
} else { } else {
print('No initial message - app launched normally'); print('InitialMessage payload already handled earlier, skipping');
// Очищаем сохраненные данные, если приложение запущено нормально
await prefs.remove(_notificationLaunchKey);
} }
} else {
print('No initial message - app launched normally');
// Очищаем сохраненные данные, если приложение запущено нормально
await prefs.remove(_notificationLaunchKey);
}
// Initialize local notifications // Initialize local notifications
const AndroidInitializationSettings initializationSettingsAndroid = const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('@mipmap/ic_launcher'); AndroidInitializationSettings('@mipmap/ic_launcher');
final InitializationSettings initializationSettings = final InitializationSettings initializationSettings = InitializationSettings(
InitializationSettings(android: initializationSettingsAndroid); android: initializationSettingsAndroid,
await flutterLocalNotificationsPlugin.initialize( );
initializationSettings, await flutterLocalNotificationsPlugin.initialize(
onDidReceiveNotificationResponse: _onSelectNotification, initializationSettings,
); onDidReceiveNotificationResponse: _onSelectNotification,
);
// Если приложение было запущено из локального уведомления, сохраним payload // Если приложение было запущено из локального уведомления, сохраним payload
final notificationAppLaunchDetails = await flutterLocalNotificationsPlugin final notificationAppLaunchDetails = await flutterLocalNotificationsPlugin
.getNotificationAppLaunchDetails(); .getNotificationAppLaunchDetails();
if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) { if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) {
final payload = final payload = notificationAppLaunchDetails?.notificationResponse?.payload;
notificationAppLaunchDetails?.notificationResponse?.payload; print('App launched from local notification, payload: $payload');
print('App launched from local notification, payload: $payload'); if (payload != null && payload.isNotEmpty) {
if (payload != null && payload.isNotEmpty) { try {
try { final lastHandled = prefs.getString(
final lastHandled = prefs.getString( _lastHandledNotificationLaunchPayloadKey,
);
if (lastHandled != payload) {
final data = jsonDecode(payload);
await prefs.setString(_notificationLaunchKey, jsonEncode(data));
await prefs.setString(
_lastHandledNotificationLaunchPayloadKey, _lastHandledNotificationLaunchPayloadKey,
payload,
); );
if (lastHandled != payload) { print('Saved local notification launch payload to SharedPreferences');
final data = jsonDecode(payload); } else {
await prefs.setString(_notificationLaunchKey, jsonEncode(data)); print('Local notification payload already handled earlier, skipping');
await prefs.setString(
_lastHandledNotificationLaunchPayloadKey,
payload,
);
print(
'Saved local notification launch payload to SharedPreferences',
);
} else {
print(
'Local notification payload already handled earlier, skipping',
);
}
} catch (e) {
print('Failed to save notification launch payload: $e');
} }
} catch (e) {
print('Failed to save notification launch payload: $e');
} }
} }
// Create notification channel for Android 8+
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'chat_id', // id
'Messages', // title
description: 'Chat messages notifications', // description
importance: Importance.high,
);
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin
>()
?.createNotificationChannel(channel);
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
} catch (e) {
print('Уведосления не были инициальзированы: $e');
} }
// Create notification channel for Android 8+
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'chat_id', // id
'Messages', // title
description: 'Chat messages notifications', // description
importance: Importance.high,
);
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin
>()
?.createNotificationChannel(channel);
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp( runApp(
MultiProvider( MultiProvider(
providers: [ providers: [

View File

@ -2149,13 +2149,8 @@ class _ChatScreenState extends State<ChatScreen> with RouteAware {
previewText = rawText; previewText = rawText;
} else if (hasMedia) { } else if (hasMedia) {
previewText = switch (messageType) { previewText = switch (messageType) {
<<<<<<< HEAD
MessageType.videoNote => "[Кружок]",
MessageType.voiceNote => "[Голосовое]",
=======
MessageType.voiceNote => "[Кружок}", MessageType.voiceNote => "[Кружок}",
MessageType.videoNote => "[Голосовое]", MessageType.videoNote => "[Голосовое]",
>>>>>>> 4b306f3ceef54b88bb73fa4130a0e501641e6ca8
MessageType.image => "[Фото]", MessageType.image => "[Фото]",
MessageType.video => "[Видео]", MessageType.video => "[Видео]",
MessageType.file => "[Файл]", MessageType.file => "[Файл]",

View File

@ -67,16 +67,7 @@ class _SplashScreenState extends State<SplashScreen> {
// 2. Пытаемся выполнить автологин // 2. Пытаемся выполнить автологин
final authProvider = context.read<AuthProvider>(); final authProvider = context.read<AuthProvider>();
bool? isLoggedIn; final isLoggedIn = await authProvider.tryAutoLogin();
try {
isLoggedIn = await authProvider.tryAutoLogin();
} catch (e) {
setState(() {
connectError =
'$e+_sps_init_1'.replaceAll('Exception: ', '');
});
return;
}
if (!mounted) return; if (!mounted) return;
bool connected = false; bool connected = false;