Специальный код фоновой платформы вызовов Flutter FCM - PullRequest
0 голосов
/ 21 сентября 2019
Фоновый обработчик

FCM не может вызывать специфичный для платформы код, зарегистрированный в MainActivity.java (следуя инструкции flutter ).Есть ли способ сделать эту работу?Я новичок в разработке флаттера, Java и Android, мой поиск зашел в тупик.

Могу ли я принудительно открыть приложение с установленным флагом, проверить этот флаг и затем вызвать метод?

Существует ли какая-либо система событий / сообщений, на которую я мог бы подписаться и отправить из фонового режима?

Следующий код приводит к следующему выводу:

I/flutter ( 6448): BACKGROUND_HANDLER::
I/flutter ( 6448): TRIGGERING ALARM::BEFORE
I/flutter ( 6448): TRIGGERING ALARM::AFTER
E/flutter ( 6448): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: MissingPluginException(No implementation found for method setAlarm on channel com.hybridalert.flutter_hybrid/alarm)
E/flutter ( 6448): #0      MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:314:7)
E/flutter ( 6448): <asynchronous suspension>
E/flutter ( 6448): #1      triggerAlarm (package:flutter_hybrid_alert/src/ui/home.dart:30:37)
E/flutter ( 6448): <asynchronous suspension>
E/flutter ( 6448): #2      backgroundHandle (package:flutter_hybrid_alert/src/ui/home.dart:67:5)
E/flutter ( 6448): #3      _fcmSetupBackgroundChannel.<anonymous closure> (package:firebase_messaging/firebase_messaging.dart:38:30)
E/flutter ( 6448): #4      _AsyncAwaitCompleter.start (dart:async-patch/async_patch.dart:43:6)
E/flutter ( 6448): #5      _fcmSetupBackgroundChannel.<anonymous closure> (package:firebase_messaging/firebase_messaging.dart:31:42)
E/flutter ( 6448): #6      MethodChannel._handleAsMethodCall (package:flutter/src/services/platform_channel.dart:397:55)
E/flutter ( 6448): #7      _AsyncAwaitCompleter.start (dart:async-patch/async_patch.dart:43:6)
E/flutter ( 6448): #8      MethodChannel._handleAsMethodCall (package:flutter/src/services/platform_channel.dart:394:39)
E/flutter ( 6448): #9      MethodChannel.setMethodCallHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:365:54)
E/flutter ( 6448): #10     _DefaultBinaryMessenger.handlePlatformMessage (package:flutter/src/services/binary_messenger.dart:110:33)
E/flutter ( 6448): #11     _AsyncAwaitCompleter.start (dart:async-patch/async_patch.dart:43:6)
E/flutter ( 6448): #12     _DefaultBinaryMessenger.handlePlatformMessage (package:flutter/src/services/binary_messenger.dart:101:37)
E/flutter ( 6448): #13     _invoke3.<anonymous closure> (dart:ui/hooks.dart:293:15)
E/flutter ( 6448): #14     _rootRun (dart:async/zone.dart:1124:13)
E/flutter ( 6448): #15     _CustomZone.run (dart:async/zone.dart:1021:19)
E/flutter ( 6448): #16     _CustomZone.runGuarded (dart:async/zone.dart:923:7)
E/flutter ( 6448): #17     _invoke3 (dart:ui/hooks.dart:292:10)
E/flutter ( 6448): #18     _dispatchPlatformMessage (dart:ui/hooks.dart:154:5)
E/flutter ( 6448): 

home.dart

import "package:cloud_firestore/cloud_firestore.dart";
import "package:firebase_messaging/firebase_messaging.dart";
import "package:flutter/foundation.dart";
import "package:flutter/material.dart";
import "package:flutter/services.dart";
import "package:flutter_hybrid_alert/src/api/firestore.dart";
import "package:flutter_hybrid_alert/src/api/handlers.dart";
import "package:flutter_hybrid_alert/src/api/storage.dart";
import "package:flutter_hybrid_alert/src/models/models.dart";
import "package:flutter_hybrid_alert/src/routes.dart";
import "package:flutter_hybrid_alert/src/store/actions/auth_actions.dart";
import "package:flutter_hybrid_alert/src/store/actions/blacklist_actions.dart";
import "package:flutter_hybrid_alert/src/store/actions/user_actions.dart";
import "package:flutter_hybrid_alert/src/store/app_state.dart";
import "package:flutter_hybrid_alert/src/store/reducers/auth_reducer.dart";
import "package:flutter_hybrid_alert/src/ui/login.dart";
import "package:flutter_hybrid_alert/src/widgets/indicators/app_loading.dart";
import "package:flutter_local_notifications/flutter_local_notifications.dart";
import "package:flutter_redux/flutter_redux.dart";
import "package:nested_navigators/nested_nav_item.dart";
import "package:nested_navigators/nested_navigators.dart";
import "package:redux/redux.dart";

