Compare commits
No commits in common. "966b1a6b84cd2210e60b50b4d292fd00c34210ab" and "4b306f3ceef54b88bb73fa4130a0e501641e6ca8" have entirely different histories.
966b1a6b84
...
4b306f3cee
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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.** { *; }
|
|
||||||
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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');
|
||||||
|
|
|
||||||
153
lib/main.dart
153
lib/main.dart
|
|
@ -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: [
|
||||||
|
|
|
||||||
|
|
@ -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 => "[Файл]",
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue