Files
tongxunruanjian/mobile-next/lib/core/controller/app_controller.dart
T
编码工程师andmultica-agent 754f161a3f feat(mobile-next): 导入官方 openim-flutter-demo 固定基线 3.8.3-patch.3 并完成双构建
- 上游: 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>
2026-08-19 10:45:55 +08:00

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;
}
}