Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
## [3.0.0-pre.1] - 2022-12-16

### Fixed
Fixed a bug preventing the `notificationOpened` event from being sent after opening a notification while the app was fully closed

### Changes

- The following classes have been changed to `internal`:
  - `InsertPushNotificationDependenciesIntoGradleScript`
  - `IOSRichPushNotificationPostProcess`
  - `IOSBasicPushNotificationPostProcess`
  - `PushSettingsProvider`
  - `PushNotificationEditorGameService`
  - `PushNotificationAnalytics`
- Moved `IPushNotificationsAnalytics` to it's own file.
- Change `PushNotificationsService` to use the `IPushNotificationsAnalytics` interface instead of
directly using the `PushNotificationsAnalytics` class.
- Bumped the Analytics SDK version from `4.0.0-pre.2` to `4.3.0`
- Bumped the Core Package SDK version from `1.2.0` to `1.4.0`
- Dashboard project link now available in settings panel
- Renamed the following fields in `PushNotificationSettings` (Values will be retained):
  - `androidApiKey` -> `firebaseWebApiKey`
  - `androidSenderId` -> `firebaseProjectNumber`
  - `androidApplicationId` -> `firebaseAppID`
  - `androidProjectId` -> `firebaseProjectID`

### Removed
- Removed the deprecated `PushNotifications` class.
- Removed obsolete `PushNotificationSettings` properties.

### Known Issues
- Notifications received while the app is opened will automatically trigger the `notificationOpened` event even before they are opened
- `didLaunch` property sent with `notificationOpened` event not fully accurate
- Devices running Android 13 will not receive notifications until the player grants permission. Permission won’t be requested until the device receives a notification for the first time.
  • Loading branch information
Unity Technologies committed Dec 16, 2022
1 parent 72d9171 commit 18b1637
Show file tree
Hide file tree
Showing 43 changed files with 359 additions and 679 deletions.
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,41 @@ All notable changes to this package will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [3.0.0-pre.1] - 2022-12-16

### Fixed
Fixed a bug preventing the `notificationOpened` event from being sent after opening a notification while the app was fully closed

### Changes

- The following classes have been changed to `internal`:
- `InsertPushNotificationDependenciesIntoGradleScript`
- `IOSRichPushNotificationPostProcess`
- `IOSBasicPushNotificationPostProcess`
- `PushSettingsProvider`
- `PushNotificationEditorGameService`
- `PushNotificationAnalytics`
- Moved `IPushNotificationsAnalytics` to it's own file.
- Change `PushNotificationsService` to use the `IPushNotificationsAnalytics` interface instead of
directly using the `PushNotificationsAnalytics` class.
- Bumped the Analytics SDK version from `4.0.0-pre.2` to `4.3.0`
- Bumped the Core Package SDK version from `1.2.0` to `1.4.0`
- Dashboard project link now available in settings panel
- Renamed the following fields in `PushNotificationSettings` (Values will be retained):
- `androidApiKey` -> `firebaseWebApiKey`
- `androidSenderId` -> `firebaseProjectNumber`
- `androidApplicationId` -> `firebaseAppID`
- `androidProjectId` -> `firebaseProjectID`

### Removed
- Removed the deprecated `PushNotifications` class.
- Removed obsolete `PushNotificationSettings` properties.

### Known Issues
- Notifications received while the app is opened will automatically trigger the `notificationOpened` event even before they are opened
- `didLaunch` property sent with `notificationOpened` event not fully accurate
- Devices running Android 13 will not receive notifications until the player grants permission. Permission won’t be requested until the device receives a notification for the first time.

## [2.0.0-pre.2] - 2022-05-09

### Fixed
Expand Down
78 changes: 1 addition & 77 deletions Documentation~/index.md
Original file line number Diff line number Diff line change
@@ -1,78 +1,2 @@
# Push Notifications SDK documentation

The Push Notifications SDK allows you to send push notification campaigns, including rich push notifications with images to your users.

## Platform support

This SDK supports both iOS and Android. iOS 10+ and Android SDK >= 26 (Oreo) are supported.

## Quick Start

The SDK comes with a sample script that will register for push notifications. Add this to your project and set the relevant settings in the editor under Project Settings.

## Registering for Push Notifications

To register for push notifications, three steps are required.

First, you need to populate some settings in the editor. These can be found in Project Settings > Services > Push Notifications.

