- 上游: github.com/OpenIMSDK/openim-flutter-demo tag 3.8.3-patch.3 commit b3dfdb1e8aaeaaf6f0793e10cadd20d5c184a31f - 并行目录 mobile-next/ 原样导入(含 LICENSE),不覆盖现网 mobile/ - 锁定 Flutter 3.24.5 / Dart 3.5.4 / JDK 17 / Gradle 7.6.3 / AGP 7.3.1 - 实测 Android debug 与 release 构建均成功(见 docs/mobile-next-baseline-import.md) - 本提交可单独回退:git revert 1db1229(重写前) Co-authored-by: multica-agent <github@multica.ai>
289 lines
9.1 KiB
Dart
289 lines
9.1 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:audio_session/audio_session.dart';
|
|
import 'package:device_info_plus/device_info_plus.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_new_badger/flutter_new_badger.dart';
|
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
|
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart' as im;
|
|
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:just_audio/just_audio.dart';
|
|
import 'package:openim/core/im_callback.dart';
|
|
import 'package:openim_common/openim_common.dart';
|
|
import 'package:sound_mode/sound_mode.dart';
|
|
import 'package:sound_mode/utils/ringer_mode_statuses.dart';
|
|
import 'package:vibration/vibration.dart';
|
|
|
|
import '../../utils/upgrade_manager.dart';
|
|
import 'im_controller.dart';
|
|
|
|
class AppController extends GetxController with UpgradeManger {
|
|
var isRunningBackground = false;
|
|
|
|
final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
|
|
|
final initializationSettingsAndroid = const AndroidInitializationSettings('@mipmap/ic_launcher');
|
|
|
|
final DarwinInitializationSettings initializationSettingsDarwin = const DarwinInitializationSettings(
|
|
requestAlertPermission: false,
|
|
requestBadgePermission: false,
|
|
requestSoundPermission: false,
|
|
);
|
|
|
|
RTCBridge? get rtcBridge => PackageBridge.rtcBridge;
|
|
|
|
bool get shouldMuted =>
|
|
rtcBridge?.hasConnection == true ||
|
|
Get.find<IMController>().imSdkStatusSubject.values.last.status != IMSdkStatus.syncEnded;
|
|
|
|
final _ring = 'assets/audio/message_ring.wav';
|
|
final _audioPlayer = AudioPlayer();
|
|
final configuration = const AudioSessionConfiguration(
|
|
avAudioSessionCategory: AVAudioSessionCategory.ambient,
|
|
avAudioSessionCategoryOptions: AVAudioSessionCategoryOptions.mixWithOthers,
|
|
androidAudioFocusGainType: AndroidAudioFocusGainType.gainTransientMayDuck,
|
|
androidAudioAttributes: AndroidAudioAttributes(
|
|
contentType: AndroidAudioContentType.sonification,
|
|
usage: AndroidAudioUsage.notification,
|
|
),
|
|
);
|
|
late AudioSession session;
|
|
|
|
late BaseDeviceInfo deviceInfo;
|
|
|
|
final clientConfigMap = <String, dynamic>{}.obs;
|
|
|
|
Future<void> runningBackground(bool run) async {
|
|
Logger.print('-----App running background : $run-------------');
|
|
|
|
if (isRunningBackground && !run) {}
|
|
isRunningBackground = run;
|
|
if (!run) {
|
|
_cancelAllNotifications();
|
|
}
|
|
}
|
|
|
|
@override
|
|
void onInit() async {
|
|
_initPlayer();
|
|
final initializationSettings = InitializationSettings(
|
|
android: initializationSettingsAndroid,
|
|
iOS: initializationSettingsDarwin,
|
|
);
|
|
await flutterLocalNotificationsPlugin.initialize(
|
|
initializationSettings,
|
|
onDidReceiveNotificationResponse: (notificationResponse) {},
|
|
);
|
|
|
|
autoCheckVersionUpgrade();
|
|
super.onInit();
|
|
}
|
|
|
|
void _requestPermissions() {
|
|
flutterLocalNotificationsPlugin
|
|
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
|
|
?.requestNotificationsPermission();
|
|
flutterLocalNotificationsPlugin
|
|
.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>()
|
|
?.requestPermissions(
|
|
alert: true,
|
|
badge: true,
|
|
sound: true,
|
|
);
|
|
}
|
|
|
|
Future<void> showNotification(im.Message message, {bool showNotification = true}) async {
|
|
if (_isGlobalNotDisturb() ||
|
|
message.attachedInfoElem?.notSenderNotificationPush == true ||
|
|
message.contentType == im.MessageType.typing ||
|
|
message.sendID == OpenIM.iMManager.userID ||
|
|
(message.contentType! >= 1000 && message.contentType != 1400)) return;
|
|
|
|
var sourceID = message.sessionType == ConversationType.single ? message.sendID : message.groupID;
|
|
if (sourceID != null && message.sessionType != null) {
|
|
var i = await OpenIM.iMManager.conversationManager.getOneConversation(
|
|
sourceID: sourceID,
|
|
sessionType: message.sessionType!,
|
|
);
|
|
if (i.recvMsgOpt != 0) return;
|
|
}
|
|
|
|
if (showNotification) {
|
|
promptSoundOrNotification(message.seq!);
|
|
}
|
|
}
|
|
|
|
Future<void> promptSoundOrNotification(int seq) async {
|
|
if (Get.find<IMController>().imSdkStatusSubject.values.lastOrNull?.status != IMSdkStatus.syncEnded) {
|
|
return;
|
|
}
|
|
if (!isRunningBackground) {
|
|
_playMessageSound();
|
|
} else {
|
|
if (Platform.isAndroid) {
|
|
final id = seq;
|
|
|
|
const androidPlatformChannelSpecifics = AndroidNotificationDetails('chat', 'OpenIM聊天消息',
|
|
channelDescription: '来自OpenIM的信息', importance: Importance.max, priority: Priority.high, ticker: 'ticker');
|
|
const NotificationDetails platformChannelSpecifics =
|
|
NotificationDetails(android: androidPlatformChannelSpecifics);
|
|
await flutterLocalNotificationsPlugin.show(id, '您收到了一条新消息', '消息内容:.....', platformChannelSpecifics,
|
|
payload: '');
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _cancelAllNotifications() async {
|
|
await flutterLocalNotificationsPlugin.cancelAll();
|
|
}
|
|
|
|
Future<void> _startForegroundService() async {
|
|
await getAppInfo();
|
|
const androidPlatformChannelSpecifics = AndroidNotificationDetails('pro', 'OpenIM后台进程',
|
|
channelDescription: '保证app能收到信息', importance: Importance.max, priority: Priority.high, ticker: 'ticker');
|
|
|
|
await flutterLocalNotificationsPlugin
|
|
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
|
|
?.startForegroundService(1, packageInfo!.appName, '正在运行...',
|
|
notificationDetails: androidPlatformChannelSpecifics, payload: '');
|
|
}
|
|
|
|
Future<void> _stopForegroundService() async {
|
|
await flutterLocalNotificationsPlugin
|
|
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
|
|
?.stopForegroundService();
|
|
}
|
|
|
|
void showBadge(count) {
|
|
OpenIM.iMManager.messageManager.setAppBadge(count);
|
|
|
|
if (count == 0) {
|
|
removeBadge();
|
|
PushController.resetBadge();
|
|
} else {
|
|
FlutterNewBadger.setBadge(count);
|
|
PushController.setBadge(count);
|
|
}
|
|
}
|
|
|
|
void removeBadge() {
|
|
FlutterNewBadger.removeBadge();
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
closeSubject();
|
|
_audioPlayer.dispose();
|
|
super.onClose();
|
|
}
|
|
|
|
Locale? getLocale() {
|
|
var local = Get.locale;
|
|
var index = DataSp.getLanguage() ?? 0;
|
|
switch (index) {
|
|
case 1:
|
|
local = const Locale('zh', 'CN');
|
|
break;
|
|
case 2:
|
|
local = const Locale('en', 'US');
|
|
break;
|
|
}
|
|
return local;
|
|
}
|
|
|
|
@override
|
|
void onReady() {
|
|
queryClientConfig();
|
|
_getDeviceInfo();
|
|
_cancelAllNotifications();
|
|
super.onReady();
|
|
}
|
|
|
|
bool _isGlobalNotDisturb() {
|
|
bool isRegistered = Get.isRegistered<IMController>();
|
|
if (isRegistered) {
|
|
var logic = Get.find<IMController>();
|
|
return logic.userInfo.value.globalRecvMsgOpt == 2;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void _initPlayer() async {
|
|
session = await AudioSession.instance;
|
|
await session.configure(configuration);
|
|
|
|
_audioPlayer.setAsset(_ring, package: 'openim_common');
|
|
_audioPlayer.playerStateStream.listen((state) {
|
|
switch (state.processingState) {
|
|
case ProcessingState.idle:
|
|
case ProcessingState.loading:
|
|
case ProcessingState.buffering:
|
|
case ProcessingState.ready:
|
|
break;
|
|
case ProcessingState.completed:
|
|
_stopMessageSound();
|
|
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
|
|
void _playMessageSound() async {
|
|
if (shouldMuted) {
|
|
return;
|
|
}
|
|
bool isRegistered = Get.isRegistered<IMController>();
|
|
bool isAllowVibration = true;
|
|
bool isAllowBeep = true;
|
|
if (isRegistered) {
|
|
var logic = Get.find<IMController>();
|
|
isAllowVibration = logic.userInfo.value.allowVibration == 1;
|
|
isAllowBeep = logic.userInfo.value.allowBeep == 1;
|
|
}
|
|
|
|
RingerModeStatus ringerStatus = await SoundMode.ringerModeStatus;
|
|
|
|
Logger.print('System ringer status: $ringerStatus, user is allow beep: $isAllowBeep',
|
|
fileName: 'app_controller.dart');
|
|
|
|
if (!_audioPlayer.playerState.playing &&
|
|
isAllowBeep &&
|
|
(ringerStatus == RingerModeStatus.normal || ringerStatus == RingerModeStatus.unknown)) {
|
|
await session.setActive(true);
|
|
_audioPlayer.setAsset(_ring, package: 'openim_common');
|
|
_audioPlayer.setLoopMode(LoopMode.off);
|
|
_audioPlayer.setVolume(1.0);
|
|
_audioPlayer.play();
|
|
}
|
|
|
|
if (isAllowVibration &&
|
|
(ringerStatus == RingerModeStatus.normal ||
|
|
ringerStatus == RingerModeStatus.vibrate ||
|
|
ringerStatus == RingerModeStatus.unknown)) {
|
|
if (await Vibration.hasVibrator() == true) {
|
|
Vibration.vibrate();
|
|
}
|
|
}
|
|
}
|
|
|
|
void _stopMessageSound() async {
|
|
if (_audioPlayer.playerState.playing) {
|
|
_audioPlayer.stop();
|
|
}
|
|
await session.setActive(false);
|
|
}
|
|
|
|
void _getDeviceInfo() async {
|
|
final deviceInfoPlugin = DeviceInfoPlugin();
|
|
deviceInfo = await deviceInfoPlugin.deviceInfo;
|
|
}
|
|
|
|
Future queryClientConfig() async {
|
|
final map = await Apis.getClientConfig();
|
|
clientConfigMap.assignAll(map);
|
|
|
|
return clientConfigMap;
|
|
}
|
|
}
|