Skip to content

Commit

Permalink
Push to Start Live Activities (#738)
Browse files Browse the repository at this point in the history
  • Loading branch information
brismithers authored Jun 4, 2024
1 parent 3a7988d commit 4b4951b
Show file tree
Hide file tree
Showing 41 changed files with 2,785 additions and 86 deletions.
8 changes: 8 additions & 0 deletions OneSignalExample/Assets/App.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions OneSignalExample/Assets/App/Editor.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions OneSignalExample/Assets/App/Editor/iOS.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

169 changes: 169 additions & 0 deletions OneSignalExample/Assets/App/Editor/iOS/BuildPostProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* Modified MIT License
*
* Copyright 2023 OneSignal
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* 1. The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 2. All copies of substantial portions of the Software may only be used in connection
* with services provided by OneSignal.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#if UNITY_IOS

using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEngine;
using UnityEditor.iOS.Xcode;
using UnityEditor.iOS.Xcode.Extensions;
using System.IO;
using System.Linq;

namespace App.Editor.iOS {
/// <summary>
/// Adds the ExampleWidgetExtension to the iOS project frameworks to the iOS project and enables the main target
/// for Live Activities.
/// </summary>
public class BuildPostProcessor : IPostprocessBuildWithReport {

private static readonly string WdigetExtensionTargetRelativePath = "ExampleWidget";
private static readonly string WidgetExtensionTargetName = "ExampleWidgetExtension";
private static readonly string WidgetExtensionPath = Path.Combine("iOS", "ExampleWidget");
private static readonly string[] WidgetExtensionFiles = new string[] { "Assets.xcassets", "ExampleWidgetBundle.swift", "ExampleWidgetLiveActivity.swift" };

/// <summary>
/// must be between 40 and 50 to ensure that it's not overriden by Podfile generation (40) and that it's
/// added before "pod install" (50)
/// https://github.com/googlesamples/unity-jar-resolver#appending-text-to-generated-podfile
/// </summary>
public int callbackOrder => 45;

public void OnPostprocessBuild(BuildReport report) {
if (report.summary.platform != BuildTarget.iOS)
return;

Debug.Log("BuildPostProcessor.OnPostprocessBuild for target " + report.summary.platform + " at path " + report.summary.outputPath + " with CWD " + Directory.GetCurrentDirectory());

EnableAppForLiveActivities(report.summary.outputPath);
CreateWidgetExtension(report.summary.outputPath);

Debug.Log("BuildPostProcessor.OnPostprocessBuild complete");
}

static void EnableAppForLiveActivities(string outputPath) {
var plistPath = Path.Combine(outputPath, "Info.plist");

if (!File.Exists(plistPath)) {
Debug.LogError($"Could not find PList {plistPath}!");
return;
}

var mainPlist = new PlistDocument();
mainPlist.ReadFromFile(plistPath);
mainPlist.root.SetBoolean("NSSupportsLiveActivities", true);
mainPlist.WriteToFile(plistPath);
}

static void CreateWidgetExtension(string outputPath) {
AddWidgetExtensionToProject(outputPath);
AddWidgetExtensionToPodFile(outputPath);
}

static void AddWidgetExtensionToProject(string outputPath) {
var project = new PBXProject();
var projectPath = PBXProject.GetPBXProjectPath(outputPath);
project.ReadFromString(File.ReadAllText(projectPath));

var extensionGuid = project.TargetGuidByName(WidgetExtensionTargetName);

// skip target setup if already present
if (!string.IsNullOrEmpty(extensionGuid))
return;

var widgetDestPath = Path.Combine(outputPath, WdigetExtensionTargetRelativePath);

Directory.CreateDirectory(widgetDestPath);
CopyFileOrDirectory(Path.Combine(WidgetExtensionPath, "Info.plist"), Path.Combine(widgetDestPath, "Info.plist"));

extensionGuid = project.AddAppExtension(project.GetUnityMainTargetGuid(),
WidgetExtensionTargetName,
$"{PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.iOS)}.{WidgetExtensionTargetName}",
$"{WdigetExtensionTargetRelativePath}/Info.plist"
);

var buildPhaseID = project.AddSourcesBuildPhase(extensionGuid);

foreach (var file in WidgetExtensionFiles) {
var destPathRelative = Path.Combine(WdigetExtensionTargetRelativePath, file);
var sourceFileGuid = project.AddFile(destPathRelative, destPathRelative);
project.AddFileToBuildSection(extensionGuid, buildPhaseID, sourceFileGuid);
CopyFileOrDirectory(Path.Combine(WidgetExtensionPath, file), Path.Combine(outputPath, destPathRelative));
}

project.SetBuildProperty(extensionGuid, "TARGETED_DEVICE_FAMILY", "1,2");
project.SetBuildProperty(extensionGuid, "IPHONEOS_DEPLOYMENT_TARGET", "17.0");
project.SetBuildProperty(extensionGuid, "SWIFT_VERSION", "5.0");

project.WriteToFile(projectPath);
}

static void AddWidgetExtensionToPodFile(string outputPath) {
var podfilePath = Path.Combine(outputPath, "Podfile");

if (!File.Exists(podfilePath)) {
Debug.LogError($"Could not find Podfile {podfilePath}!");
return;
}

var podfile = File.ReadAllText(podfilePath);
podfile += $"target '{WidgetExtensionTargetName}' do\n pod 'OneSignalXCFramework', '>= 5.0.2', '< 6.0.0'\nend\n";
File.WriteAllText(podfilePath, podfile);
}

static void CopyFileOrDirectory(string sourcePath, string destinationPath)
{
var file = new FileInfo(sourcePath);
var dir = new DirectoryInfo(sourcePath);

if (!file.Exists && !dir.Exists)
{
return;
}

if (file.Exists)
{
file.CopyTo(destinationPath, true);
}
else {
Directory.CreateDirectory(destinationPath);

foreach (FileInfo childFile in dir.EnumerateFiles().Where(f => !f.Name.EndsWith(".meta")))
{
CopyFileOrDirectory(childFile.FullName, Path.Combine(destinationPath, childFile.Name));
}

foreach (DirectoryInfo subDir in dir.GetDirectories())
{
CopyFileOrDirectory(subDir.FullName, Path.Combine(destinationPath, subDir.Name));
}
}
}
}
}
#endif
11 changes: 11 additions & 0 deletions OneSignalExample/Assets/App/Editor/iOS/BuildPostProcessor.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -128,20 +128,33 @@ public class OneSignalExampleBehaviour : MonoBehaviour {
/// <summary>
///
/// </summary>
public string liveActivityToken;
public string liveActivityIdToken;

/// <summary>
///
/// </summary>
public string liveActivityType;

/// <summary>
///
/// </summary>
public string liveActivityTypeToken;

/// <summary>
/// we recommend initializing OneSignal early in your application's lifecycle such as in the Start method of a
/// MonoBehaviour in your opening Scene
/// </summary>
private void Start() {
// Enable lines below to debug issues with OneSignal
OneSignal.Debug.LogLevel = LogLevel.Info;
OneSignal.Debug.LogLevel = LogLevel.Verbose;
OneSignal.Debug.AlertLevel = LogLevel.Fatal;

_log($"Initializing with appId <b>{appId}</b>");
OneSignal.Initialize(appId);

// Setup the default live activity
OneSignal.LiveActivities.SetupDefault();

// Setting ConsentRequired to true will prevent the OneSignalSDK from operating until
// PrivacyConsent is also set to true
OneSignal.ConsentRequired = consentRequired;
Expand Down Expand Up @@ -437,10 +450,28 @@ public void ToggleShareLocation() {
* iOS
*/

public void StartDefaultLiveActivity() {
_log($"Start Default Live Activity with id: <b>{liveActivityId}</b>...");

OneSignal.LiveActivities.StartDefault(
liveActivityId,
new Dictionary<string, object>() {
{ "title", "Welcome!" }
},
new Dictionary<string, object>() {
{ "message", new Dictionary<string, object>() {
{ "en", "Hello World!"}
}},
{ "intValue", 3 },
{ "doubleValue", 3.14 },
{ "boolValue", true }
});
}

public async void EnterLiveActivityAsync() {
_log($"Entering Live Activity with id: <b>{liveActivityId}</b> and token: <b>{liveActivityToken}</b> and awaiting result...");
_log($"Entering Live Activity with id: <b>{liveActivityId}</b> and token: <b>{liveActivityIdToken}</b> and awaiting result...");

var result = await OneSignal.LiveActivities.EnterAsync(liveActivityId, liveActivityToken);
var result = await OneSignal.LiveActivities.EnterAsync(liveActivityId, liveActivityIdToken);

if (result)
_log("Live Activity enter success");
Expand All @@ -459,6 +490,18 @@ public async void ExitLiveActivityAsync() {
_log("Live Activity exit failed");
}

public void SetPushToStartToken() {
_log($"Set Push To Start Token for type: <b>{liveActivityType}</b> with token: <b>{liveActivityTypeToken}</b>...");

OneSignal.LiveActivities.SetPushToStartToken(liveActivityType, liveActivityTypeToken);
}

public void RemovePushToStartToken() {
_log($"Remove Push To Start Token for type: <b>{liveActivityType}</b>...");

OneSignal.LiveActivities.RemovePushToStartToken(liveActivityType);
}

#region Rendering
/*
* You can safely ignore everything in this region and below
Expand Down Expand Up @@ -487,7 +530,10 @@ public async void ExitLiveActivityAsync() {
public void SetOutcomeValue(string newVal) => outcomeValue = Convert.ToSingle(newVal);

public void SetLiveActivityId(string newVal) => liveActivityId = newVal;
public void SetLiveActivityToken(string newVal) => liveActivityToken = newVal;
public void SetLiveActivityIdToken(string newVal) => liveActivityIdToken = newVal;

public void SetLiveActivityType(string newVal) => liveActivityType = newVal;
public void SetLiveActivityTypeToken(string newVal) => liveActivityTypeToken = newVal;

private void Awake() {
SDKDebug.LogIntercept += _log;
Expand Down
Loading

0 comments on commit 4b4951b

Please sign in to comment.