diff --git a/packages/firebase_messaging/.gitignore b/packages/firebase_messaging/.gitignore new file mode 100644 index 000000000000..14c7d4c3f73e --- /dev/null +++ b/packages/firebase_messaging/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +.atom/ +.idea +.packages +.pub/ +build/ +ios/.generated/ +packages +pubspec.lock diff --git a/packages/firebase_messaging/CHANGELOG.md b/packages/firebase_messaging/CHANGELOG.md new file mode 100644 index 000000000000..2f3ef339c512 --- /dev/null +++ b/packages/firebase_messaging/CHANGELOG.md @@ -0,0 +1,21 @@ +## v0.0.2+1 + +* Added workaround for https://github.com/flutter/flutter/issues/9694 to README +* Moved code to https://github.com/flutter/plugins + +## v0.0.2 + +* Updated to latest plugin API + +## v0.0.1+2 + +* Downgraded gradle dependency for example app to make `flutter run` happy + +## v0.0.1+1 + +* Updated README with installation instructions +* Added CHANGELOG + +## v0.0.1 + +* Initial Release diff --git a/packages/firebase_messaging/LICENSE b/packages/firebase_messaging/LICENSE new file mode 100644 index 000000000000..050286cb5565 --- /dev/null +++ b/packages/firebase_messaging/LICENSE @@ -0,0 +1,26 @@ +Copyright 2017 The Chromium Authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/firebase_messaging/README.md b/packages/firebase_messaging/README.md new file mode 100644 index 000000000000..4b747a53650c --- /dev/null +++ b/packages/firebase_messaging/README.md @@ -0,0 +1,94 @@ +# Firebase Cloud Messaging for Flutter + +[![Build Status](https://travis-ci.org/flutter/firebase_messaging.svg?branch=master)](https://travis-ci.org/flutter/firebase_messaging) +[![pub package](https://img.shields.io/pub/v/firebase_messaging.svg)](https://pub.dartlang.org/packages/firebase_messaging) + +**WARNING: This is incomplete and experimental.** + +This plugin allows Flutter apps to interact with the [Firebase Cloud Messaging (FCM) API](https://firebase.google.com/docs/cloud-messaging/) from Dart code. + +With this plugin, your Flutter app can receive and process push notifications as well as data messages on Android and iOS. Read Firebase's [About FCM Messages](https://firebase.google.com/docs/cloud-messaging/concept-options) to learn more about the differences between notification messages and data messages. + +Not all features of the API are implemented in the plugin yet. If something is missing feel free to send a [pull request](https://github.com/flutter/firebase_messaging/pull/new/master) or file an [issue](https://github.com/flutter/firebase_messaging/issues/new). + +## Getting Started + +Check out the `example` directory for a sample app that uses this plugin. To learn more about Flutter plugins in general, view our [online documentation](https://flutter.io/platform-plugins). + +### Add the Plugin + +Open the `pubspec.yaml` file of your app and under `dependencies` add a line for this plugin: + +```yaml +dependencies: + firebase_messaging: +``` + +You can find the most recent version of the plugin on [pub](https://pub.dartlang.org/packages/firebase_messaging). + +### Android Integration + +To integrate your plugin into the Android part of your app, follow these steps: + +1. Using the [Firebase Console](https://console.firebase.google.com/) add an Android app to your project: Follow the assistant, download the generated `google-services.json` file and place it inside `android/app`. Next, modify the `android/build.gradle` file and the `android/app/build.gradle` file to add the Google services plugin as described by the Firebase assistant. + +1. (optional, but recommended) If want to be notified in your app (via `onResume` and `onLaunch`, see below) when the user clicks on a notification in the system tray include the following `intent-filter` within the `` tag of your `android/app/src/main/AndroidManifest.xml`: + ```xml + + + + + ``` + +### iOS Integration + +To integrate your plugin into the iOS part of your app, follow these steps: + +1. Generate the certificates required by Apple for receiving push notifications following [this guide](https://firebase.google.com/docs/cloud-messaging/ios/certs) in the Firebase docs. You can skip the section titled "Create the Provisioning Profile". + +1. Using the [Firebase Console](https://console.firebase.google.com/) add an iOS app to your project: Follow the assistant, download the generated `GoogleService-Info.plist` file, open `ios/Runner.xcworkspace` with Xcode, and within Xcode place the file inside `ios/Runner`. **Don't** follow the steps named "Add Firebase SDK" and "Add initialization code" in the Firebase assistant. + +1. In Xcode, select `Runner` in the Project Navigator. In the Capabilities Tab turn on `Push Notifications`. + +1. Remove the `use_frameworks!` line from `ios/Podfile` (workaround for [flutter/flutter#9694](https://github.com/flutter/flutter/issues/9694)). + +1. Follow the steps in the "[Upload your APNs certificate](https://firebase.google.com/docs/cloud-messaging/ios/client#upload_your_apns_certificate)" section of the Firebase docs. + +### Dart/Flutter Integration + +From your Dart code, you need to import the plugin and instantiate it: + +```dart +import 'package:firebase_messaging/firebase_messaging.dart'; + +final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging(); +``` + +Next, you should probably request permissions for receiving Push Notifications. For this, call `_firebaseMessaging.requestNotificationPermissions()`. This will bring up a permissions dialog for the user to confirm on iOS. It's a no-op on Android. Last, but not least, register `onMessage`, `onResume`, and `onLaunch` callbacks via `_firebaseMessaging.configure()` to listen for incoming messages (see table below for more information). + +## Receiving Messages + +Messages are sent to your Flutter app via the `onMessage`, `onLaunch`, and `onResume` callbacks that you configured with the plugin during setup. Here is how different message types are delivered on the supported platforms: + +| | App in Foreground | App in Background | App Terminated | +| --------------------------: | ----------------- | ----------------- | -------------- | +| **Notification on Android** | `onMessage` | Notification is delivered to system tray. When the user clicks on it to open app `onResume` fires if `click_action: FLUTTER_NOTIFICATION_CLICK` is set (see below). | Notification is delivered to system tray. When the user clicks on it to open app `onLaunch` fires if `click_action: FLUTTER_NOTIFICATION_CLICK` is set (see below). | +| **Notification on iOS** | `onMessage` | Notification is delivered to system tray. When the user clicks on it to open app `onResume` fires. | Notification is delivered to system tray. When the user clicks on it to open app `onLaunch` fires. | +| **Data Message on Android** | `onMessage` | `onMessage` while app stays in the background. | *not supported by plugin, message is lost* | +| **Data Message on iOS** | `onMessage` | Message is stored by FCM and delivered to app via `onMessage` when the app is brought back to foreground. | Message is stored by FCM and delivered to app via `onMessage` when the app is brought back to foreground. | + +Additional reading: Firebase's [About FCM Messages](https://firebase.google.com/docs/cloud-messaging/concept-options). + +## Sending Messages +Refer to the [Firebase documentation](https://firebase.google.com/docs/cloud-messaging/) about FCM for all the details about sending messages to your app. When sending a notification message to an Android device, you need to make sure to set the `click_action` property of the message to `FLUTTER_NOTIFICATION_CLICK`. Otherwise the plugin will be unable to deliver the notification to your app when the users clicks on it in the system tray. + +For testing purposes, the simplest way to send a notification is via the [Firebase Console](https://firebase.google.com/docs/cloud-messaging/send-with-console). Make sure to include `click_action: FLUTTER_NOTIFICATION_CLICK` as a "Custom data" key-value-pair (under "Advanced options") when targeting an Android device. The Firebase Console does not support sending data messages. + +Alternatively, a notification or data message can be sent from a terminal: + +```shell +DATA='{"notification": {"click_action": "FLUTTER_NOTIFICATION_CLICK", "body": "this is a body","title": "this is a title"}, "priority": "high", "data": {"id": "1", "status": "done"}, "to": ""}' +curl https://fcm.googleapis.com/fcm/send -H "Content-Type:application/json" -X POST -d "$DATA" -H "Authorization: key=" +``` + +Remove the `notification` property in `DATA` to send a data message. diff --git a/packages/firebase_messaging/android/.gitignore b/packages/firebase_messaging/android/.gitignore new file mode 100644 index 000000000000..3214739ce3a1 --- /dev/null +++ b/packages/firebase_messaging/android/.gitignore @@ -0,0 +1,11 @@ +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures + +/gradle +/gradlew +/gradlew.bat diff --git a/packages/firebase_messaging/android/android.iml b/packages/firebase_messaging/android/android.iml new file mode 100644 index 000000000000..c963ea792e12 --- /dev/null +++ b/packages/firebase_messaging/android/android.iml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/firebase_messaging/android/build.gradle b/packages/firebase_messaging/android/build.gradle new file mode 100644 index 000000000000..b165da6744fd --- /dev/null +++ b/packages/firebase_messaging/android/build.gradle @@ -0,0 +1,36 @@ +group 'io.flutter.plugins.firebase_messaging' +version '1.0-SNAPSHOT' + +buildscript { + repositories { + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:2.3.0' + } +} + +allprojects { + repositories { + jcenter() + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 25 + buildToolsVersion '25.0.0' + + defaultConfig { + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + lintOptions { + disable 'InvalidPackage' + } + dependencies { + compile 'com.google.firebase:firebase-core:10.2.1' + compile 'com.google.firebase:firebase-messaging:10.2.4' + } +} diff --git a/packages/firebase_messaging/android/firebase_messaging.iml b/packages/firebase_messaging/android/firebase_messaging.iml new file mode 100644 index 000000000000..9ef7f775f115 --- /dev/null +++ b/packages/firebase_messaging/android/firebase_messaging.iml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/firebase_messaging/android/gradle.properties b/packages/firebase_messaging/android/gradle.properties new file mode 100644 index 000000000000..8bd86f680510 --- /dev/null +++ b/packages/firebase_messaging/android/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx1536M diff --git a/packages/firebase_messaging/android/settings.gradle b/packages/firebase_messaging/android/settings.gradle new file mode 100644 index 000000000000..f82964eb43a1 --- /dev/null +++ b/packages/firebase_messaging/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'firebase_messaging' diff --git a/packages/firebase_messaging/android/src/main/AndroidManifest.xml b/packages/firebase_messaging/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..8c1dd41cf313 --- /dev/null +++ b/packages/firebase_messaging/android/src/main/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase_messaging/FirebaseMessagingPlugin.java b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase_messaging/FirebaseMessagingPlugin.java new file mode 100644 index 000000000000..e4fffa99d78f --- /dev/null +++ b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase_messaging/FirebaseMessagingPlugin.java @@ -0,0 +1,106 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.firebase_messaging; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.support.v4.content.LocalBroadcastManager; +import com.google.firebase.FirebaseApp; +import com.google.firebase.messaging.FirebaseMessaging; +import com.google.firebase.messaging.RemoteMessage; +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.plugin.common.PluginRegistry.NewIntentListener; +import io.flutter.plugin.common.PluginRegistry.Registrar; +import java.util.HashMap; +import java.util.Map; + +/** FirebaseMessagingPlugin */ +public class FirebaseMessagingPlugin extends BroadcastReceiver + implements MethodCallHandler, NewIntentListener { + private final Activity activity; + private final MethodChannel channel; + + private static final String CLICK_ACTION_VALUE = "FLUTTER_NOTIFICATION_CLICK"; + + public static void registerWith(Registrar registrar) { + final MethodChannel channel = new MethodChannel(registrar.messenger(), "firebase_messaging"); + final FirebaseMessagingPlugin plugin = + new FirebaseMessagingPlugin(registrar.activity(), channel); + registrar.addNewIntentListener(plugin); + channel.setMethodCallHandler(plugin); + } + + private FirebaseMessagingPlugin(Activity activity, MethodChannel channel) { + this.activity = activity; + this.channel = channel; + FirebaseApp.initializeApp(activity); + + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(FlutterFirebaseInstanceIDService.ACTION_TOKEN); + intentFilter.addAction(FlutterFirebaseMessagingService.ACTION_REMOTE_MESSAGE); + LocalBroadcastManager manager = LocalBroadcastManager.getInstance(activity); + manager.registerReceiver(this, intentFilter); + } + + // BroadcastReceiver implementation. + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action.equals(FlutterFirebaseInstanceIDService.ACTION_TOKEN)) { + String token = intent.getStringExtra(FlutterFirebaseInstanceIDService.EXTRA_TOKEN); + channel.invokeMethod("onToken", token); + } else if (action.equals(FlutterFirebaseMessagingService.ACTION_REMOTE_MESSAGE)) { + RemoteMessage message = + intent.getParcelableExtra(FlutterFirebaseMessagingService.EXTRA_REMOTE_MESSAGE); + channel.invokeMethod("onMessage", message.getData()); + } + } + + @Override + public void onMethodCall(MethodCall call, Result result) { + if ("configure".equals(call.method)) { + FlutterFirebaseInstanceIDService.broadcastToken(activity); + sendMessageFromIntent("onLaunch", activity.getIntent()); + result.success(null); + } else if ("subscribeToTopic".equals(call.method)) { + String topic = call.arguments(); + FirebaseMessaging.getInstance().subscribeToTopic(topic); + result.success(null); + } else if ("unsubscribeFromTopic".equals(call.method)) { + String topic = call.arguments(); + FirebaseMessaging.getInstance().unsubscribeFromTopic(topic); + result.success(null); + } else { + result.notImplemented(); + } + } + + @Override + public boolean onNewIntent(Intent intent) { + return sendMessageFromIntent("onResume", intent); + } + + /** @return true if intent contained a message to send. */ + private boolean sendMessageFromIntent(String method, Intent intent) { + if (CLICK_ACTION_VALUE.equals(intent.getAction()) + || CLICK_ACTION_VALUE.equals(intent.getStringExtra("click_action"))) { + Map message = new HashMap<>(); + Bundle extras = intent.getExtras(); + for (String key : extras.keySet()) { + message.put(key, extras.get(key).toString()); + } + channel.invokeMethod(method, message); + return true; + } + return false; + } +} diff --git a/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase_messaging/FlutterFirebaseInstanceIDService.java b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase_messaging/FlutterFirebaseInstanceIDService.java new file mode 100644 index 000000000000..2a52e759f2c2 --- /dev/null +++ b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase_messaging/FlutterFirebaseInstanceIDService.java @@ -0,0 +1,29 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.firebase_messaging; + +import android.content.Context; +import android.content.Intent; +import android.support.v4.content.LocalBroadcastManager; +import com.google.firebase.iid.FirebaseInstanceId; +import com.google.firebase.iid.FirebaseInstanceIdService; + +public class FlutterFirebaseInstanceIDService extends FirebaseInstanceIdService { + + public static final String ACTION_TOKEN = "io.flutter.plugins.firebase_messaging.TOKEN"; + public static final String EXTRA_TOKEN = "token"; + + public static void broadcastToken(Context context) { + Intent intent = new Intent(ACTION_TOKEN); + intent.putExtra(EXTRA_TOKEN, FirebaseInstanceId.getInstance().getToken()); + LocalBroadcastManager.getInstance(context).sendBroadcast(intent); + } + + /** Called if InstanceID token is updated. */ + @Override + public void onTokenRefresh() { + broadcastToken(this); + } +} diff --git a/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase_messaging/FlutterFirebaseMessagingService.java b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase_messaging/FlutterFirebaseMessagingService.java new file mode 100644 index 000000000000..90673868027c --- /dev/null +++ b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase_messaging/FlutterFirebaseMessagingService.java @@ -0,0 +1,29 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.firebase_messaging; + +import android.content.Intent; +import android.support.v4.content.LocalBroadcastManager; +import com.google.firebase.messaging.FirebaseMessagingService; +import com.google.firebase.messaging.RemoteMessage; + +public class FlutterFirebaseMessagingService extends FirebaseMessagingService { + + public static final String ACTION_REMOTE_MESSAGE = + "io.flutter.plugins.firebase_messaging.NOTIFICATION"; + public static final String EXTRA_REMOTE_MESSAGE = "notification"; + + /** + * Called when message is received. + * + * @param remoteMessage Object representing the message received from Firebase Cloud Messaging. + */ + @Override + public void onMessageReceived(RemoteMessage remoteMessage) { + Intent intent = new Intent(ACTION_REMOTE_MESSAGE); + intent.putExtra(EXTRA_REMOTE_MESSAGE, remoteMessage); + LocalBroadcastManager.getInstance(this).sendBroadcast(intent); + } +} diff --git a/packages/firebase_messaging/example/.gitignore b/packages/firebase_messaging/example/.gitignore new file mode 100644 index 000000000000..eb15c3d27cab --- /dev/null +++ b/packages/firebase_messaging/example/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +.atom/ +.idea +.packages +.pub/ +build/ +ios/.generated/ +packages +pubspec.lock +.flutter-plugins diff --git a/packages/firebase_messaging/example/README.md b/packages/firebase_messaging/example/README.md new file mode 100644 index 000000000000..4845e56be4ae --- /dev/null +++ b/packages/firebase_messaging/example/README.md @@ -0,0 +1,8 @@ +# firebase_messaging_example + +Demonstrates how to use the firebase_messaging plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](http://flutter.io/). diff --git a/packages/firebase_messaging/example/android.iml b/packages/firebase_messaging/example/android.iml new file mode 100644 index 000000000000..462b903e05b6 --- /dev/null +++ b/packages/firebase_messaging/example/android.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/packages/firebase_messaging/example/android/.gitignore b/packages/firebase_messaging/example/android/.gitignore new file mode 100644 index 000000000000..5c4ef82869b5 --- /dev/null +++ b/packages/firebase_messaging/example/android/.gitignore @@ -0,0 +1,12 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures + +/gradle +/gradlew +/gradlew.bat diff --git a/packages/firebase_messaging/example/android/app/build.gradle b/packages/firebase_messaging/example/android/app/build.gradle new file mode 100644 index 000000000000..134f802483f1 --- /dev/null +++ b/packages/firebase_messaging/example/android/app/build.gradle @@ -0,0 +1,49 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withInputStream { stream -> + localProperties.load(stream) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 25 + buildToolsVersion '25.0.3' + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + applicationId 'io.flutter.plugins.firebase_messaging_example' + } + + buildTypes { + release { + // 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 + } + } +} + +flutter { + source '../..' +} + +dependencies { + androidTestCompile 'com.android.support:support-annotations:24.0.0' + androidTestCompile 'com.android.support.test:runner:0.5' + androidTestCompile 'com.android.support.test:rules:0.5' +} + +apply plugin: 'com.google.gms.google-services' diff --git a/packages/firebase_messaging/example/android/app/google-services.json b/packages/firebase_messaging/example/android/app/google-services.json new file mode 100644 index 000000000000..c45fbe537d7e --- /dev/null +++ b/packages/firebase_messaging/example/android/app/google-services.json @@ -0,0 +1,93 @@ +{ + "project_info": { + "project_number": "3401408244", + "firebase_url": "https://test-project-cloud-messaging.firebaseio.com", + "project_id": "test-project-cloud-messaging", + "storage_bucket": "test-project-cloud-messaging.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:3401408244:android:5920de3503a2d18c", + "android_client_info": { + "package_name": "com.google.firebase.quickstart.fcm" + } + }, + "oauth_client": [ + { + "client_id": "3401408244-s01tdnd5guf3vrrn4n780j22sm20gob8.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.google.firebase.quickstart.fcm", + "certificate_hash": "a647cb35cdac21e3a26f2b5697c5b789cde3b5a0" + } + }, + { + "client_id": "3401408244-m5m1eph6d37rrp71hj33hcttffmmeat0.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBMV3_n5xWH-rzyxsUWbvPPo7-_ZgESSOo" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 2, + "other_platform_oauth_client": [ + { + "client_id": "3401408244-kpa4vjuab7s8gjut5gjdf3hku8gbdp1c.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "io.flutter.plugins.firebase-messaging" + } + }, + { + "client_id": "3401408244-m5m1eph6d37rrp71hj33hcttffmmeat0.apps.googleusercontent.com", + "client_type": 3 + } + ] + }, + "ads_service": { + "status": 2 + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:3401408244:android:fc026014a0a3ac30", + "android_client_info": { + "package_name": "io.flutter.plugins.firebase_messaging_example" + } + }, + "oauth_client": [ + { + "client_id": "3401408244-m5m1eph6d37rrp71hj33hcttffmmeat0.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBMV3_n5xWH-rzyxsUWbvPPo7-_ZgESSOo" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 1, + "other_platform_oauth_client": [] + }, + "ads_service": { + "status": 2 + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/packages/firebase_messaging/example/android/app/src/main/AndroidManifest.xml b/packages/firebase_messaging/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..7260ee4258ab --- /dev/null +++ b/packages/firebase_messaging/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_messaging/example/android/app/src/main/java/io/flutter/plugins/.gitignore b/packages/firebase_messaging/example/android/app/src/main/java/io/flutter/plugins/.gitignore new file mode 100644 index 000000000000..9eb4563d2ae1 --- /dev/null +++ b/packages/firebase_messaging/example/android/app/src/main/java/io/flutter/plugins/.gitignore @@ -0,0 +1 @@ +GeneratedPluginRegistrant.java diff --git a/packages/firebase_messaging/example/android/app/src/main/java/io/flutter/plugins/firebase_messaging_example/MainActivity.java b/packages/firebase_messaging/example/android/app/src/main/java/io/flutter/plugins/firebase_messaging_example/MainActivity.java new file mode 100644 index 000000000000..c6481ee0c2f5 --- /dev/null +++ b/packages/firebase_messaging/example/android/app/src/main/java/io/flutter/plugins/firebase_messaging_example/MainActivity.java @@ -0,0 +1,18 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.firebase_messaging_example; + +import android.os.Bundle; +import io.flutter.app.FlutterActivity; +import io.flutter.plugins.GeneratedPluginRegistrant; + +public class MainActivity extends FlutterActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + GeneratedPluginRegistrant.registerWith(this); + } +} diff --git a/packages/firebase_messaging/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/firebase_messaging/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000000..db77bb4b7b09 Binary files /dev/null and b/packages/firebase_messaging/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/packages/firebase_messaging/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/firebase_messaging/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000000..17987b79bb8a Binary files /dev/null and b/packages/firebase_messaging/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/packages/firebase_messaging/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/firebase_messaging/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000000..09d4391482be Binary files /dev/null and b/packages/firebase_messaging/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/packages/firebase_messaging/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/firebase_messaging/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000000..d5f1c8d34e7a Binary files /dev/null and b/packages/firebase_messaging/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/packages/firebase_messaging/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/firebase_messaging/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000000..4d6372eebdb2 Binary files /dev/null and b/packages/firebase_messaging/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/packages/firebase_messaging/example/android/build.gradle b/packages/firebase_messaging/example/android/build.gradle new file mode 100644 index 000000000000..2023b0f5725e --- /dev/null +++ b/packages/firebase_messaging/example/android/build.gradle @@ -0,0 +1,30 @@ +buildscript { + repositories { + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.google.gms:google-services:3.0.0' + } +} + +allprojects { + repositories { + jcenter() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} + +task wrapper(type: Wrapper) { + gradleVersion = '2.14.1' +} diff --git a/packages/firebase_messaging/example/android/gradle.properties b/packages/firebase_messaging/example/android/gradle.properties new file mode 100644 index 000000000000..8bd86f680510 --- /dev/null +++ b/packages/firebase_messaging/example/android/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx1536M diff --git a/packages/firebase_messaging/example/android/settings.gradle b/packages/firebase_messaging/example/android/settings.gradle new file mode 100644 index 000000000000..115da6cb4f4d --- /dev/null +++ b/packages/firebase_messaging/example/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withInputStream { stream -> plugins.load(stream) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/packages/firebase_messaging/example/example.iml b/packages/firebase_messaging/example/example.iml new file mode 100644 index 000000000000..bbf850c6236c --- /dev/null +++ b/packages/firebase_messaging/example/example.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/firebase_messaging/example/firebase_messaging_example.iml b/packages/firebase_messaging/example/firebase_messaging_example.iml new file mode 100644 index 000000000000..9d5dae19540c --- /dev/null +++ b/packages/firebase_messaging/example/firebase_messaging_example.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/firebase_messaging/example/ios/.gitignore b/packages/firebase_messaging/example/ios/.gitignore new file mode 100644 index 000000000000..5816daf6dafb --- /dev/null +++ b/packages/firebase_messaging/example/ios/.gitignore @@ -0,0 +1,40 @@ +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +.DS_Store +*.swp +profile + +DerivedData/ +build/ + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* + +/Flutter/app.flx +/Flutter/app.zip +/Flutter/App.framework +/Flutter/Flutter.framework +/Flutter/Generated.xcconfig +/ServiceDefinitions.json + +Podfile.lock +Pods diff --git a/packages/firebase_messaging/example/ios/Flutter/AppFrameworkInfo.plist b/packages/firebase_messaging/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000000..6c2de8086bcd --- /dev/null +++ b/packages/firebase_messaging/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + UIRequiredDeviceCapabilities + + arm64 + + MinimumOSVersion + 8.0 + + diff --git a/packages/firebase_messaging/example/ios/Flutter/Debug.xcconfig b/packages/firebase_messaging/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000000..9803018ca79d --- /dev/null +++ b/packages/firebase_messaging/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Generated.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" diff --git a/packages/firebase_messaging/example/ios/Flutter/Release.xcconfig b/packages/firebase_messaging/example/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000000..a4a8c604e13d --- /dev/null +++ b/packages/firebase_messaging/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Generated.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" diff --git a/packages/firebase_messaging/example/ios/Podfile b/packages/firebase_messaging/example/ios/Podfile new file mode 100644 index 000000000000..90b5f651fb63 --- /dev/null +++ b/packages/firebase_messaging/example/ios/Podfile @@ -0,0 +1,36 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +if ENV['FLUTTER_FRAMEWORK_DIR'] == nil + abort('Please set FLUTTER_FRAMEWORK_DIR to the directory containing Flutter.framework') +end + +target 'Runner' do + # Pods for Runner + + # Flutter Pods + pod 'Flutter', :path => ENV['FLUTTER_FRAMEWORK_DIR'] + + if File.exists? '../.flutter-plugins' + flutter_root = File.expand_path('..') + File.foreach('../.flutter-plugins') { |line| + plugin = line.split(pattern='=') + if plugin.length == 2 + name = plugin[0].strip() + path = plugin[1].strip() + resolved_path = File.expand_path("#{path}/ios", flutter_root) + pod name, :path => resolved_path + else + puts "Invalid plugin specification: #{line}" + end + } + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['ENABLE_BITCODE'] = 'NO' + end + end +end diff --git a/packages/firebase_messaging/example/ios/Runner.xcodeproj/project.pbxproj b/packages/firebase_messaging/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..39ea2b06a882 --- /dev/null +++ b/packages/firebase_messaging/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,507 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 2EF3EFF8E25860E112E91756 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 799CAB03D87EEEE1FF65E7BE /* libPods-Runner.a */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; + 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 5C5A74FB1EAA74D700FE37CF /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5C5A74FA1EAA74D700FE37CF /* GoogleService-Info.plist */; }; + 5C8223C01EC265DD00E12B15 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C8223BF1EC265DD00E12B15 /* GeneratedPluginRegistrant.m */; }; + 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; + 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; + 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB31CF90195004384FC /* Generated.xcconfig */; }; + 9740EEBB1CF902C7004384FC /* app.flx in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB71CF902C7004384FC /* app.flx */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, + 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; + 5C5A74FA1EAA74D700FE37CF /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 5C8223BE1EC265DD00E12B15 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 5C8223BF1EC265DD00E12B15 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 5CDE2B941EA81E0D006C00D0 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; + 799CAB03D87EEEE1FF65E7BE /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 9740EEB71CF902C7004384FC /* app.flx */ = {isa = PBXFileReference; lastKnownFileType = file; name = app.flx; path = Flutter/app.flx; sourceTree = ""; }; + 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, + 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, + 2EF3EFF8E25860E112E91756 /* libPods-Runner.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 840012C8B5EDBCF56B0E4AC1 /* Pods */ = { + isa = PBXGroup; + children = ( + ); + name = Pods; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 9740EEB71CF902C7004384FC /* app.flx */, + 3B80C3931E831B6300D905FE /* App.framework */, + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEBA1CF902C7004384FC /* Flutter.framework */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 840012C8B5EDBCF56B0E4AC1 /* Pods */, + CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 5C8223BE1EC265DD00E12B15 /* GeneratedPluginRegistrant.h */, + 5C8223BF1EC265DD00E12B15 /* GeneratedPluginRegistrant.m */, + 5CDE2B941EA81E0D006C00D0 /* Runner.entitlements */, + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 5C5A74FA1EAA74D700FE37CF /* GoogleService-Info.plist */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + CF3B75C9A7D2FA2A4C99F110 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 799CAB03D87EEEE1FF65E7BE /* libPods-Runner.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + AB1344B0443C71CD721E1BB7 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 95BB15E9E1769C0D146AA592 /* [CP] Embed Pods Frameworks */, + 532EA9D341340B1DCD08293D /* [CP] Copy Pods Resources */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0830; + ORGANIZATIONNAME = "The Chromium Authors"; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + DevelopmentTeam = 3GRKCVVJ22; + SystemCapabilities = { + com.apple.Push = { + enabled = 1; + }; + }; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9740EEBB1CF902C7004384FC /* app.flx in Resources */, + 5C5A74FB1EAA74D700FE37CF /* GoogleService-Info.plist in Resources */, + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + }; + 532EA9D341340B1DCD08293D /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 95BB15E9E1769C0D146AA592 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + AB1344B0443C71CD721E1BB7 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 5C8223C01EC265DD00E12B15 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ARCHS = arm64; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + DEVELOPMENT_TEAM = 3GRKCVVJ22; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = "io.flutter.plugins.firebase-messaging"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ARCHS = arm64; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + DEVELOPMENT_TEAM = 3GRKCVVJ22; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = "io.flutter.plugins.firebase-messaging"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/firebase_messaging/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/firebase_messaging/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/firebase_messaging/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/firebase_messaging/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/firebase_messaging/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..1c9580788197 --- /dev/null +++ b/packages/firebase_messaging/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_messaging/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/firebase_messaging/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/firebase_messaging/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/firebase_messaging/example/ios/Runner/.gitignore b/packages/firebase_messaging/example/ios/Runner/.gitignore new file mode 100644 index 000000000000..0cab08d0bdd7 --- /dev/null +++ b/packages/firebase_messaging/example/ios/Runner/.gitignore @@ -0,0 +1,2 @@ +GeneratedPluginRegistrant.h +GeneratedPluginRegistrant.m diff --git a/packages/firebase_messaging/example/ios/Runner/AppDelegate.h b/packages/firebase_messaging/example/ios/Runner/AppDelegate.h new file mode 100644 index 000000000000..d9e18e990f2e --- /dev/null +++ b/packages/firebase_messaging/example/ios/Runner/AppDelegate.h @@ -0,0 +1,10 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/packages/firebase_messaging/example/ios/Runner/AppDelegate.m b/packages/firebase_messaging/example/ios/Runner/AppDelegate.m new file mode 100644 index 000000000000..a4b51c88eb60 --- /dev/null +++ b/packages/firebase_messaging/example/ios/Runner/AppDelegate.m @@ -0,0 +1,16 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "AppDelegate.h" +#include "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..d22f10b2ab63 --- /dev/null +++ b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 000000000000..28c6bf03016f Binary files /dev/null and b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 000000000000..2ccbfd967d96 Binary files /dev/null and b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 000000000000..f091b6b0bca8 Binary files /dev/null and b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 000000000000..4cde12118dda Binary files /dev/null and b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 000000000000..d0ef06e7edb8 Binary files /dev/null and b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 000000000000..dcdc2306c285 Binary files /dev/null and b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 000000000000..2ccbfd967d96 Binary files /dev/null and b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 000000000000..c8f9ed8f5cee Binary files /dev/null and b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 000000000000..a6d6b8609df0 Binary files /dev/null and b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 000000000000..a6d6b8609df0 Binary files /dev/null and b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 000000000000..75b2d164a5a9 Binary files /dev/null and b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 000000000000..c4df70d39da7 Binary files /dev/null and b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 000000000000..6a84f41e14e2 Binary files /dev/null and b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 000000000000..d0e1f5853602 Binary files /dev/null and b/packages/firebase_messaging/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/packages/firebase_messaging/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/firebase_messaging/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000000..ebf48f603974 --- /dev/null +++ b/packages/firebase_messaging/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_messaging/example/ios/Runner/Base.lproj/Main.storyboard b/packages/firebase_messaging/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000000..f3c28516fb38 --- /dev/null +++ b/packages/firebase_messaging/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_messaging/example/ios/Runner/GoogleService-Info.plist b/packages/firebase_messaging/example/ios/Runner/GoogleService-Info.plist new file mode 100644 index 000000000000..aae17f35f51a --- /dev/null +++ b/packages/firebase_messaging/example/ios/Runner/GoogleService-Info.plist @@ -0,0 +1,40 @@ + + + + + AD_UNIT_ID_FOR_BANNER_TEST + ca-app-pub-3940256099942544/2934735716 + AD_UNIT_ID_FOR_INTERSTITIAL_TEST + ca-app-pub-3940256099942544/4411468910 + CLIENT_ID + 3401408244-kpa4vjuab7s8gjut5gjdf3hku8gbdp1c.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.3401408244-kpa4vjuab7s8gjut5gjdf3hku8gbdp1c + API_KEY + AIzaSyCwJxWJTafbmE8PNdWEbU5Ciw5z1R5Fl3k + GCM_SENDER_ID + 3401408244 + PLIST_VERSION + 1 + BUNDLE_ID + io.flutter.plugins.firebase-messaging + PROJECT_ID + test-project-cloud-messaging + STORAGE_BUCKET + test-project-cloud-messaging.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:3401408244:ios:d297dea19b53a7da + DATABASE_URL + https://test-project-cloud-messaging.firebaseio.com + + \ No newline at end of file diff --git a/packages/firebase_messaging/example/ios/Runner/Info.plist b/packages/firebase_messaging/example/ios/Runner/Info.plist new file mode 100644 index 000000000000..ededae49efe7 --- /dev/null +++ b/packages/firebase_messaging/example/ios/Runner/Info.plist @@ -0,0 +1,51 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Firebase Cloud Messaging Example + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + firebase_messaging_example + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/packages/firebase_messaging/example/ios/Runner/Runner.entitlements b/packages/firebase_messaging/example/ios/Runner/Runner.entitlements new file mode 100644 index 000000000000..903def2af530 --- /dev/null +++ b/packages/firebase_messaging/example/ios/Runner/Runner.entitlements @@ -0,0 +1,8 @@ + + + + + aps-environment + development + + diff --git a/packages/firebase_messaging/example/ios/Runner/main.m b/packages/firebase_messaging/example/ios/Runner/main.m new file mode 100644 index 000000000000..dff6597e4513 --- /dev/null +++ b/packages/firebase_messaging/example/ios/Runner/main.m @@ -0,0 +1,9 @@ +#import +#import +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/packages/firebase_messaging/example/lib/main.dart b/packages/firebase_messaging/example/lib/main.dart new file mode 100644 index 000000000000..e3cfd9425c5c --- /dev/null +++ b/packages/firebase_messaging/example/lib/main.dart @@ -0,0 +1,229 @@ +// Copyright 2017 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/material.dart'; + +final Map _items = {}; +Item _itemForMessage(Map message) { + String itemId = message['id']; + Item item = _items.putIfAbsent(itemId, () => new Item(itemId: itemId)) + ..status = message['status']; + return item; +} + +class Item { + Item({this.itemId}); + final String itemId; + + StreamController _controller = new StreamController.broadcast(); + Stream get onChanged => _controller.stream; + + String _status; + String get status => _status; + set status(String value) { + _status = value; + _controller.add(this); + } + + static final Map routes = {}; + Route get route { + String routeName = '/detail/$itemId'; + return routes.putIfAbsent( + routeName, + () => new MaterialPageRoute( + settings: new RouteSettings(name: routeName), + builder: (BuildContext context) => new DetailPage(itemId), + ), + ); + } +} + +class DetailPage extends StatefulWidget { + DetailPage(this.itemId); + final String itemId; + @override + _DetailPageState createState() => new _DetailPageState(); +} + +class _DetailPageState extends State { + Item _item; + StreamSubscription _subscription; + + @override + void initState() { + super.initState(); + _item = _items[widget.itemId]; + _subscription = _item.onChanged.listen((Item item) { + if (!mounted) { + _subscription.cancel(); + } else { + setState(() { + _item = item; + }); + } + }); + } + + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar( + title: new Text("Item ${_item.itemId}"), + ), + body: new Material( + child: new Center(child: new Text("Item status: ${_item.status}")), + ), + ); + } +} + +class PushMessagingExample extends StatefulWidget { + @override + _PushMessagingExampleState createState() => new _PushMessagingExampleState(); +} + +class _PushMessagingExampleState extends State { + String _homeScreenText = "Waiting for token..."; + bool _topicButtonsDisabled = false; + + final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging(); + final TextEditingController _topicController = + new TextEditingController(text: 'topic'); + + Future _showItemDialog(Map message) async { + Item item = _itemForMessage(message); + showDialog( + context: context, + child: new AlertDialog( + content: new Text("Item ${item.itemId} has been updated"), + actions: [ + new FlatButton( + child: new Text('CLOSE'), + onPressed: () { + Navigator.pop(context, false); + }), + new FlatButton( + child: new Text('SHOW'), + onPressed: () { + Navigator.pop(context, true); + }), + ], + )).then((bool shouldNavigate) { + if (shouldNavigate == true) _navigateToItemDetail(message); + }); + } + + Future _navigateToItemDetail(Map message) async { + Item item = _itemForMessage(message); + // Clear away dialogs + Navigator.popUntil(context, (Route route) => route is PageRoute); + if (!item.route.isCurrent) Navigator.push(context, item.route); + } + + @override + void initState() { + super.initState(); + _firebaseMessaging.configure( + onMessage: (Map message) { + print("onMessage: $message"); + _showItemDialog(message); + }, + onLaunch: (Map message) { + print("onLaunch: $message"); + _navigateToItemDetail(message); + }, + onResume: (Map message) { + print("onResume: $message"); + _navigateToItemDetail(message); + }, + ); + _firebaseMessaging.requestNotificationPermissions( + const IosNotificationSettings(sound: true, badge: true, alert: true)); + _firebaseMessaging.onIosSettingsRegistered + .listen((IosNotificationSettings settings) { + print("Settings registered: $settings"); + }); + _firebaseMessaging.getToken().then((String token) { + assert(token != null); + setState(() { + _homeScreenText = "Push Messaging token: $token"; + }); + print(_homeScreenText); + }); + } + + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar( + title: new Text('Push Messaging Demo'), + ), + // For testing -- simulate a message being received + floatingActionButton: new FloatingActionButton( + onPressed: () => _showItemDialog({ + "id": "2", + "status": "out of stock", + }), + tooltip: 'Simulate Message', + child: new Icon(Icons.message), + ), + body: new Material( + child: new Column( + children: [ + new Center( + child: new Text(_homeScreenText), + ), + new Row(children: [ + new Expanded( + child: new TextField( + controller: _topicController, + onChanged: (String v) { + setState(() { + _topicButtonsDisabled = v.length == 0; + }); + }), + ), + new FlatButton( + child: new Text("subscribe"), + onPressed: _topicButtonsDisabled + ? null + : () { + _firebaseMessaging + .subscribeToTopic(_topicController.text); + _clearTopicText(); + }, + ), + new FlatButton( + child: new Text("unsubscribe"), + onPressed: _topicButtonsDisabled + ? null + : () { + _firebaseMessaging + .unsubscribeFromTopic(_topicController.text); + _clearTopicText(); + }, + ), + ]) + ], + ), + )); + } + + void _clearTopicText() { + setState(() { + _topicController.text = ""; + _topicButtonsDisabled = true; + }); + } +} + +void main() { + runApp( + new MaterialApp( + home: new PushMessagingExample(), + ), + ); +} diff --git a/packages/firebase_messaging/example/pubspec.yaml b/packages/firebase_messaging/example/pubspec.yaml new file mode 100644 index 000000000000..d97172586c56 --- /dev/null +++ b/packages/firebase_messaging/example/pubspec.yaml @@ -0,0 +1,53 @@ +name: firebase_messaging_example +description: Demonstrates how to use the firebase_messaging plugin. + +dependencies: + flutter: + sdk: flutter + firebase_messaging: + path: ../ + +# For information on the generic Dart part of this file, see the +# following page: https://www.dartlang.org/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section here, in + # this "flutter" section, as in: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # To add assets from package dependencies, first ensure the asset + # is in the lib/ directory of the dependency. Then, + # refer to the asset with a path prefixed with + # `packages/PACKAGE_NAME/`. Note: the `lib/` is implied, do not + # include `lib/` in the asset path. + # + # Here is an example: + # + # assets: + # - packages/PACKAGE_NAME/path/to/asset + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 diff --git a/packages/firebase_messaging/firebase_messaging.iml b/packages/firebase_messaging/firebase_messaging.iml new file mode 100644 index 000000000000..dff626c24d4a --- /dev/null +++ b/packages/firebase_messaging/firebase_messaging.iml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/firebase_messaging/ios/.gitignore b/packages/firebase_messaging/ios/.gitignore new file mode 100644 index 000000000000..956c87f3aa28 --- /dev/null +++ b/packages/firebase_messaging/ios/.gitignore @@ -0,0 +1,31 @@ +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +.DS_Store +*.swp +profile + +DerivedData/ +build/ + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* + diff --git a/packages/firebase_messaging/ios/Assets/.gitkeep b/packages/firebase_messaging/ios/Assets/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/firebase_messaging/ios/Classes/FirebaseMessagingPlugin.h b/packages/firebase_messaging/ios/Classes/FirebaseMessagingPlugin.h new file mode 100644 index 000000000000..2512ea40bb9e --- /dev/null +++ b/packages/firebase_messaging/ios/Classes/FirebaseMessagingPlugin.h @@ -0,0 +1,8 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +@interface FirebaseMessagingPlugin : NSObject +@end diff --git a/packages/firebase_messaging/ios/Classes/FirebaseMessagingPlugin.m b/packages/firebase_messaging/ios/Classes/FirebaseMessagingPlugin.m new file mode 100644 index 000000000000..02513940a800 --- /dev/null +++ b/packages/firebase_messaging/ios/Classes/FirebaseMessagingPlugin.m @@ -0,0 +1,180 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FirebaseMessagingPlugin.h" + +#import "Firebase/Firebase.h" + +#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +@interface FirebaseMessagingPlugin () +@end +#endif + +@implementation FirebaseMessagingPlugin { + FlutterMethodChannel *_channel; + NSDictionary *_launchNotification; + BOOL _resumingFromBackground; +} + ++ (void)registerWithRegistrar:(NSObject *)registrar { + FlutterMethodChannel *channel = + [FlutterMethodChannel methodChannelWithName:@"firebase_messaging" + binaryMessenger:[registrar messenger]]; + FirebaseMessagingPlugin *instance = [[FirebaseMessagingPlugin alloc] initWithChannel:channel]; + [registrar addApplicationDelegate:instance]; + [registrar addMethodCallDelegate:instance channel:channel]; +} + +- (instancetype)initWithChannel:(FlutterMethodChannel *)channel { + self = [super init]; + if (self) { + _channel = channel; + _resumingFromBackground = NO; + if (![FIRApp defaultApp]) { + [FIRApp configure]; + } + [FIRMessaging messaging].remoteMessageDelegate = self; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(tokenRefreshNotification:) + name:kFIRInstanceIDTokenRefreshNotification + object:nil]; + } + return self; +} + +- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { + NSString *method = call.method; + if ([@"requestNotificationPermissions" isEqualToString:method]) { + UIUserNotificationType notificationTypes = 0; + NSDictionary *arguments = call.arguments; + if (arguments[@"sound"]) { + notificationTypes |= UIUserNotificationTypeSound; + } + if (arguments[@"alert"]) { + notificationTypes |= UIUserNotificationTypeAlert; + } + if (arguments[@"badge"]) { + notificationTypes |= UIUserNotificationTypeBadge; + } + UIUserNotificationSettings *settings = + [UIUserNotificationSettings settingsForTypes:notificationTypes categories:nil]; + [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; + + result(nil); + } else if ([@"configure" isEqualToString:method]) { + [[UIApplication sharedApplication] registerForRemoteNotifications]; + if (_launchNotification != nil) { + [_channel invokeMethod:@"onLaunch" arguments:_launchNotification]; + } + result(nil); + } else if ([@"subscribeToTopic" isEqualToString:method]) { + NSString *topic = call.arguments; + [[FIRMessaging messaging] subscribeToTopic:topic]; + result(nil); + } else if ([@"unsubscribeFromTopic" isEqualToString:method]) { + NSString *topic = call.arguments; + [[FIRMessaging messaging] unsubscribeFromTopic:topic]; + result(nil); + } else { + result(FlutterMethodNotImplemented); + } +} + +- (void)tokenRefreshNotification:(NSNotification *)notification { + NSString *refreshedToken = [[FIRInstanceID instanceID] token]; + + // Connect to FCM since connection may have failed when attempted before having a token. + [self connectToFcm]; + + [_channel invokeMethod:@"onToken" arguments:refreshedToken]; +} + +#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +// Receive data message on iOS 10 devices while app is in the foreground. +- (void)applicationReceivedRemoteMessage:(FIRMessagingRemoteMessage *)remoteMessage { + [self didReceiveRemoteNotification:remoteMessage.appData]; +} +#endif + +- (void)connectToFcm { + // Won't connect since there is no token + if (![[FIRInstanceID instanceID] token]) { + return; + } + + // Disconnect previous FCM connection if it exists. + [[FIRMessaging messaging] disconnect]; + + [[FIRMessaging messaging] connectWithCompletion:^(NSError *_Nullable error) { + if (error != nil) { + NSLog(@"Unable to connect to FCM. %@", error); + } + }]; +} + +- (void)didReceiveRemoteNotification:(NSDictionary *)userInfo { + if (_resumingFromBackground) { + [_channel invokeMethod:@"onResume" arguments:userInfo]; + } else { + [_channel invokeMethod:@"onMessage" arguments:userInfo]; + } +} + +#pragma mark - AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + if (launchOptions != nil) { + _launchNotification = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]; + } + return YES; +} + +- (void)applicationDidEnterBackground:(UIApplication *)application { + [[FIRMessaging messaging] disconnect]; + _resumingFromBackground = YES; +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + _resumingFromBackground = NO; + [self connectToFcm]; + // Clears push notifications from the notification center, with the + // side effect of resetting the badge count. We need to clear notifications + // because otherwise the user could tap notifications in the notification + // center while the app is in the foreground, and we wouldn't be able to + // distinguish that case from the case where a message came in and the + // user dismissed the notification center without tapping anything. + // TODO(goderbauer): Revisit this behavior once we provide an API for managing + // the badge number, or if we add support for running Dart in the background. + // Setting badgeNumber to 0 is a no-op (= notifications will not be cleared) if it is already 0, + // therefore the next line is setting it to 1 first before clearing it again to remove all + // notifications. + application.applicationIconBadgeNumber = 1; + application.applicationIconBadgeNumber = 0; +} + +- (bool)application:(UIApplication *)application + didReceiveRemoteNotification:(NSDictionary *)userInfo + fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler { + [self didReceiveRemoteNotification:userInfo]; + completionHandler(UIBackgroundFetchResultNoData); + return YES; +} + +- (void)application:(UIApplication *)application + didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { + [_channel invokeMethod:@"onToken" arguments:[[FIRInstanceID instanceID] token]]; +} + +- (void)application:(UIApplication *)application + didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings { + NSDictionary *settingsDictionary = @{ + @"sound" : [NSNumber numberWithBool:notificationSettings.types & UIUserNotificationTypeSound], + @"badge" : [NSNumber numberWithBool:notificationSettings.types & UIUserNotificationTypeBadge], + @"alert" : [NSNumber numberWithBool:notificationSettings.types & UIUserNotificationTypeAlert], + }; + [_channel invokeMethod:@"onIosSettingsRegistered" arguments:settingsDictionary]; +} + +@end diff --git a/packages/firebase_messaging/ios/firebase_messaging.podspec b/packages/firebase_messaging/ios/firebase_messaging.podspec new file mode 100644 index 000000000000..a2ee159659c1 --- /dev/null +++ b/packages/firebase_messaging/ios/firebase_messaging.podspec @@ -0,0 +1,23 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html +# +Pod::Spec.new do |s| + s.name = 'firebase_messaging' + s.version = '0.0.1' + s.summary = 'Firebase Cloud Messaging plugin for Flutter.' + s.description = <<-DESC +Firebase Cloud Messaging plugin for Flutter. + DESC + s.homepage = 'https://github.com/flutter/firebase_messaging' + s.license = { :file => '../LICENSE' } + s.author = { 'Google Inc.' => 'goderbauer@google.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.public_header_files = 'Classes/**/*.h' + s.dependency 'Flutter' + s.dependency 'Firebase/Core' + s.dependency 'Firebase/Messaging' + + s.ios.deployment_target = '8.0' +end + diff --git a/packages/firebase_messaging/lib/firebase_messaging.dart b/packages/firebase_messaging/lib/firebase_messaging.dart new file mode 100644 index 000000000000..4cc397eeac82 --- /dev/null +++ b/packages/firebase_messaging/lib/firebase_messaging.dart @@ -0,0 +1,146 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:meta/meta.dart'; +import 'package:platform/platform.dart'; + +typedef Future MessageHandler(Map message); + +/// Implementation of the Firebase Cloud Messaging API for Flutter. +/// +/// Your app should call [requestNotificationPermissions] first and then +/// register handlers for incoming messages with [configure]. +class FirebaseMessaging { + factory FirebaseMessaging() => _instance; + + @visibleForTesting + FirebaseMessaging.private(MethodChannel channel, Platform platform) + : _channel = channel, + _platform = platform; + + static final FirebaseMessaging _instance = new FirebaseMessaging.private( + const MethodChannel('firebase_messaging'), new LocalPlatform()); + + final MethodChannel _channel; + final Platform _platform; + + MessageHandler _onMessage; + MessageHandler _onLaunch; + MessageHandler _onResume; + String _token; + + /// On iOS, prompts the user for notification permissions the first time + /// it is called. + /// + /// Does nothing on Android. + void requestNotificationPermissions( + [IosNotificationSettings iosSettings = const IosNotificationSettings()]) { + if (!_platform.isIOS) { + return; + } + _channel.invokeMethod( + 'requestNotificationPermissions', iosSettings.toMap()); + } + + final StreamController _iosSettingsStreamController = + new StreamController.broadcast(); + + /// Stream that fires when the user changes their notification settings. + /// + /// Only fires on iOS. + Stream get onIosSettingsRegistered { + return _iosSettingsStreamController.stream; + } + + /// Sets up [MessageHandler] for incoming messages. + void configure({ + MessageHandler onMessage, + MessageHandler onLaunch, + MessageHandler onResume, + }) { + _onMessage = onMessage; + _onLaunch = onLaunch; + _onResume = onResume; + _channel.setMethodCallHandler(_handleMethod); + _channel.invokeMethod('configure'); + } + + final StreamController _tokenStreamController = + new StreamController.broadcast(); + + /// Fires when a new FCM token is generated. + Stream get onTokenRefresh { + return _tokenStreamController.stream; + } + + /// Returns the FCM token. + Future getToken() { + return _token != null ? new Future.value(_token) : onTokenRefresh.first; + } + + /// Subscribe to topic in background. + /// + /// [topic] must match the following regular expression: + /// "[a-zA-Z0-9-_.~%]{1,900}". + void subscribeToTopic(String topic) { + _channel.invokeMethod('subscribeToTopic', topic); + } + + /// Unsubscribe from topic in background. + void unsubscribeFromTopic(String topic) { + _channel.invokeMethod('unsubscribeFromTopic', topic); + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onToken": + String token = call.arguments; + if (_token != token) { + _token = token; + _tokenStreamController.add(_token); + } + return null; + case "onIosSettingsRegistered": + _iosSettingsStreamController + .add(new IosNotificationSettings._fromMap(call.arguments)); + return null; + case "onMessage": + return _onMessage(call.arguments); + case "onLaunch": + return _onLaunch(call.arguments); + case "onResume": + return _onResume(call.arguments); + default: + throw new UnsupportedError("Unrecognized JSON message"); + } + } +} + +class IosNotificationSettings { + final bool sound; + final bool alert; + final bool badge; + + const IosNotificationSettings({ + this.sound: true, + this.alert: true, + this.badge: true, + }); + + IosNotificationSettings._fromMap(Map settings) + : sound = settings['sound'], + alert = settings['alert'], + badge = settings['badge']; + + @visibleForTesting + Map toMap() { + return {'sound': sound, 'alert': alert, 'badge': badge}; + } + + @override + String toString() => 'PushNotificationSettings ${toMap()}'; +} diff --git a/packages/firebase_messaging/pubspec.yaml b/packages/firebase_messaging/pubspec.yaml new file mode 100644 index 000000000000..c386ee08c9d3 --- /dev/null +++ b/packages/firebase_messaging/pubspec.yaml @@ -0,0 +1,24 @@ +name: firebase_messaging + +description: Firebase Cloud Messaging plugin for Flutter applications. +author: Flutter Team +homepage: https://github.com/flutter/plugins/tree/master/packages/firebase_messaging +version: 0.0.2+1 + +flutter: + plugin: + androidPackage: io.flutter.plugins.firebase_messaging + pluginClass: FirebaseMessagingPlugin + +dependencies: + meta: ^1.0.4 + platform: ^2.0.0 + flutter: + sdk: flutter + +dev_dependencies: + mockito: ^2.0.2 + test: ^0.12.20 + +environment: + sdk: ">=1.8.0 <2.0.0" diff --git a/packages/firebase_messaging/test/firebase_messaging_test.dart b/packages/firebase_messaging/test/firebase_messaging_test.dart new file mode 100644 index 000000000000..abb61f979477 --- /dev/null +++ b/packages/firebase_messaging/test/firebase_messaging_test.dart @@ -0,0 +1,141 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:mockito/mockito.dart'; +import 'package:platform/platform.dart'; +import 'package:test/test.dart'; + +void main() { + MockMethodChannel mockChannel; + FirebaseMessaging firebaseMessaging; + + setUp(() { + mockChannel = new MockMethodChannel(); + firebaseMessaging = new FirebaseMessaging.private( + mockChannel, new FakePlatform(operatingSystem: 'ios')); + }); + + test('requestNotificationPermissions on ios with default permissions', () { + firebaseMessaging.requestNotificationPermissions(); + verify(mockChannel.invokeMethod('requestNotificationPermissions', + {'sound': true, 'badge': true, 'alert': true})); + }); + + test('requestNotificationPermissions on ios with custom permissions', () { + firebaseMessaging.requestNotificationPermissions( + const IosNotificationSettings(sound: false)); + verify(mockChannel.invokeMethod('requestNotificationPermissions', + {'sound': false, 'badge': true, 'alert': true})); + }); + + test('requestNotificationPermissions on android', () { + firebaseMessaging = new FirebaseMessaging.private( + mockChannel, new FakePlatform(operatingSystem: 'android')); + + firebaseMessaging.requestNotificationPermissions(); + verifyZeroInteractions(mockChannel); + }); + + test('requestNotificationPermissions on android', () { + firebaseMessaging = new FirebaseMessaging.private( + mockChannel, new FakePlatform(operatingSystem: 'android')); + + firebaseMessaging.requestNotificationPermissions(); + verifyZeroInteractions(mockChannel); + }); + + test('configure', () { + firebaseMessaging.configure(); + verify(mockChannel.setMethodCallHandler(any)); + verify(mockChannel.invokeMethod('configure')); + }); + + test('incoming token', () async { + firebaseMessaging.configure(); + dynamic handler = + verify(mockChannel.setMethodCallHandler(captureAny)).captured.single; + String token1 = 'I am a super secret token'; + String token2 = 'I am the new token in town'; + Future tokenFromStream = firebaseMessaging.onTokenRefresh.first; + await handler(new MethodCall('onToken', token1)); + + expect(await firebaseMessaging.getToken(), token1); + expect(await tokenFromStream, token1); + + tokenFromStream = firebaseMessaging.onTokenRefresh.first; + await handler(new MethodCall('onToken', token2)); + + expect(await firebaseMessaging.getToken(), token2); + expect(await tokenFromStream, token2); + }); + + test('incoming iOS settings', () async { + firebaseMessaging.configure(); + dynamic handler = + verify(mockChannel.setMethodCallHandler(captureAny)).captured.single; + IosNotificationSettings iosSettings = new IosNotificationSettings(); + + Future iosSettingsFromStream = + firebaseMessaging.onIosSettingsRegistered.first; + await handler( + new MethodCall('onIosSettingsRegistered', iosSettings.toMap())); + expect((await iosSettingsFromStream).toMap(), iosSettings.toMap()); + + iosSettings = new IosNotificationSettings(sound: false); + iosSettingsFromStream = firebaseMessaging.onIosSettingsRegistered.first; + await handler( + new MethodCall('onIosSettingsRegistered', iosSettings.toMap())); + expect((await iosSettingsFromStream).toMap(), iosSettings.toMap()); + }); + + test('incoming messages', () async { + Completer onMessage = new Completer(); + Completer onLaunch = new Completer(); + Completer onResume = new Completer(); + + firebaseMessaging.configure(onMessage: (m) { + onMessage.complete(m); + }, onLaunch: (m) { + onLaunch.complete(m); + }, onResume: (m) { + onResume.complete(m); + }); + dynamic handler = + verify(mockChannel.setMethodCallHandler(captureAny)).captured.single; + + Object onMessageMessage = new Object(); + Object onLaunchMessage = new Object(); + Object onResumeMessage = new Object(); + + await handler(new MethodCall('onMessage', onMessageMessage)); + expect(await onMessage.future, onMessageMessage); + expect(onLaunch.isCompleted, isFalse); + expect(onResume.isCompleted, isFalse); + + await handler(new MethodCall('onLaunch', onLaunchMessage)); + expect(await onLaunch.future, onLaunchMessage); + expect(onResume.isCompleted, isFalse); + + await handler(new MethodCall('onResume', onResumeMessage)); + expect(await onResume.future, onResumeMessage); + }); + + const String myTopic = 'Flutter'; + + test('subscribe to topic', () { + firebaseMessaging.subscribeToTopic(myTopic); + verify(mockChannel.invokeMethod('subscribeToTopic', myTopic)); + }); + + test('unsubscribe from topic', () { + firebaseMessaging.unsubscribeFromTopic(myTopic); + verify(mockChannel.invokeMethod('unsubscribeFromTopic', myTopic)); + }); +} + +class MockMethodChannel extends Mock implements MethodChannel {}