enum NestedNavItemKey { blacklist, recent, history, settings }

const platform = const MethodChannel("com.hybridalert.flutter_hybrid/alarm");

Future<void> triggerAlarm() async {
  try {
    final bool res = await platform.invokeMethod("setAlarm");
    print(res ? "Alarm triggered successfully." : "Alarm trigger failed.");
  } on PlatformException catch (e) {
    print("Error triggering alarm: \"${e.message}\".");
  }
}

/// showNotification - Init local notifications and show a new one.
void showNotification(String body) {
  final androidInitSettings = AndroidInitializationSettings("@mipmap/ic_launcher");
  final initSettings = InitializationSettings(androidInitSettings, null);
  final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
  flutterLocalNotificationsPlugin.initialize(initSettings);

  const String notificationChannel = "HybridAlert";
  final androidPlatformChannelSpecifics = AndroidNotificationDetails(
    notificationChannel,
    notificationChannel,
    "Alerts for Hybrid platform.",
    importance: Importance.Max,
    priority: Priority.High,
    ticker: "ticker",
  );

  final platformChannelSpecifics = NotificationDetails(androidPlatformChannelSpecifics, null);
  flutterLocalNotificationsPlugin.show(0, notificationChannel, body, platformChannelSpecifics);
}

Future<dynamic> backgroundHandle(Map<String, dynamic> message) {
  print("BACKGROUND_HANDLER::");
  if (message.containsKey("data")) {
    // Handle data message
    final dynamic data = message["data"];
    showNotification("BG:: " + data["name"]);

    // THIS THROWS THE ERROR
    print("TRIGGERING ALARM::BEFORE");
    triggerAlarm();
    print("TRIGGERING ALARM::AFTER");
  }

  return null;
}

/// handleNotifications - Force local notifications.
Future<dynamic> handleNotifications(Map<String, dynamic> message) async {
  print("NOTIFICATION_HANDLER::");
  if (message.containsKey("data")) {
    final dynamic data = message["data"];
    return showNotification("FG:: " + data["name"]);
  }

  final dynamic body = message["notification"]["body"];
  return showNotification(body);
}

Future<void> initFCM() async {
  final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
  _firebaseMessaging.requestNotificationPermissions();

  return _firebaseMessaging.getToken().then((token) {
    print("TOKEN:: $token");
    return handleSetUserToken(token);
  }).then((_) {
    return _firebaseMessaging.configure(
      onMessage: handleNotifications,
      onBackgroundMessage: backgroundHandle,
    );
  });
}

class Home extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _HomeState();
  }
}

triggerMainThread() async {
  try {
    final bool res = await platform.invokeMethod("setAlarm");
    print(res ? "Alarm triggered successfully." : "Alarm trigger failed.");
  } on PlatformException catch (e) {
    print("Error triggering alarm: \"${e.message}\".");
  }
}

class _HomeState extends State<Home> {
  AppUserSettings settings;
  String userId;
  Store store;

