forked from firka/firka
Compare commits
2 Commits
dev
...
notificati
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7242437231 | ||
|
|
50f0332842 |
@@ -12,6 +12,7 @@ android {
|
||||
compileSdk = flutter.compileSdkVersion
|
||||
ndkVersion = flutter.ndkVersion
|
||||
compileOptions {
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
@@ -71,6 +72,7 @@ configurations.all {
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
|
||||
implementation("androidx.glance:glance-appwidget:1.1.1")
|
||||
implementation("com.google.android.gms:play-services-wearable:18.1.0")
|
||||
}
|
||||
@@ -90,4 +92,4 @@ tasks.matching { it.name.startsWith("compileFlutterBuild") }.configureEach {
|
||||
}
|
||||
flutter {
|
||||
source = "../.."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:firka/core/extensions.dart';
|
||||
import 'package:firka/core/settings.dart';
|
||||
import 'package:firka/data/models/generic_cache_model.dart';
|
||||
import 'package:firka/data/models/timetable_cache_model.dart';
|
||||
import 'package:isar_community/isar.dart';
|
||||
@@ -39,6 +41,65 @@ class KretaClient {
|
||||
|
||||
bool get needsReauth => _reauthCubit.state.needsReauth;
|
||||
|
||||
Future<Response<dynamic>> sendNotifReq(
|
||||
String method,
|
||||
TokenModel model,
|
||||
String? fcmToken, {
|
||||
bool auth = true,
|
||||
}) async {
|
||||
var isGuardian = model.studentId!.contains("G0");
|
||||
|
||||
return await dio.request(
|
||||
"https://kretaglobalmobileapi2.ekreta.hu/api/v3/Registration",
|
||||
options: Options(
|
||||
method: method,
|
||||
headers: {
|
||||
"accept": "*/*",
|
||||
"user-agent": Constants.userAgent,
|
||||
if (auth) "Authorization": "Bearer ${model.accessToken}",
|
||||
"apiKey": "7856d350-1fda-45f5-822d-e1a2f3f1acf0",
|
||||
},
|
||||
),
|
||||
queryParameters: <String, String>{
|
||||
"RegistrationId": ?model.registrationId,
|
||||
"Handle": ?fcmToken,
|
||||
"NotificationRole": isGuardian ? "2" : "1",
|
||||
"NotificationEnvironment": isGuardian
|
||||
? "Gondviselo_Native"
|
||||
: "Tanulo_Native",
|
||||
"NotificationType": "1",
|
||||
if (method == "POST") "Platform": "fcmv1",
|
||||
"NotificationSource": "Kreta",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> updateFCMToken({String? fcmToken}) async {
|
||||
fcmToken = fcmToken ?? (await FirebaseMessaging.instance.getToken())!;
|
||||
|
||||
Response<dynamic> resp;
|
||||
|
||||
if (model.registrationId == null) {
|
||||
resp = await sendNotifReq("POST", model, fcmToken, auth: true);
|
||||
|
||||
if (resp.statusCode == 200) {
|
||||
model.registrationId = resp.data["registrationId"];
|
||||
model.fcmToken = fcmToken;
|
||||
logger.info("New registr: ${model.registrationId}");
|
||||
} else {
|
||||
logger.info("POST RESP");
|
||||
logger.info(resp.statusCode);
|
||||
logger.info(resp);
|
||||
}
|
||||
} else if (fcmToken != model.fcmToken) {
|
||||
logger.info("FCM mismatch!");
|
||||
model.registrationId = null;
|
||||
await updateFCMToken(fcmToken: fcmToken);
|
||||
} else {
|
||||
logger.info("FCM already registered!");
|
||||
}
|
||||
}
|
||||
|
||||
void clearReauthFlag() {
|
||||
_reauthCubit.clear();
|
||||
debugPrint('[KretaClient] Reauth flag cleared');
|
||||
@@ -83,7 +144,9 @@ class KretaClient {
|
||||
}
|
||||
|
||||
final extended = await extendToken(sourceToken);
|
||||
return TokenModel.fromResp(extended);
|
||||
return TokenModel.fromResp(extended)
|
||||
..registrationId ??= sourceToken.registrationId
|
||||
..fcmToken ??= sourceToken.fcmToken;
|
||||
} finally {
|
||||
if (Platform.isIOS && studentIdNorm != null && leaseOperationId != null) {
|
||||
await WatchSyncHelper.releaseIPhoneRefreshLease(
|
||||
@@ -169,11 +232,13 @@ class KretaClient {
|
||||
try {
|
||||
var tokenModel = await _refreshModelWithCrossDeviceLease(model);
|
||||
|
||||
model = tokenModel;
|
||||
await updateFCMToken();
|
||||
|
||||
await isar.writeTxn(() async {
|
||||
await isar.tokenModels.put(tokenModel);
|
||||
});
|
||||
|
||||
model = tokenModel;
|
||||
await _syncTokenToAppleTargets(model);
|
||||
clearReauthFlag();
|
||||
logger.info("[Recovery] Step 1 SUCCESS: Local refresh succeeded");
|
||||
@@ -304,7 +369,7 @@ class KretaClient {
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<T> _mutexCallback<T>(Future<T> Function() callback) async {
|
||||
Future<T> mutexCallback<T>(Future<T> Function() callback) async {
|
||||
const maxWaitTime = Duration(seconds: 30);
|
||||
|
||||
if (_tokenMutexCompleter != null) {
|
||||
@@ -337,7 +402,7 @@ class KretaClient {
|
||||
}
|
||||
|
||||
Future<Response> _authReq(String method, String url, [Object? data]) async {
|
||||
var localToken = await _mutexCallback<String>(() async {
|
||||
var localToken = await mutexCallback<String>(() async {
|
||||
var now = timeNow();
|
||||
|
||||
if (now.millisecondsSinceEpoch >=
|
||||
@@ -366,7 +431,11 @@ class KretaClient {
|
||||
|
||||
return await dio.get(
|
||||
url,
|
||||
options: Options(method: method, headers: headers),
|
||||
options: Options(
|
||||
method: method,
|
||||
headers: headers,
|
||||
receiveTimeout: Duration(seconds: 20),
|
||||
),
|
||||
data: data,
|
||||
);
|
||||
}
|
||||
@@ -381,7 +450,7 @@ class KretaClient {
|
||||
try {
|
||||
logger.finest("Sending authenticated request to: $url");
|
||||
resp = await _authReq(method, url, data);
|
||||
if (!url.endsWith("TanuloAdatlap")) {
|
||||
if (!isDebug() && !url.endsWith("TanuloAdatlap")) {
|
||||
logger.finest("Response: ${resp.statusCode} ${resp.data}");
|
||||
}
|
||||
|
||||
@@ -404,6 +473,9 @@ class KretaClient {
|
||||
);
|
||||
} else {
|
||||
logger.shout("Request to url: $url failed", ex.toString());
|
||||
if (ex is Exception) {
|
||||
logger.shout(ex);
|
||||
}
|
||||
}
|
||||
|
||||
rethrow;
|
||||
@@ -733,8 +805,10 @@ class KretaClient {
|
||||
}) async {
|
||||
if (from == null && to == null) {
|
||||
DateTime now = timeNow();
|
||||
/* you are taking too long °w°
|
||||
DateTime start = now.copyWith(month: 9, day: 1);
|
||||
from = now.isBefore(start) ? start.subtract(Duration(days: 365)) : start;
|
||||
from = now.isBefore(start) ? start.subtract(Duration(days: 365)) : start;*/
|
||||
from = now.subtract(Duration(days: 14));
|
||||
}
|
||||
return await _genericListedCachingGet(
|
||||
CacheId.getHomework,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:typed_data';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:firka/api/client/kreta_client.dart';
|
||||
import 'package:firka/core/bloc/home_refresh_cubit.dart';
|
||||
@@ -17,6 +18,7 @@ import 'package:isar_community/isar.dart';
|
||||
import 'dart:io';
|
||||
|
||||
late final Logger logger;
|
||||
late final FlutterLocalNotificationsPlugin flnp;
|
||||
|
||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
late AppInitialization initData;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:ui' as ui;
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:firka/api/consts.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -25,7 +28,9 @@ import 'package:firka/l10n/app_localizations_en.dart';
|
||||
import 'package:firka/l10n/app_localizations_hu.dart';
|
||||
import 'package:firka/core/swear_generator.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:kreta_api/kreta_api.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:isar_community/isar.dart';
|
||||
@@ -299,6 +304,49 @@ Future<AppInitialization> initializeApp() async {
|
||||
|
||||
await _initData(init);
|
||||
|
||||
initData = init;
|
||||
initDone = true;
|
||||
|
||||
flnp = FlutterLocalNotificationsPlugin();
|
||||
const DarwinInitializationSettings initializationSettingsDarwin =
|
||||
DarwinInitializationSettings(
|
||||
requestSoundPermission: true,
|
||||
requestBadgePermission: true,
|
||||
requestAlertPermission: false,
|
||||
);
|
||||
const AndroidInitializationSettings initializationSettingsAndroid =
|
||||
AndroidInitializationSettings('ic_notification');
|
||||
const InitializationSettings initializationSettings = InitializationSettings(
|
||||
android: initializationSettingsAndroid,
|
||||
iOS: initializationSettingsDarwin,
|
||||
macOS: initializationSettingsDarwin,
|
||||
);
|
||||
await flnp.initialize(settings: initializationSettings);
|
||||
|
||||
Future<void> updateToken([String? fcm]) async {
|
||||
TokenModel? token = pickActiveToken(
|
||||
tokens: initData.tokens,
|
||||
settings: initData.settings,
|
||||
);
|
||||
|
||||
if (token == null) return;
|
||||
try {
|
||||
await initData.client.mutexCallback(() async {
|
||||
if (!await initData.client.refreshTokenProactively()) {
|
||||
throw TokenExpiredException();
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
FirebaseMessaging.instance.onTokenRefresh.listen(
|
||||
(token) => unawaited(updateToken(token)),
|
||||
);
|
||||
|
||||
await updateToken();
|
||||
|
||||
return init;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ class TokenModel {
|
||||
String? idToken; // Unique identifier for the token if needed
|
||||
String? accessToken; // The main auth token
|
||||
String? refreshToken; // Token used to refresh the access token
|
||||
String? registrationId;
|
||||
String? fcmToken;
|
||||
DateTime? expiryDate;
|
||||
int? tokenVersion;
|
||||
int? updatedAtMs;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import 'dart:async';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
@@ -7,8 +10,34 @@ import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/app/initialization.dart';
|
||||
import 'package:firka/app/initialization_screen.dart';
|
||||
|
||||
Future<void> ertesites(String title, String message) async {
|
||||
logger.info("REMOTE Title: $title");
|
||||
logger.info("REMOTE Message: $message");
|
||||
const NotificationDetails notificationDetails = NotificationDetails(
|
||||
android: AndroidNotificationDetails(
|
||||
'teszt',
|
||||
'Értesítés',
|
||||
channelDescription: 'Értesítés a Krétától.',
|
||||
importance: Importance.max,
|
||||
ticker: 'ticker',
|
||||
),
|
||||
);
|
||||
await flnp.show(
|
||||
id: 0,
|
||||
title: title,
|
||||
body: message,
|
||||
notificationDetails: notificationDetails,
|
||||
payload: "szia",
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||
await ertesites(message.data["Title"], message.data["Message"]);
|
||||
}
|
||||
|
||||
void main() async {
|
||||
logger = Logger("Firka");
|
||||
|
||||
dio.options.connectTimeout = Duration(seconds: 5);
|
||||
dio.options.receiveTimeout = Duration(seconds: 3);
|
||||
dio.options.validateStatus = (status) => status != null && status < 500;
|
||||
@@ -28,6 +57,26 @@ void main() async {
|
||||
|
||||
await setupLogging();
|
||||
|
||||
await Firebase.initializeApp(
|
||||
name: defaultFirebaseAppName,
|
||||
options: FirebaseOptions(
|
||||
apiKey: "AIzaSyA_SnXigQkSvFuB5ECpgz8pZ1SjKzuKiFo",
|
||||
appId: "1:694136934013:android:2d6873f63e005250",
|
||||
androidClientId:
|
||||
"694136934013-6e2jmrbqume6lt92d2ceb5se6uru4uvm.apps.googleusercontent.com",
|
||||
projectId: "ellenorzo-v2",
|
||||
messagingSenderId: "694136934013",
|
||||
storageBucket: "ellenorzo-v2.appspot.com",
|
||||
databaseURL: "https://ellenorzo-v2.firebaseio.com",
|
||||
),
|
||||
);
|
||||
|
||||
FirebaseMessaging.instance.setAutoInitEnabled(true);
|
||||
|
||||
FirebaseMessaging.onBackgroundMessage(
|
||||
_firebaseMessagingBackgroundHandler,
|
||||
);
|
||||
|
||||
runApp(InitializationScreen());
|
||||
},
|
||||
(error, stackTrace) {
|
||||
|
||||
@@ -2,6 +2,8 @@ import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:carousel_slider/carousel_slider.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:firka/api/client/kreta_stream.dart';
|
||||
import 'package:firka/ui/phone/widgets/info_card.dart';
|
||||
import 'package:firka/ui/phone/widgets/lesson.dart';
|
||||
@@ -41,6 +43,7 @@ class _HomeMainScreen extends FirkaState<HomeMainScreen> {
|
||||
|
||||
List<Lesson>? lessons;
|
||||
List<NoticeBoardItem>? noticeBoard;
|
||||
List<RemoteMessage> notifications = [];
|
||||
List<InfoBoardItem>? infoBoard;
|
||||
List<Test>? tests;
|
||||
List<Grade>? grades;
|
||||
@@ -189,6 +192,10 @@ class _HomeMainScreen extends FirkaState<HomeMainScreen> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
FirebaseMessaging.onMessage.listen((event) {
|
||||
notifications.add(event);
|
||||
}, onError: (e) => logger.info(e));
|
||||
|
||||
(() async {
|
||||
await fetchData();
|
||||
})();
|
||||
@@ -216,6 +223,29 @@ class _HomeMainScreen extends FirkaState<HomeMainScreen> {
|
||||
final omissionItems = omissions?.groupList((o) => o.date).values ?? [];
|
||||
final noticeBoardWidgets = <(Widget, DateTime)>[];
|
||||
|
||||
for (final n in notifications) {
|
||||
noticeBoardWidgets.add((
|
||||
InfoCard.messageItem(
|
||||
InfoBoardItem(
|
||||
uid: "",
|
||||
title: "Értesítés",
|
||||
author: "Kréta",
|
||||
type: NameUidDesc(
|
||||
uid: "notificaiton",
|
||||
name: "notification",
|
||||
description: "notification",
|
||||
),
|
||||
contentHTML:
|
||||
"<code>${n.data}</code><code>${n.notification}<br/>${n.notification?.title}<br/>${n.notification?.body}<br/>${n.notification?.android}",
|
||||
contentText: "",
|
||||
date: now,
|
||||
createdAt: now,
|
||||
),
|
||||
),
|
||||
now,
|
||||
));
|
||||
}
|
||||
|
||||
for (final item in infoItems) {
|
||||
noticeBoardWidgets.add((InfoCard.messageItem(item), item.date));
|
||||
}
|
||||
|
||||
@@ -165,6 +165,8 @@ class _LoginWebviewWidgetState extends FirkaState<LoginWebviewWidget>
|
||||
|
||||
await initializeApp();
|
||||
|
||||
await initData.client.updateFCMToken();
|
||||
|
||||
if (!mounted) return NavigationDecision.prevent;
|
||||
|
||||
if (mounted) {
|
||||
@@ -286,8 +288,8 @@ class _LoginWebviewWidgetState extends FirkaState<LoginWebviewWidget>
|
||||
opacity: _isLoading
|
||||
? 1.0
|
||||
: _fadeAnimationController!.isAnimating
|
||||
? _fadeAnimation!.value
|
||||
: 0.0,
|
||||
? _fadeAnimation!.value
|
||||
: 0.0,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: Container(
|
||||
color: appStyle.colors.background,
|
||||
@@ -332,7 +334,8 @@ class _LoginWebviewWidgetState extends FirkaState<LoginWebviewWidget>
|
||||
text: _displayPath,
|
||||
style: appStyle.fonts.B_14R.copyWith(
|
||||
fontSize: 16,
|
||||
color: appStyle.colors.textTeritary ??
|
||||
color:
|
||||
appStyle.colors.textTeritary ??
|
||||
appStyle.colors.textSecondary,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: firka
|
||||
description: "Firka, Alternatív e-Kréta kliens."
|
||||
publish_to: 'none'
|
||||
publish_to: "none"
|
||||
|
||||
version: 1.1.2+2001
|
||||
|
||||
@@ -17,10 +17,11 @@ dependencies:
|
||||
|
||||
cupertino_icons: ^1.0.8
|
||||
flutter_launcher_icons: ^0.14.3
|
||||
flutter_local_notifications: ^22.0.0
|
||||
dio: ^5.8.0+1
|
||||
isar_community: 3.3.0
|
||||
isar_community_flutter_libs: 3.3.0
|
||||
build_runner: any
|
||||
isar_community: ^3.3.0
|
||||
isar_community_flutter_libs: ^3.3.0
|
||||
build_runner: ^2.15.0
|
||||
path_provider: ^2.1.0
|
||||
carousel_slider: ^5.0.0
|
||||
webview_flutter: ^4.7.0
|
||||
@@ -30,7 +31,7 @@ dependencies:
|
||||
permission_handler: ^12.0.1
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
intl: any
|
||||
intl: ^0.20.2
|
||||
image_picker: ^1.1.2
|
||||
image: ^4.5.4
|
||||
path: ^1.9.1
|
||||
@@ -59,20 +60,22 @@ dependencies:
|
||||
go_router: ^17.1.0
|
||||
flutter_bloc: ^9.0.0
|
||||
vibration: ^3.1.8
|
||||
firebase_messaging: ^16.2.0
|
||||
firebase_core: ^4.7.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^6.0.0
|
||||
yaml: ^3.1.2
|
||||
isar_community_generator: 3.3.0
|
||||
isar_community_generator: ^3.3.0
|
||||
android_notification_icons: ^0.0.1
|
||||
integration_test:
|
||||
sdk: flutter
|
||||
|
||||
android_notification_icons:
|
||||
image_path: 'assets/images/logos/dave_monochrome.png'
|
||||
icon_name: 'ic_notification'
|
||||
image_path: "assets/images/logos/dave_monochrome.png"
|
||||
icon_name: "ic_notification"
|
||||
|
||||
flutter:
|
||||
generate: true
|
||||
|
||||
Reference in New Issue
Block a user