Как получить полноэкранное уведомление от FCM?

#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:

  1. Создайте новый файл App.java внутри папки java/com/yourdomain
 package com.yourdomain;
import io.flutter.app.FlutterApplication;

public class App extends FlutterApplication {
  @Override
  public void onCreate() {
    super.onCreate();
  }
}
 
  1. Затем внутри 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() Устанавливает обработчик фоновых сообщений для запуска, когда приложение находится в фоновом режиме или завершено.