Next, you need to initialise Unity Services, so that the required analytics events can be sent to Unity Analytics. You also need to implement the privacy flow, as detailed in the Analytics documentation, for the required events to be sent correctly.

You can then register for notifications. Ideally, to ensure no notifications are missed, this should be done in the startup code for your game. However, note that on first registration on iOS a user will be shown a permission request, so also ensure this call is made at a convenient place in your game. The SDK will handle the showing of notification content, including images, titles and message body.

A full code sample is shown below.

```cs
await UnityServices.InitializeAsync();
// Note: This is the minimum required in Analytics version 3.0.0 and above to ensure the events with the push notification data are sent correctly.
// In a real game you would need to handle privacy consent states here, see the Analytics documentation for more details.
await AnalyticsService.Instance.CheckForRequiredConsents();

try
{
string pushToken = await PushNotificationService.Instance.RegisterForPushNotificationsAsync();

PushNotificationService.Instance.OnNotificationReceived += notificationData =>
{
Debug.Log("Received a notification!");
};
}
catch (Exception e)
{
Debug.Log("Failed to retrieve a push notification token.");
}
```

### Notification received callbacks

You can register a delegate to receive a C# event callback when a notification is received, if you wish to perform custom behavior at that point. To do this, add a delegate / method callback to `PushNotifications.OnNotificationReceived` as shown in the sample above.

### Push Notification settings

The SDK requires a number of settings in order to function correctly. Some settings are only used on a certain platform, indicated in the setting name. The following settings can be set:

* AndroidApiKey: The API key for a Firebase project to be used for Android's Firebase Cloud Messaging API. This can be found in your Firebase dashboard.
* AndroidSenderId: The sender ID to be used for Android's Firebase Cloud Messaging. This can be found in your Firebase dashboard.
* AndroidApplicationId: The application ID for a Firebase application to be used for Android's Firebase Cloud Messaging API. This can be found in your Firebase dashboard.
* AndroidProjectId: The project ID for a Firebase project to be used for Android's Firebase Cloud Messaging API. This can be found in your Firebase dashboard.

These settings are set in Project Settings -> Services -> Push Notifications.

### Analytics

The SDK will record two analytics events:

* `notificationServices`: This event is recorded whenever a new token is registered on the client. It contains the push token and is used to register this token with the backend service. This allows you to send notifications from the Unity Dashboard, and is required for proper functionality of this product.
* `notificationOpened`: This event is recorded whenever a notification is opened by a user. It contains data regarding which campaign and cohort the user was in, and whether the app was launched from the notification.

#### Analytics only mode

*WARNING: This section is only applicable if you're trying to use the Unity Dashboard Push Notification service alongside a separate Push Notification implementation. For most users this section is not required or recommended as it'll lead to reduced functionality of the product*

It's possible to integrate the SDK with an existing push notification service if required. To do so, do not call the registration methods as indicated above, and instead use the two methods in `PushNotificationService.Instance.Analytics` alongside your existing implementation.

`RecordPushTokenUpdated` should be called when you receive a new push token for a device. Note that the OS may create a new token at multiple points in the app's lifecycle, so call this whenever the token changes, and not just at startup.

`RecordNotificationOpened` should be called when a notification is opened. It takes a dictionary that is the data the notification payload contained, and a boolean flag to indicate whether the app was launched from the notification or not.