  @override
  void initState() {
    super.initState();

    // THIS WORKS FINE
    print("TRY_MAIN::BEFORE");
    triggerMainThread();
    print("TRY_MAIN::AFTER");

    initFCM();
    if (userId == null) {
      getStorageUser().then((String id) {
        store.dispatch(SetUserFBEmailAction(id));
        setState(() {
          userId = id;
        });
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    store = StoreProvider.of<AppState>(context);
    final AppState state = store.state;
    final Stream<DocumentSnapshot> userStream =
        userId != null ? usersRef.document(userId).get().asStream() : null;

    return StreamBuilder(
      stream: userStream,
      builder: (BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
        if (!snapshot.hasData || (state.loading.settings && state.auth.hybridAuthenticated)) {
          return buildAppLoadingIndicator("Loading settings...");
        }

        /*       Initialize User Store Data
         ======================================== */

        // Set hybrid email in store from firebase
        final AppUser appUser = AppUser.fromJson(snapshot.data.data);
        final List<String> stateBlacklist = state.blacklist;
        final List<String> blacklist = appUser.settings.blacklist;
        if (blacklist != null) {
          if (stateBlacklist != null) {
            // Compare the two
            final bool listsEqual = listEquals(blacklist, stateBlacklist);
            if (!listsEqual) {
              store.dispatch(SetBlacklistAction(blacklist));
            }
          } else {
            store.dispatch(SetBlacklistAction(blacklist));
          }
        }

        final String hybridEmail = appUser.email;
        final String stateHybridEmail = state.user.hybridEmail;
        final bool hybridEmailChanged = hybridEmail != stateHybridEmail;
        if (hybridEmailChanged) {
          store.dispatch(SetUserHybridEmailAction(hybridEmail));
        }

        // Set firebase userId as fbEmail.
        if (state.user.fbEmail == null) {
          store.dispatch(SetUserFBEmailAction(userId));
        }

        //  Build the Settings UI.
        return NestedNavigators(
          items: {
            NestedNavItemKey.recent: NestedNavigatorItem(
              initialRoute: Routes.recent,
              icon: Icons.home,
              text: "Recent",
            ),
            NestedNavItemKey.history: NestedNavigatorItem(
              initialRoute: Routes.history,
              icon: Icons.history,
              text: "History",
            ),
            NestedNavItemKey.blacklist: NestedNavigatorItem(
              initialRoute: Routes.blacklist,
              icon: Icons.list,
              text: "Blacklist",
            ),
            NestedNavItemKey.settings: NestedNavigatorItem(
              initialRoute: Routes.settings,
              icon: Icons.settings,
              text: "Settings",
            ),
          },
          generateRoute: (routeSettings) {
            return Routes.generateRoute(routeSettings, store.state);
          },
          buildBottomNavigationItem: (key, item, selected) => BottomNavigationBarItem(
            icon: Icon(
              item.icon,
//          color: Colors.blue,
            ),
            title: Text(
              item.text,
              style: TextStyle(
                fontSize: 18,
              ),
            ),
          ),
          bottomNavigationBarTheme: Theme.of(context).copyWith(
            splashColor: Colors.grey,
          ),
        );
      },
    );
  }
}

class AppHome extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return AppHomeState();
  }
}

class AppHomeState extends State {
  String hybridToken;

  @override
  void initState() {
    super.initState();
    getStorageToken().then((token) {
      setState(() {
        hybridToken = token;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return StoreConnector<AppState, AppState>(
      converter: (store) => store.state,
      builder: (BuildContext context, AppState state) {
        final AuthState authState = state.auth;
        final bool firebaseAuth = authState.firebaseAuthenticated;
        final bool hybridAuth = authState.hybridAuthenticated;
        if (!hybridAuth) {
          if (hybridToken != null && hybridToken != "") {
            final AuthState newState = AuthState(true, true);
            store.dispatch(SetAuthAction(newState));
          }
        }

        final bool isLoggedIn = hybridAuth && firebaseAuth;
        if (isLoggedIn) {
          return Home();
        }

        return LoginScreen();
      },
    );
  }
}

MainActivity.java

package com.hybridalert.flutter_hybrid_alert;

import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;

import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterActivity {
    private static final String CHANNEL = "com.hybridalert.flutter_hybrid/alarm";

    // I'm aware this is the default code from the flutter tutorial. Just testing at this point.
    private int setAlarm() {
        int batteryLevel = -1;
        if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
            BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
            batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
        } else {
            Intent intent = new ContextWrapper(getApplicationContext()).
                    registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
            batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /
                    intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
        }

        return batteryLevel;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GeneratedPluginRegistrant.registerWith(this);
        new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
                new MethodCallHandler() {
                    @Override
                    public void onMethodCall(MethodCall call, Result result) {
                        System.out.println("CALL_METHOD::" + call.method);
                        // Note: this method is invoked on the main thread.
                        if (call.method.equals("setAlarm")) {
                            int batteryLevel = setAlarm();

                            if (batteryLevel != -1) {
                                result.success(true);
                            } else {
                                result.error("UNAVAILABLE", "Battery level not available.", null);
                            }
                        } else {
                            result.notImplemented();
                        }
                    }
                });

    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...