#firebase #flutter #push-notification #google-cloud-functions #firebase-cloud-messaging
#firebase #флаттер #push-уведомление #google-cloud-функции #firebase-облако-обмен сообщениями
Вопрос:
Я создаю проект домашней автоматизации, в котором есть датчик пожара, который будет записывать данные в базу данных Firebase при обнаружении пожара, а затем с этого момента мне нужно подать сигнал тревоги для пользователя. Мне удалось запустить уведомление из облачных функций Firebase, но это не совсем то, чего я хочу. Что я хочу, так это сделать полноэкранное уведомление для пользователя с пользовательским звуком, что-то вроде телефонного звонка или вызова what’s app, когда есть пожарная тревога — изменение в базе данных.
Я попробовал использовать функцию верхнего уровня без ошибок при запуске моего приложения:
firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
print('onMessage: $message');
toast3('asdasdsawwwww $message');
setMessage(message);
},
onLaunch: (Map<String, dynamic> message) async {
print('onLaunch: $message');
setMessage(message);
},
onResume: (Map<String, dynamic> message) async {
print('onResume: $message');
setMessage(message);
},
onBackgroundMessage: myBackgroundMessageHandler);
print('onMessage:12qew11');
firebaseMessaging.requestNotificationPermissions(
const IosNotificationSettings(sound: true, badge: true, alert: true),
);
}
Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) {
print('HEREE');
final assetsAudioPlayer = AssetsAudioPlayer();
assetsAudioPlayer.open(
Audio("assets/audio/alarm.mp3"),
);
return Fluttertoast.showToast(
msg: 'done background:))))$message',
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.BOTTOM,
timeInSecForIos: 4,
backgroundColor: Colors.redAccent,
textColor: Colors.white,
fontSize: 15.0);
}
Моя функция Firebase:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().functions);
var fireDatabase;
exports.myFirstCloudFun = functions.database.ref('/usersData/{userID}/Fire').onUpdate(async (event, context) => {
const uidGotten = context.params.userID;
const fireData = event.after.val()
console.log('data changed in fire is' fireData 'userID is ' uidGotten);
const usereIdTokens = await admin
.firestore()
.collection(uidGotten)
.doc('userTokens')
.get();
console.log('Tokens to try are' usereIdTokens.data);
var tokens = usereIdTokens.data().user_all_tokens;
var payload = {
notification: {
title: 'Push Title',
body: 'Push Body' fireData,
sound: 'default',
},
data: {
push_key: 'Fire Value Is',
key1: "fireData is " fireData,
},
};
tokens.forEach.toString().trim;
console.log('Tokens to send are ' tokens[1] ' ////// ' tokens);
try {
const response = await admin.messaging().sendToDevice(tokens, payload);
console.log('Notification sent successfully');
} catch (err) {
console.log(err);
}
});
My Application.kt
package com.eghubs.eg_home_hubs
import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService
public class Application: FlutterApplication(), PluginRegistrantCallback {
override fun onCreate() {
super.onCreate()
FlutterFirebaseMessagingService.setPluginRegistrant(this)
}
override fun registerWith(registry: PluginRegistry) {
FirebaseCloudMessagingPluginRegistrant.registerWith(registry)
}
}
My FirebaseCloudMessagingPluginRegistrant.kt
package com.eghubs.eg_home_hubs
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin
class FirebaseCloudMessagingPluginRegistrant {
companion object {
fun registerWith(registry: PluginRegistry) {
if (alreadyRegisteredWith(registry)) {
return;
}
FirebaseMessagingPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"))
}
fun alreadyRegisteredWith(registry: PluginRegistry): Boolean {
val key = FirebaseCloudMessagingPluginRegistrant::class.java.name
if (registry.hasPlugin(key)) {
return true
}
registry.registrarFor(key)
return false
}
}
}
My AndroidManifest is :
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.eghubs.eg_home_hubs">
<uses-permission android:name="android.permission.ACCESS_CORSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET"/>
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here.
android:name="io.flutter.app.FlutterApplication"
android:name="androidx.multidex.MultiDexApplication"
-->
<application
android:name=".Application" <!-- here is the change-->
android:label="EG HomeHubs"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher">
tools:replace="android:allowBackup">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
android:showWhenLocked="true"
android:turnScreenOn="true">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<!-- Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame. -->
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter> <!-- Noti:this is for cloud messiging -->
<action android:name="FLUTTER_NOTIFICATION_CLICK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
but I get nothing when the application is not opened in the background, why onBackgroundMessage function is not called?
My question is how can I achieve that what’s an app call or something of that kind?
OR Is there any better way to do this, some other way to achieve that fire alarm functionality in my project from the firebase database change?
EDIT: My app/build.gradle
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new FileNotFoundException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
//GradleException
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
packagingOptions {
exclude 'META-INF/services/javax.annotation.processing.Processor'
}
compileSdkVersion 30
sourceSets {
main.java.srcDirs = 'src/main/kotlin'
}
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.eghubs.eg_home_hubs"
minSdkVersion 21
targetSdkVersion 30
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
// multiDexEnabled true
}
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
debug {
minifyEnabled true
shrinkResources true
}
}
}
flutter {
source '../..'
}
dependencies {
implementation 'com.google.firebase:firebase-analytics'
implementation platform('com.google.firebase:firebase-bom:26.0.0')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// implementation 'androidx.multidex:multidex:2.0.1' //with androidx libraries
implementation'com.google.firebase:firebase-messaging:21.0.1'
}
apply plugin: 'com.android.application'
// Add this line
apply plugin: 'com.google.gms.google-services'
Мой android / build.gradle:
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.google.gms:google-services:4.3.4'
//classpath 'com.android.tools.build:gradle:3.5.3' //todo rollback this
classpath 'com.android.tools.build:gradle:4.1.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
jcenter()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Комментарии:
1. Я не очень в этом разбираюсь, но я думаю, что этот плагин может вам помочь pub.dev/packages/android_alarm_manager но обратите внимание, что плагин предназначен только для Android
2. к сожалению, если это только для Android, я не могу его использовать, но я благодарен вам за помощь
![]()
3. Тогда, может быть, это: pub.dev/packages/background_fetch
4. Спасибо за ваш ответ, но я кое-что сделал с библиотекой workmanger, но дело в том, что она просто запускается каждые 15 минут, что не очень хорошо, если есть пожарная сигнализация!. Я думаю, что сегодня я попробую что-нибудь, например, канал уведомлений, который позволит мне выбрать пользовательский звук для уведомления, который я выберу как длинный сигнал тревоги, не знаю, возможно ли это (я имею в виду одинокий сигнал тревоги, может быть, 30 секунд будет достаточно), но япопробую в эти дни.
Ответ №1:
У меня есть несколько решений для решения этой проблемы.
Решение 1:
Первое, что вам нужно сделать, это проверить onBackgroundMessage
, поддерживается ли оно в iOS или нет.
А в Android вы должны включить эту Allow running in background
опцию.
Решение 2:
Создайте канал высокой важности через flutter_local_notifications
пакет.
ссылка на пакет flutter_local_notifications: https://pub.dev/packages/flutter_local_notifications
Решение 3:
- Создайте новый файл
App.java
внутри папки java/com/yourdomain
package com.yourdomain;
import io.flutter.app.FlutterApplication;
public class App extends FlutterApplication {
@Override
public void onCreate() {
super.onCreate();
}
}
- Затем внутри
AndroidManifest.xml
файла добавьтеandroid:name=".App"
<application
android:name=".App"
...
>
После этого перестройте приложение, и уведомления будут работать корректно, даже если приложение завершено или убито.
Комментарии:
1. Первое не является решением, и да, эта функция работает с ios, второе позволяет добиться звукового эффекта, и я беспокоюсь об этом, так что спасибо за ваше. для третьего я сделал то, что вы пытаетесь сделать в kotlin, как уже указано в моем вопросе.
Ответ №2:
На Android, чтобы ваше onBackgroundMessage
приложение вызывалось, когда приложение работает в фоновом режиме, сообщение FCM должно быть сообщением данных без уведомления, посмотрите здесь: https://firebase.google.com/docs/cloud-messaging/android/receive
Также, когда устройство находится в спящем режиме, чтобы получать сообщения без особых задержек, вы должны отключить оптимизацию заряда батареи, посмотрите на это: https://developer.android.com/training/monitoring-device-state/doze-standby
Комментарии:
1. Спасибо за ваш ответ :), я пытался сделать полезную нагрузку только сообщением данных, но я не могу это проверить, когда я пытаюсь сделать тост, он выдает мне ошибку, когда я пытаюсь запустить свое приложение, оно выдает ошибку, также, когда я пытаюсь воспроизвести звук, он выдаету меня ошибка, и все они выполняются, когда приложение находится в фоновом режиме и не убито, мне нужно такое взаимодействие, чтобы я мог попробовать с помощью убитого приложения, что, по-вашему, я должен сделать для тревоги ?. также как я могу отключить оптимизацию заряда батареи в flutter?
2. У меня нет опыта работы с flutter, но вы не можете показывать всплывающие окна или диалоги из-за того, что onBackgroundMessage не запускается в основном потоке / контексте пользовательского интерфейса. В стандартном приложении для Android для отображения главного экрана будет использоваться Intent
3. будет ли для библиотеки workmanger, которая выполняет фоновые задачи каждые 15 минут, даже если приложение будет убито, я могу запустить тост, я действительно не знаю, как реализовать экран аварийного сигнала в этом случае.
4. onBackgroundMessage будет вызываться только с данными FCM, даже если приложение будет убито, вопрос в том, как запустить приложение из onBackgroundMessage, я знаю, как это сделать в приложении для Android и Java с использованием и намерением, но я понятия не имею, с помощью flutter
Ответ №3:
Хорошо.
Начиная с обновления 8.0.0-dev.1, мы можем получить onBackgroundMessage по умолчанию без необходимости в этом процессе установки
НОВОЕ: FirebaseMessaging.onBackgroundMessage() Устанавливает обработчик фоновых сообщений для запуска, когда приложение находится в фоновом режиме или завершено.