Note that this should allow you to send and schedule notifications from the Unity Dashboard. However, it greatly depends on the other push notification implementation you have implemented, and may lead to missing images or other content in notifications, so it's strongly recommended to use the standard set up, with this SDK being the only Push Notification solution integrated, if possible.
Please consult the full manual on https://docs.unity.com/push-notifications/
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace Unity.Services.PushNotifications.Editor
{
public class InsertPushNotificationDependenciesIntoGradleScript : IPostGenerateGradleAndroidProject
internal class InsertPushNotificationDependenciesIntoGradleScript : IPostGenerateGradleAndroidProject
{
public int callbackOrder => 0;

Expand Down
15 changes: 13 additions & 2 deletions Editor/Settings/PushNotificationEditorGameService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Unity.Services.Core.Editor;
using Unity.Services.Core.Editor.OrganizationHandler;
using UnityEditor;

namespace Unity.Services.PushNotifications.Editor
Expand All @@ -8,13 +9,23 @@ public struct PushNotificationIdentifier : IEditorGameServiceIdentifier
public string GetKey() => "Push Notifications";
}

public class PushNotificationEditorGameService : IEditorGameService
internal class PushNotificationEditorGameService : IEditorGameService
{
static readonly PushNotificationIdentifier k_Identifier = new PushNotificationIdentifier();

public string GetFormattedDashboardUrl()
{
return null;
string organizationId = OrganizationProvider.Organization.Key;
string projectId = CloudProjectSettings.projectId;

bool isProjectConfigured = !string.IsNullOrEmpty(organizationId) && !string.IsNullOrEmpty(projectId);

if (isProjectConfigured)
{
return $"https://dashboard.unity3d.com/organizations/{organizationId}/projects/{projectId}/environments/default/campaigns/push/overview";
}

return "https://dashboard.unity3d.com";
}

public string Name => "Push Notifications";
Expand Down
37 changes: 31 additions & 6 deletions Editor/Settings/PushSettingsProvider.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
using System.Collections.Generic;
using Unity.Services.Core.Editor;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

namespace Unity.Services.PushNotifications.Editor
{
public class PushSettingsProvider : EditorGameServiceSettingsProvider
internal class PushSettingsProvider : EditorGameServiceSettingsProvider
{
const string k_Title = "Push Notifications";
const string k_GoToDashboardContainer = "dashboard-button-container";
const string k_GoToDashboardBtn = "dashboard-link-button";


protected override IEditorGameService EditorGameService => k_GameService;
protected override string Title => k_Title;
Expand All @@ -24,20 +28,26 @@ protected override VisualElement GenerateServiceDetailUI()

VisualElement containerVisualElement = new VisualElement();

Label headerLabel = new Label("Android");
Label headerLabel = new Label("Android (Firebase) Settings");
headerLabel.style.fontSize = 14;
headerLabel.style.unityFontStyleAndWeight = UnityEngine.FontStyle.Bold;
headerLabel.style.marginLeft = 4;
containerVisualElement.Add(headerLabel);

CreateFormRow("API Key", nameof(PushNotificationSettings.androidApiKey), serializedSettings, containerVisualElement);
CreateFormRow("Sender ID", nameof(PushNotificationSettings.androidSenderId), serializedSettings, containerVisualElement);
CreateFormRow("Application ID", nameof(PushNotificationSettings.androidApplicationId), serializedSettings, containerVisualElement);
CreateFormRow("Project ID", nameof(PushNotificationSettings.androidProjectId), serializedSettings, containerVisualElement);
CreateFormRow("Firebase Web API Key", nameof(PushNotificationSettings.firebaseWebApiKey), serializedSettings, containerVisualElement);
CreateFormRow("Firebase Project Number", nameof(PushNotificationSettings.firebaseProjectNumber), serializedSettings, containerVisualElement);
CreateFormRow("Firebase App ID", nameof(PushNotificationSettings.firebaseAppID), serializedSettings, containerVisualElement);
CreateFormRow("Firebase Project ID", nameof(PushNotificationSettings.firebaseProjectID), serializedSettings, containerVisualElement);

return containerVisualElement;
}

public override void OnActivate(string searchContext, VisualElement rootElement)
{
base.OnActivate(searchContext, rootElement);
SetDashboardButton(rootElement);
}

protected override VisualElement GenerateUnsupportedDetailUI()
{
return GenerateServiceDetailUI();
Expand Down Expand Up @@ -77,6 +87,21 @@ static void CreateFormRow(string title, string fieldName, SerializedObject setti
container.Add(rowContainer);
}

static void SetDashboardButton(VisualElement rootElement)
{
rootElement.Q(k_GoToDashboardContainer).style.display = DisplayStyle.Flex;
var goToDashboard = rootElement.Q(k_GoToDashboardBtn);

if (goToDashboard != null)
{
var clickable = new Clickable(() =>
{
Application.OpenURL(k_GameService.GetFormattedDashboardUrl());
});
goToDashboard.AddManipulator(clickable);
}
}

static SerializedObject GetSerializedSettings()
{
PushNotificationSettings cfg = AssetDatabase.LoadAssetAtPath<PushNotificationSettings>(PushNotificationSettings.fullAssetPath);
Expand Down
30 changes: 1 addition & 29 deletions Editor/Unity.Services.PushNotifications.Editor.api
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,8 @@
// make sure the XML doc file is present and located next to the scraped dll
namespace Unity.Services.PushNotifications.Editor
{
public class InsertPushNotificationDependenciesIntoGradleScript : UnityEditor.Android.IPostGenerateGradleAndroidProject, UnityEditor.Build.IOrderedCallback
{
public virtual int callbackOrder { get; }
public InsertPushNotificationDependenciesIntoGradleScript() {}
public virtual void OnPostGenerateGradleAndroidProject(string path);
}

public class PushNotificationEditorGameService : Unity.Services.Core.Editor.IEditorGameService
{
public virtual Unity.Services.Core.Editor.IEditorGameServiceEnabler Enabler { get; }
public virtual bool HasDashboard { get; }
public virtual Unity.Services.Core.Editor.IEditorGameServiceIdentifier Identifier { get; }
public virtual string Name { get; }
public virtual bool RequiresCoppaCompliance { get; }
public PushNotificationEditorGameService() {}
public virtual string GetFormattedDashboardUrl();
}

public struct PushNotificationIdentifier : Unity.Services.Core.Editor.IEditorGameServiceIdentifier
{
public virtual string GetKey();
}

public class PushSettingsProvider : Unity.Services.Core.Editor.EditorGameServiceSettingsProvider
{
protected virtual string Description { get; }
protected virtual Unity.Services.Core.Editor.IEditorGameService EditorGameService { get; }
protected virtual string Title { get; }
[UnityEditor.SettingsProvider] public static UnityEditor.SettingsProvider CreateSettingsProvider();
protected virtual UnityEngine.UIElements.VisualElement GenerateServiceDetailUI();
protected virtual UnityEngine.UIElements.VisualElement GenerateUnsupportedDetailUI();
public string GetKey();
}
}
18 changes: 9 additions & 9 deletions Editor/iOS/IOSBasicPushNotificationPostProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace Unity.Services.PushNotifications.Editor
{
public class IOSBasicPushNotificationPostProcess : MonoBehaviour
internal class IOSBasicPushNotificationPostProcess : MonoBehaviour
{
[PostProcessBuild(0)]
public static void OnPostProcessBuild(BuildTarget buildTarget, String path)
Expand All @@ -29,7 +29,7 @@ public static void OnPostProcessBuild(BuildTarget buildTarget, String path)
AddCapabilities(project, projectPath, mainTargetGuid, path);
UpdateInfoPlist(path);
UpdatePreprocessorFile(path);

project.WriteToFile(projectPath);
}

Expand All @@ -40,19 +40,19 @@ static void AddCapabilities(PBXProject project, string projectPath, string mainT
{
string bundleIdentifier = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.iOS);
entitlementsFileName = $"{bundleIdentifier.Substring(bundleIdentifier.LastIndexOf(".") + 1)}.entitlements";

// Add the entitlements file to the build
project.AddFile(Path.Combine(buildPath, entitlementsFileName), entitlementsFileName);
project.AddBuildProperty(mainTargetGuid, "CODE_SIGN_ENTITLEMENTS", entitlementsFileName);
}

var capManager = new ProjectCapabilityManager(projectPath, entitlementsFileName, "Unity-iPhone");

// TODO: Do we want to allow the user to specify which push environment they want to use, or should we always assume
// TODO: Do we want to allow the user to specify which push environment they want to use, or should we always assume
// live? If so, the below will need updating with a new setting.
const bool useDevEnvironment = false;
const bool useDevEnvironment = false;
capManager.AddPushNotifications(useDevEnvironment);

capManager.WriteToFile();
}

Expand All @@ -65,10 +65,10 @@ static void UpdateInfoPlist(string projectPath)
string plistPath = projectPath + "/Info.plist";
PlistDocument plist = new PlistDocument();
plist.ReadFromFile(plistPath);
PlistElementArray existingBackgroundModes = (PlistElementArray) plist.root["UIBackgroundModes"] ?? plist.root.CreateArray("UIBackgroundModes");

PlistElementArray existingBackgroundModes = (PlistElementArray)plist.root["UIBackgroundModes"] ?? plist.root.CreateArray("UIBackgroundModes");
existingBackgroundModes.AddString("remote-notification");

plist.WriteToFile(plistPath);
}

Expand Down
Loading

0 comments on commit 18b1637

Please sign in to comment.