Compare commits

...

14 Commits

Author SHA1 Message Date
Savya Bikram Shah
705d6b4825 fixes 2026-06-01 16:52:00 +05:45
Savya Bikram Shah
89011184d5 removed logrocket and fixed Drawing UX 2026-06-01 16:04:27 +05:45
Savya Bikram Shah
28810235a5 Logrocket fixes 2026-06-01 15:47:14 +05:45
Savya Bikram Shah
f9826325c6 Ad fixes and appsflyer 2026-06-01 15:01:15 +05:45
Savya Bikram Shah
140a252350 Merge remote-tracking branch 'origin/work_branch' into savya
# Conflicts:
#	Assets/Darkmatter/Scenes/Colorbook.unity
2026-06-01 14:10:04 +05:45
Savya Bikram Shah
82386cae08 fixes 2026-06-01 14:09:51 +05:45
Mausham
0c39c1a229 some fixes 2026-06-01 14:09:33 +05:45
Savya Bikram Shah
e821d4e65e background fixes 2026-06-01 14:06:28 +05:45
Mausham
d700760524 some additional fixes 2026-06-01 14:00:54 +05:45
Savya Bikram Shah
0bf6f64548 analyitcs 2026-06-01 13:38:28 +05:45
Mausham
ac0f3a458a added 5 more drawing 2026-06-01 13:29:05 +05:45
Savya Bikram Shah
6f79bcd018 fixes, improvements. firebase 2026-06-01 13:15:40 +05:45
Savya Bikram Shah
384176fdcc fxies 2026-06-01 12:22:47 +05:45
Savya Bikram Shah
5c7a9f38a9 Responsiveness 2026-06-01 11:36:41 +05:45
516 changed files with 47239 additions and 6688 deletions

View File

@@ -15,7 +15,7 @@ MonoBehaviour:
m_DefaultGroup: 0e030d5498bfe4ffd8443c796618c539
m_currentHash:
serializedVersion: 2
Hash: fdf5dbef4b3bdd1999753be21e456785
Hash: 00000000000000000000000000000000
m_OptimizeCatalogSize: 0
m_BuildRemoteCatalog: 0
m_CatalogRequestsTimeout: 0

View File

@@ -21,6 +21,12 @@ MonoBehaviour:
m_SerializedLabels:
- drawing
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 14477b5d35d0be9439ecb935f1c4e64e
m_Address: Donut
m_ReadOnly: 0
m_SerializedLabels:
- drawing
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 2043f692673a79543afed5cb879f0e04
m_Address: Five
m_ReadOnly: 0
@@ -39,6 +45,24 @@ MonoBehaviour:
m_SerializedLabels:
- drawing
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 4474b93704045a740ba9b8114e8a5238
m_Address: Fish
m_ReadOnly: 0
m_SerializedLabels:
- drawing
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 62ae112e11b695a40b889d773a36f8bd
m_Address: Elephant
m_ReadOnly: 0
m_SerializedLabels:
- drawing
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 6e36ba5d4763c694289c8ca75ee81449
m_Address: Bus
m_ReadOnly: 0
m_SerializedLabels:
- drawing
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 977dc7dac5ee6b543b8ed47c2299919e
m_Address: Airplane
m_ReadOnly: 0
@@ -63,6 +87,12 @@ MonoBehaviour:
m_SerializedLabels:
- drawing
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: f3200c6715fea4a41bc71c0314b519cd
m_Address: Frog
m_ReadOnly: 0
m_SerializedLabels:
- drawing
FlaggedDuringContentUpdateRestriction: 0
m_ReadOnly: 0
m_Settings: {fileID: 11400000, guid: 4a94ef317c3674edd8270e4ed15031f6, type: 2}
m_SchemaSet:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 684ad60441f714a338ca786efcac33b6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Assets/AppsFlyer.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7863556d88b814e09ba9cfc75a91d655
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
namespace AppsFlyerSDK
{
public enum MediationNetwork : ulong
{
GoogleAdMob = 1,
IronSource = 2,
ApplovinMax = 3,
Fyber = 4,
Appodeal = 5,
Admost = 6,
Topon = 7,
Tradplus = 8,
Yandex = 9,
ChartBoost = 10,
Unity = 11,
ToponPte = 12,
Custom = 13,
DirectMonetization = 14
}
public static class AdRevenueScheme
{
/**
* code ISO 3166-1 format
*/
public const string COUNTRY = "country";
/**
* ID of the ad unit for the impression
*/
public const string AD_UNIT = "ad_unit";
/**
* Format of the ad
*/
public const string AD_TYPE = "ad_type";
/**
* ID of the ad placement for the impression
*/
public const string PLACEMENT = "placement";
}
/// <summary>
// Data class representing ad revenue information.
//
// @property monetizationNetwork The name of the network that monetized the ad.
// @property mediationNetwork An instance of MediationNetwork representing the mediation service used.
// @property currencyIso4217Code The ISO 4217 currency code describing the currency of the revenue.
// @property eventRevenue The amount of revenue generated by the ad.
/// </summary>
public class AFAdRevenueData
{
public string monetizationNetwork { get; private set; }
public MediationNetwork mediationNetwork { get; private set; }
public string currencyIso4217Code { get; private set; }
public double eventRevenue { get; private set; }
public AFAdRevenueData(string monetization, MediationNetwork mediation, string currency, double revenue)
{
monetizationNetwork = monetization;
mediationNetwork = mediation;
currencyIso4217Code = currency;
eventRevenue = revenue;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 49e1906ae949e4bfea400bd1da9f7e39
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,72 @@
using UnityEngine;
using System.Collections;
public class AFInAppEvents {
/**
* Event Type
* */
public const string LEVEL_ACHIEVED = "af_level_achieved";
public const string ADD_PAYMENT_INFO = "af_add_payment_info";
public const string ADD_TO_CART = "af_add_to_cart";
public const string ADD_TO_WISH_LIST = "af_add_to_wishlist";
public const string COMPLETE_REGISTRATION = "af_complete_registration";
public const string TUTORIAL_COMPLETION = "af_tutorial_completion";
public const string INITIATED_CHECKOUT = "af_initiated_checkout";
public const string PURCHASE = "af_purchase";
public const string RATE = "af_rate";
public const string SEARCH = "af_search";
public const string SPENT_CREDIT = "af_spent_credits";
public const string ACHIEVEMENT_UNLOCKED = "af_achievement_unlocked";
public const string CONTENT_VIEW = "af_content_view";
public const string TRAVEL_BOOKING = "af_travel_booking";
public const string SHARE = "af_share";
public const string INVITE = "af_invite";
public const string LOGIN = "af_login";
public const string RE_ENGAGE = "af_re_engage";
public const string UPDATE = "af_update";
public const string OPENED_FROM_PUSH_NOTIFICATION = "af_opened_from_push_notification";
public const string LOCATION_CHANGED = "af_location_changed";
public const string LOCATION_COORDINATES = "af_location_coordinates";
public const string ORDER_ID = "af_order_id";
/**
* Event Parameter Name
* **/
public const string LEVEL = "af_level";
public const string SCORE = "af_score";
public const string SUCCESS = "af_success";
public const string PRICE = "af_price";
public const string CONTENT_TYPE = "af_content_type";
public const string CONTENT_ID = "af_content_id";
public const string CONTENT_LIST = "af_content_list";
public const string CURRENCY = "af_currency";
public const string QUANTITY = "af_quantity";
public const string REGSITRATION_METHOD = "af_registration_method";
public const string PAYMENT_INFO_AVAILIBLE = "af_payment_info_available";
public const string MAX_RATING_VALUE = "af_max_rating_value";
public const string RATING_VALUE = "af_rating_value";
public const string SEARCH_STRING = "af_search_string";
public const string DATE_A = "af_date_a";
public const string DATE_B = "af_date_b";
public const string DESTINATION_A = "af_destination_a";
public const string DESTINATION_B = "af_destination_b";
public const string DESCRIPTION = "af_description";
public const string CLASS = "af_class";
public const string EVENT_START = "af_event_start";
public const string EVENT_END = "af_event_end";
public const string LATITUDE = "af_lat";
public const string LONGTITUDE = "af_long";
public const string CUSTOMER_USER_ID = "af_customer_user_id";
public const string VALIDATED = "af_validated";
public const string REVENUE = "af_revenue";
public const string RECEIPT_ID = "af_receipt_id";
public const string PARAM_1 = "af_param_1";
public const string PARAM_2 = "af_param_2";
public const string PARAM_3 = "af_param_3";
public const string PARAM_4 = "af_param_4";
public const string PARAM_5 = "af_param_5";
public const string PARAM_6 = "af_param_6";
public const string PARAM_7 = "af_param_7";
public const string PARAM_8 = "af_param_8";
public const string PARAM_9 = "af_param_9";
public const string PARAM_10 = "af_param_10";
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4075c6cf6f3d94b9a9f37f826e6a0e6f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

547
Assets/AppsFlyer/AFMiniJSON.cs Executable file
View File

@@ -0,0 +1,547 @@
/*
* Copyright (c) 2013 Calvin Rien
*
* Based on the JSON parser by Patrick van Bergen
* http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html
*
* Simplified it so that it doesn't throw exceptions
* and can be used in Unity iPhone with maximum code stripping.
*
* 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:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 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.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace AFMiniJSON {
// Example usage:
//
// using UnityEngine;
// using System.Collections;
// using System.Collections.Generic;
// using MiniJSON;
//
// public class MiniJSONTest : MonoBehaviour {
// void Start () {
// var jsonString = "{ \"array\": [1.44,2,3], " +
// "\"object\": {\"key1\":\"value1\", \"key2\":256}, " +
// "\"string\": \"The quick brown fox \\\"jumps\\\" over the lazy dog \", " +
// "\"unicode\": \"\\u3041 Men\u00fa sesi\u00f3n\", " +
// "\"int\": 65536, " +
// "\"float\": 3.1415926, " +
// "\"bool\": true, " +
// "\"null\": null }";
//
// var dict = Json.Deserialize(jsonString) as Dictionary<string,object>;
//
// Debug.Log("deserialized: " + dict.GetType());
// Debug.Log("dict['array'][0]: " + ((List<object>) dict["array"])[0]);
// Debug.Log("dict['string']: " + (string) dict["string"]);
// Debug.Log("dict['float']: " + (double) dict["float"]); // floats come out as doubles
// Debug.Log("dict['int']: " + (long) dict["int"]); // ints come out as longs
// Debug.Log("dict['unicode']: " + (string) dict["unicode"]);
//
// var str = Json.Serialize(dict);
//
// Debug.Log("serialized: " + str);
// }
// }
/// <summary>
/// This class encodes and decodes JSON strings.
/// Spec. details, see http://www.json.org/
///
/// JSON uses Arrays and Objects. These correspond here to the datatypes IList and IDictionary.
/// All numbers are parsed to doubles.
/// </summary>
public static class Json {
/// <summary>
/// Parses the string json into a value
/// </summary>
/// <param name="json">A JSON string.</param>
/// <returns>An List&lt;object&gt;, a Dictionary&lt;string, object&gt;, a double, an integer,a string, null, true, or false</returns>
public static object Deserialize(string json) {
// save the string for debug information
if (json == null) {
return null;
}
return Parser.Parse(json);
}
sealed class Parser : IDisposable {
const string WORD_BREAK = "{}[],:\"";
public static bool IsWordBreak(char c) {
return Char.IsWhiteSpace(c) || WORD_BREAK.IndexOf(c) != -1;
}
enum TOKEN {
NONE,
CURLY_OPEN,
CURLY_CLOSE,
SQUARED_OPEN,
SQUARED_CLOSE,
COLON,
COMMA,
STRING,
NUMBER,
TRUE,
FALSE,
NULL
};
StringReader json;
Parser(string jsonString) {
json = new StringReader(jsonString);
}
public static object Parse(string jsonString) {
using (var instance = new Parser(jsonString)) {
return instance.ParseValue();
}
}
public void Dispose() {
json.Dispose();
json = null;
}
Dictionary<string, object> ParseObject() {
Dictionary<string, object> table = new Dictionary<string, object>();
// ditch opening brace
json.Read();
// {
while (true) {
switch (NextToken) {
case TOKEN.NONE:
return null;
case TOKEN.COMMA:
continue;
case TOKEN.CURLY_CLOSE:
return table;
default:
// name
string name = ParseString();
if (name == null) {
return null;
}
// :
if (NextToken != TOKEN.COLON) {
return null;
}
// ditch the colon
json.Read();
// value
table[name] = ParseValue();
break;
}
}
}
List<object> ParseArray() {
List<object> array = new List<object>();
// ditch opening bracket
json.Read();
// [
var parsing = true;
while (parsing) {
TOKEN nextToken = NextToken;
switch (nextToken) {
case TOKEN.NONE:
return null;
case TOKEN.COMMA:
continue;
case TOKEN.SQUARED_CLOSE:
parsing = false;
break;
default:
object value = ParseByToken(nextToken);
array.Add(value);
break;
}
}
return array;
}
object ParseValue() {
TOKEN nextToken = NextToken;
return ParseByToken(nextToken);
}
object ParseByToken(TOKEN token) {
switch (token) {
case TOKEN.STRING:
return ParseString();
case TOKEN.NUMBER:
return ParseNumber();
case TOKEN.CURLY_OPEN:
return ParseObject();
case TOKEN.SQUARED_OPEN:
return ParseArray();
case TOKEN.TRUE:
return true;
case TOKEN.FALSE:
return false;
case TOKEN.NULL:
return null;
default:
return null;
}
}
string ParseString() {
StringBuilder s = new StringBuilder();
char c;
// ditch opening quote
json.Read();
bool parsing = true;
while (parsing) {
if (json.Peek() == -1) {
parsing = false;
break;
}
c = NextChar;
switch (c) {
case '"':
parsing = false;
break;
case '\\':
if (json.Peek() == -1) {
parsing = false;
break;
}
c = NextChar;
switch (c) {
case '"':
case '\\':
case '/':
s.Append(c);
break;
case 'b':
s.Append('\b');
break;
case 'f':
s.Append('\f');
break;
case 'n':
s.Append('\n');
break;
case 'r':
s.Append('\r');
break;
case 't':
s.Append('\t');
break;
case 'u':
var hex = new char[4];
for (int i=0; i< 4; i++) {
hex[i] = NextChar;
}
s.Append((char) Convert.ToInt32(new string(hex), 16));
break;
}
break;
default:
s.Append(c);
break;
}
}
return s.ToString();
}
object ParseNumber() {
string number = NextWord;
if (number.IndexOf('.') == -1) {
long parsedInt;
Int64.TryParse(number, out parsedInt);
return parsedInt;
}
double parsedDouble;
Double.TryParse(number, out parsedDouble);
return parsedDouble;
}
void EatWhitespace() {
while (Char.IsWhiteSpace(PeekChar)) {
json.Read();
if (json.Peek() == -1) {
break;
}
}
}
char PeekChar {
get {
return Convert.ToChar(json.Peek());
}
}
char NextChar {
get {
return Convert.ToChar(json.Read());
}
}
string NextWord {
get {
StringBuilder word = new StringBuilder();
while (!IsWordBreak(PeekChar)) {
word.Append(NextChar);
if (json.Peek() == -1) {
break;
}
}
return word.ToString();
}
}
TOKEN NextToken {
get {
EatWhitespace();
if (json.Peek() == -1) {
return TOKEN.NONE;
}
switch (PeekChar) {
case '{':
return TOKEN.CURLY_OPEN;
case '}':
json.Read();
return TOKEN.CURLY_CLOSE;
case '[':
return TOKEN.SQUARED_OPEN;
case ']':
json.Read();
return TOKEN.SQUARED_CLOSE;
case ',':
json.Read();
return TOKEN.COMMA;
case '"':
return TOKEN.STRING;
case ':':
return TOKEN.COLON;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
return TOKEN.NUMBER;
}
switch (NextWord) {
case "false":
return TOKEN.FALSE;
case "true":
return TOKEN.TRUE;
case "null":
return TOKEN.NULL;
}
return TOKEN.NONE;
}
}
}
/// <summary>
/// Converts a IDictionary / IList object or a simple type (string, int, etc.) into a JSON string
/// </summary>
/// <param name="json">A Dictionary&lt;string, object&gt; / List&lt;object&gt;</param>
/// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns>
public static string Serialize(object obj) {
return Serializer.Serialize(obj);
}
sealed class Serializer {
StringBuilder builder;
Serializer() {
builder = new StringBuilder();
}
public static string Serialize(object obj) {
var instance = new Serializer();
instance.SerializeValue(obj);
return instance.builder.ToString();
}
void SerializeValue(object value) {
IList asList;
IDictionary asDict;
string asStr;
if (value == null) {
builder.Append("null");
} else if ((asStr = value as string) != null) {
SerializeString(asStr);
} else if (value is bool) {
builder.Append((bool) value ? "true" : "false");
} else if ((asList = value as IList) != null) {
SerializeArray(asList);
} else if ((asDict = value as IDictionary) != null) {
SerializeObject(asDict);
} else if (value is char) {
SerializeString(new string((char) value, 1));
} else {
SerializeOther(value);
}
}
void SerializeObject(IDictionary obj) {
bool first = true;
builder.Append('{');
foreach (object e in obj.Keys) {
if (!first) {
builder.Append(',');
}
SerializeString(e.ToString());
builder.Append(':');
SerializeValue(obj[e]);
first = false;
}
builder.Append('}');
}
void SerializeArray(IList anArray) {
builder.Append('[');
bool first = true;
foreach (object obj in anArray) {
if (!first) {
builder.Append(',');
}
SerializeValue(obj);
first = false;
}
builder.Append(']');
}
void SerializeString(string str) {
builder.Append('\"');
char[] charArray = str.ToCharArray();
foreach (var c in charArray) {
switch (c) {
case '"':
builder.Append("\\\"");
break;
case '\\':
builder.Append("\\\\");
break;
case '\b':
builder.Append("\\b");
break;
case '\f':
builder.Append("\\f");
break;
case '\n':
builder.Append("\\n");
break;
case '\r':
builder.Append("\\r");
break;
case '\t':
builder.Append("\\t");
break;
default:
int codepoint = Convert.ToInt32(c);
if ((codepoint >= 32) && (codepoint <= 126)) {
builder.Append(c);
} else {
builder.Append("\\u");
builder.Append(codepoint.ToString("x4"));
}
break;
}
}
builder.Append('\"');
}
void SerializeOther(object value) {
// NOTE: decimals lose precision during serialization.
// They always have, I'm just letting you know.
// Previously floats and doubles lost precision too.
if (value is float) {
builder.Append(((float) value).ToString("R"));
} else if (value is int
|| value is uint
|| value is long
|| value is sbyte
|| value is byte
|| value is short
|| value is ushort
|| value is ulong) {
builder.Append(value);
} else if (value is double
|| value is decimal) {
builder.Append(Convert.ToDouble(value).ToString("R"));
} else {
SerializeString(value.ToString());
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bc3d1c806d507463e9b560eb09d8eb0e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
namespace AppsFlyerSDK
{
public enum AFPurchaseType
{
Subscription = 0,
OneTimePurchase = 1
}
/// <summary>
/// Purchase details class matching Android SDK AFPurchaseDetails
/// </summary>
public class AFPurchaseDetailsAndroid
{
public AFPurchaseType purchaseType { get; private set; }
public string purchaseToken { get; private set; }
public string productId { get; private set; }
public AFPurchaseDetailsAndroid(AFPurchaseType type, String purchaseToken, String productId)
{
this.purchaseType = type;
this.purchaseToken = purchaseToken;
this.productId = productId;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d71b3864006f94ac08938b2ebdc940bc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
namespace AppsFlyerSDK
{
/// <summary>
/// Purchase type enum matching iOS SDK AFSDKPurchaseType
/// </summary>
public enum AFSDKPurchaseType
{
Subscription,
OneTimePurchase
}
/// <summary>
/// Purchase details class matching iOS SDK AFSDKPurchaseDetails
/// </summary>
public class AFSDKPurchaseDetailsIOS
{
public string productId { get; private set; }
public string transactionId { get; private set; }
public AFSDKPurchaseType purchaseType { get; private set; }
private AFSDKPurchaseDetailsIOS(string productId, string transactionId, AFSDKPurchaseType purchaseType)
{
this.productId = productId;
this.transactionId = transactionId;
this.purchaseType = purchaseType;
}
public static AFSDKPurchaseDetailsIOS Init(string productId, string transactionId, AFSDKPurchaseType purchaseType)
{
return new AFSDKPurchaseDetailsIOS(productId, transactionId, purchaseType);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 44bb6c4472701416080eb050732075ea
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
namespace AppsFlyerSDK
{
public enum AFSDKValidateAndLogStatus
{
AFSDKValidateAndLogStatusSuccess,
AFSDKValidateAndLogStatusFailure,
AFSDKValidateAndLogStatusError
}
/// <summary>
//
/// </summary>
public class AFSDKValidateAndLogResult
{
public AFSDKValidateAndLogStatus status { get; private set; }
public Dictionary<string, object> result { get; private set; }
public Dictionary<string, object> errorData { get; private set; }
public string error { get; private set; }
private AFSDKValidateAndLogResult(AFSDKValidateAndLogStatus status, Dictionary<string, object> result, Dictionary<string, object> errorData, string error)
{
this.status = status;
this.result = result;
this.errorData = errorData;
this.error = error;
}
public static AFSDKValidateAndLogResult Init(AFSDKValidateAndLogStatus status, Dictionary<string, object> result, Dictionary<string, object> errorData, string error)
{
return new AFSDKValidateAndLogResult(status, result, errorData, error);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2df1c6f1eab2e4849bf2762a8d78933f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,13 @@
{
"name": "AppsFlyer",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2a37df438292d4903b4e5159c5de3bf9
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 45161025a517d427381d3d06153a5ad3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,852 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace AppsFlyerSDK
{
#if UNITY_ANDROID
public class AppsFlyerAndroid : IAppsFlyerAndroidBridge
{
public bool isInit { get; set; }
private static AndroidJavaClass appsFlyerAndroid = new AndroidJavaClass("com.appsflyer.unity.AppsFlyerAndroidWrapper");
public AppsFlyerAndroid() { }
/// <summary>
/// Use this method to init the sdk for the application.
/// Call this method before startSDK.
/// </summary>
/// <param name="devkey"> AppsFlyer's Dev-Key, which is accessible from your AppsFlyer account under 'App Settings' in the dashboard.</param>
/// <param name="gameObject">The current game object. This is used to get the conversion data callbacks. Pass null if you do not need the callbacks.</param>
public void initSDK(string devkey, MonoBehaviour gameObject)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("initSDK", devkey, gameObject ? gameObject.name : null);
#endif
}
/// <summary>
/// Use this method to start the sdk for the application.
/// The AppsFlyer's Dev-Key must be provided.
/// </summary>
/// <param name="devkey"> AppsFlyer's Dev-Key, which is accessible from your AppsFlyer account under 'App Settings' in the dashboard.</param>
public void startSDK(bool onRequestResponse, string CallBackObjectName)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("startTracking", onRequestResponse, CallBackObjectName);
#endif
}
/// <summary>
/// Once this API is invoked, our SDK no longer communicates with our servers and stops functioning.
/// In some extreme cases you might want to shut down all SDK activity due to legal and privacy compliance.
/// This can be achieved with the stopSDK API.
/// </summary>
/// <param name="isSDKStopped">boolean should SDK be stopped.</param>
public void stopSDK(bool isSDKStopped)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("stopTracking", isSDKStopped);
#endif
}
/// <summary>
/// Get the AppsFlyer SDK version used in app.
/// </summary>
/// <returns>AppsFlyer SDK version.</returns>
public string getSdkVersion()
{
#if !UNITY_EDITOR
return appsFlyerAndroid.CallStatic<string>("getSdkVersion");
#else
return "";
#endif
}
/// <summary>
/// Manually pass the Firebase / GCM Device Token for Uninstall measurement.
/// </summary>
/// <param name="token">Firebase Device Token.</param>
public void updateServerUninstallToken(string token)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("updateServerUninstallToken", token);
#endif
}
/// <summary>
/// Enables Debug logs for the AppsFlyer SDK.
/// Should only be set to true in development / debug.
/// </summary>
/// <param name="shouldEnable">shouldEnable boolean.</param>
public void setIsDebug(bool shouldEnable)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setIsDebug", shouldEnable);
#endif
}
/// <summary>
/// By default, IMEI and Android ID are not collected by the SDK if the OS version is higher than KitKat (4.4)
/// and the device contains Google Play Services(on SDK versions 4.8.8 and below the specific app needed GPS).
/// Use this API to explicitly send IMEI to AppsFlyer.
/// </summary>
/// <param name="aImei">device's IMEI.</param>
public void setImeiData(string aImei)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setImeiData", aImei);
#endif
}
/// <summary>
/// By default, IMEI and Android ID are not collected by the SDK if the OS version is higher than KitKat(4.4)
/// and the device contains Google Play Services(on SDK versions 4.8.8 and below the specific app needed GPS).
/// Use this API to explicitly send Android ID to AppsFlyer.
/// </summary>
/// <param name="aAndroidId">device's Android ID.</param>
public void setAndroidIdData(string aAndroidId)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setAndroidIdData", aAndroidId);
#endif
}
/// <summary>
/// Setting your own customer ID enables you to cross-reference your own unique ID with AppsFlyers unique ID and the other devices IDs.
/// This ID is available in AppsFlyer CSV reports along with Postback APIs for cross-referencing with your internal IDs.
/// </summary>
/// <param name="id">Customer ID for client.</param>
public void setCustomerUserId(string id)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setCustomerUserId", id);
#endif
}
/// <summary>
/// It is possible to delay the SDK Initialization until the customerUserID is set.
/// This feature makes sure that the SDK doesn't begin functioning until the customerUserID is provided.
/// If this API is used, all in-app events and any other SDK API calls are discarded, until the customerUserID is provided.
/// </summary>
/// <param name="wait">wait boolean.</param>
public void waitForCustomerUserId(bool wait)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("waitForCustomerUserId", wait);
#endif
}
/// <summary>
/// Use this API to provide the SDK with the relevant customer user id and trigger the SDK to begin its normal activity.
/// </summary>
/// <param name="id">Customer ID for client.</param>
public void setCustomerIdAndStartSDK(string id)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setCustomerIdAndTrack", id);
#endif
}
/// <summary>
/// Get the current AF_STORE value.
/// </summary>
/// <returns>AF_Store value.</returns>
public string getOutOfStore()
{
#if !UNITY_EDITOR
return appsFlyerAndroid.CallStatic<string>("getOutOfStore");
#else
return "";
#endif
}
/// <summary>
/// Manually set the AF_STORE value.
/// </summary>
/// <param name="sourceName">value to be set.</param>
public void setOutOfStore(string sourceName)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setOutOfStore", sourceName);
#endif
}
/// <summary>
/// Set the OneLink ID that should be used for User-Invites.
/// The link that is generated for the user invite will use this OneLink as the base link.
/// </summary>
/// <param name="oneLinkId">OneLink ID obtained from the AppsFlyer Dashboard.</param>
public void setAppInviteOneLinkID(string oneLinkId)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setAppInviteOneLinkID", oneLinkId);
#endif
}
/// <summary>
/// Set additional data to be sent to AppsFlyer.
/// </summary>
/// <param name="customData">additional data Dictionary.</param>
public void setAdditionalData(Dictionary<string, string> customData)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setAdditionalData", convertDictionaryToJavaMap(customData));
#endif
}
//// <summary>
/// Set the deepLink timeout value that should be used for DDL.
/// </summary>
/// <param name="deepLinkTimeout">deepLink timeout in milliseconds.</param>
public void setDeepLinkTimeout(long deepLinkTimeout)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setDeepLinkTimeout", deepLinkTimeout);
#endif
}
/// <summary>
/// Set the user emails.
/// </summary>
/// <param name="emails">User emails.</param>
public void setUserEmails(params string[] userEmails)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setUserEmails", (object)userEmails);
#endif
}
/// <summary>
/// Set the user phone number.
/// </summary>
/// <param name="phoneNumber">User phoneNumber.</param>
public void setPhoneNumber(string phoneNumber){
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setPhoneNumber", phoneNumber);
#endif
}
/// <summary>
/// Set the user emails and encrypt them.
/// cryptMethod Encryption method:
/// EmailCryptType.EmailCryptTypeMD5
/// EmailCryptType.EmailCryptTypeSHA1
/// EmailCryptType.EmailCryptTypeSHA256
/// EmailCryptType.EmailCryptTypeNone
/// </summary>
/// <param name="cryptMethod">Encryption method.</param>
/// <param name="emails">User emails.</param>
public void setUserEmails(EmailCryptType cryptMethod, params string[] emails)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setUserEmails", getEmailType(cryptMethod), (object)emails);
#endif
}
/// <summary>
/// Opt-out of collection of Android ID.
/// If the app does NOT contain Google Play Services, Android ID is collected by the SDK.
/// However, apps with Google play services should avoid Android ID collection as this is in violation of the Google Play policy.
/// </summary>
/// <param name="isCollect">boolean, false to opt-out.</param>
public void setCollectAndroidID(bool isCollect)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setCollectAndroidID", isCollect);
#endif
}
/// <summary>
/// Opt-out of collection of IMEI.
/// If the app does NOT contain Google Play Services, device IMEI is collected by the SDK.
/// However, apps with Google play services should avoid IMEI collection as this is in violation of the Google Play policy.
/// </summary>
/// <param name="isCollect">boolean, false to opt-out.</param>
public void setCollectIMEI(bool isCollect)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setCollectIMEI", isCollect);
#endif
}
/// <summary>
/// Advertisers can wrap AppsFlyer OneLink within another Universal Link.
/// This Universal Link will invoke the app but any deep linking data will not propagate to AppsFlyer.
/// </summary>
/// <param name="urls">Array of urls.</param>
public void setResolveDeepLinkURLs(params string[] urls)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setResolveDeepLinkURLs", (object)urls);
#endif
}
/// <summary>
/// Advertisers can use this method to set vanity onelink domains.
/// </summary>
/// <param name="domains">Array of domains.</param>
public void setOneLinkCustomDomain(params string[] domains)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setOneLinkCustomDomain", (object)domains);
#endif
}
/// <summary>
/// Manually set that the application was updated.
/// </summary>
/// <param name="isUpdate">isUpdate boolean value.</param>
public void setIsUpdate(bool isUpdate)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setIsUpdate", isUpdate);
#endif
}
/// <summary>
/// Setting user local currency code for in-app purchases.
/// The currency code should be a 3 character ISO 4217 code. (default is USD).
/// You can set the currency code for all events by calling the following method.
/// </summary>
/// <param name="currencyCode">3 character ISO 4217 code.</param>
public void setCurrencyCode(string currencyCode)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setCurrencyCode", currencyCode);
#endif
}
/// <summary>
/// Manually record the location of the user.
/// </summary>
/// <param name="latitude">latitude as double.</param>
/// <param name="longitude">longitude as double.</param>
public void recordLocation(double latitude, double longitude)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("trackLocation", latitude, longitude);
#endif
}
/// <summary>
/// Send an In-App Event.
/// In-App Events provide insight on what is happening in your app.
/// </summary>
/// <param name="eventName">Event Name as String.</param>
/// <param name="eventValues">Event Values as Dictionary.</param>
public void sendEvent(string eventName, Dictionary<string, string> eventValues)
{
sendEvent(eventName, eventValues, false, AppsFlyer.CallBackObjectName);
}
public void sendEvent(string eventName, Dictionary<string, string> eventValues, bool shouldCallback, string callBackObjectName)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("trackEvent", eventName, convertDictionaryToJavaMap(eventValues), shouldCallback, callBackObjectName);
#endif
}
/// <summary>
/// Anonymize user Data.
/// Use this API during the SDK Initialization to explicitly anonymize a user's installs, events and sessions.
/// Default is false.
/// </summary>
/// <param name="isDisabled">isDisabled boolean.</param>
public void anonymizeUser(bool isDisabled)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setDeviceTrackingDisabled", isDisabled);
#endif
}
/// <summary>
/// Calling enableTCFDataCollection(true) will enable collecting and sending any TCF related data.
/// Calling enableTCFDataCollection(false) will disable the collection of TCF related data and from sending it.
/// </summary>
/// <param name = "shouldCollectTcfData" >should start TCF Data collection boolean.</param>
public void enableTCFDataCollection(bool shouldCollectTcfData)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("enableTCFDataCollection", shouldCollectTcfData);
#endif
}
/// <summary>
/// Enable the collection of Facebook Deferred AppLinks.
/// Requires Facebook SDK and Facebook app on target/client device.
/// This API must be invoked prior to initializing the AppsFlyer SDK in order to function properly.
/// </summary>
/// <param name="isEnabled">should Facebook's deferred app links be processed by the AppsFlyer SDK.</param>
public void enableFacebookDeferredApplinks(bool isEnabled)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("enableFacebookDeferredApplinks", isEnabled);
#endif
}
/// <summary>
/// Sets or updates the user consent data related to GDPR and DMA regulations for advertising and data usage purposes within the application.
/// call this method when GDPR user is true
/// </summary>
/// <param name = "hasConsentForDataUsage" >hasConsentForDataUsage boolean.</param>
/// <param name = "hasConsentForAdsPersonalization" >hasConsentForAdsPersonalization boolean.</param>
public void setConsentData(AppsFlyerConsent appsFlyerConsent)
{
#if !UNITY_EDITOR
string isUserSubjectToGDPR = appsFlyerConsent.isUserSubjectToGDPR?.ToString().ToLower() ?? "null";
string hasConsentForDataUsage = appsFlyerConsent.hasConsentForDataUsage?.ToString().ToLower() ?? "null";
string hasConsentForAdsPersonalization = appsFlyerConsent.hasConsentForAdsPersonalization?.ToString().ToLower() ?? "null";
string hasConsentForAdStorage = appsFlyerConsent.hasConsentForAdStorage?.ToString().ToLower() ?? "null";
appsFlyerAndroid.CallStatic("setConsentData", isUserSubjectToGDPR, hasConsentForDataUsage, hasConsentForAdsPersonalization, hasConsentForAdStorage);
#endif
}
/// <summary>
/// Logs ad revenue data along with additional parameters if provided.
/// <param name = "adRevenueData" >instance of AFAdRevenueData containing ad revenue information.</param>
/// <param name = "additionalParameters" >An optional map of additional parameters to be logged with ad revenue data. This can be null if there are no additional parameters.</param>
public void logAdRevenue(AFAdRevenueData adRevenueData, Dictionary<string, string> additionalParameters)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("logAdRevenue", adRevenueData.monetizationNetwork, getMediationNetwork(adRevenueData.mediationNetwork), adRevenueData.currencyIso4217Code, adRevenueData.eventRevenue, convertDictionaryToJavaMap(additionalParameters));
#endif
}
/// <summary>
/// Restrict reengagement via deep-link to once per each unique deep-link.
/// Otherwise deep re-occurring deep-links will be permitted for non-singleTask Activities and deep-linking via AppsFlyer deep-links.
/// The default value is false.
/// </summary>
/// <param name="doConsume">doConsume boolean.</param>
public void setConsumeAFDeepLinks(bool doConsume)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setConsumeAFDeepLinks", doConsume);
#endif
}
/// <summary>
/// Specify the manufacturer or media source name to which the preinstall is attributed.
/// </summary>
/// <param name="mediaSource">Manufacturer or media source name for preinstall attribution.</param>
/// <param name="campaign">Campaign name for preinstall attribution.</param>
/// <param name="siteId">Site ID for preinstall attribution.</param>
public void setPreinstallAttribution(string mediaSource, string campaign, string siteId)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setPreinstallAttribution", mediaSource, campaign, siteId);
#endif
}
/// <summary>
/// Boolean indicator for preinstall by Manufacturer.
/// </summary>
/// <returns>boolean isPreInstalledApp.</returns>
public bool isPreInstalledApp()
{
#if !UNITY_EDITOR
return appsFlyerAndroid.CallStatic<bool>("isPreInstalledApp");
#else
return false;
#endif
}
/// <summary>
/// Get the Facebook attribution ID, if one exists.
/// </summary>
/// <returns>string Facebook attribution ID.</returns>
public string getAttributionId()
{
#if !UNITY_EDITOR
return appsFlyerAndroid.CallStatic<string>("getAttributionId");
#else
return "";
#endif
}
/// <summary>
/// Get AppsFlyer's unique device ID is created for every new install of an app.
/// </summary>
/// <returns>AppsFlyer's unique device ID.</returns>
public string getAppsFlyerId()
{
#if !UNITY_EDITOR
return appsFlyerAndroid.CallStatic<string>("getAppsFlyerId");
#else
return "";
#endif
}
/// <summary>
/// [Deprecated] API for server verification of in-app purchases - please use V2 with AFPurchaseDetailsAndroid instead.
/// An af_purchase event with the relevant values will be automatically sent if the validation is successful.
/// </summary>
/// <param name="publicKey">License Key obtained from the Google Play Console.</param>
/// <param name="signature"><code>data.INAPP_DATA_SIGNATURE</code> from <code>onActivityResult(int requestCode, int resultCode, Intent data)</code></param>
/// <param name="purchaseData"><code>data.INAPP_PURCHASE_DATA</code> from <code>onActivityResult(int requestCode, int resultCode, Intent data)</code></param>
/// <param name="price">Purchase price, should be derived from <code>skuDetails.getStringArrayList("DETAILS_LIST")</code></param>
/// <param name="currency">Purchase currency, should be derived from <code>skuDetails.getStringArrayList("DETAILS_LIST")</code></param>
/// <param name="additionalParameters">additionalParameters Freehand parameters to be sent with the purchase (if validated).</param>
[System.Obsolete("This method is deprecated. Use validateAndSendInAppPurchase(AFPurchaseDetailsAndroid details, Dictionary<string, string> purchaseAdditionalDetails, MonoBehaviour gameObject) instead.")]
public void validateAndSendInAppPurchase(string publicKey, string signature, string purchaseData, string price, string currency, Dictionary<string, string> additionalParameters, MonoBehaviour gameObject)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("validateAndTrackInAppPurchase", publicKey, signature, purchaseData, price, currency, convertDictionaryToJavaMap(additionalParameters), gameObject ? gameObject.name : null);
#endif
}
/// <summary>
/// V2 - API for server verification of in-app purchases.
/// An af_purchase event with the relevant values will be automatically sent if the validation is successful.
/// </summary>
/// <param name="details">AFPurchaseDetailsAndroid instance.</param>
/// <param name="purchaseAdditionalDetails">purchaseAdditionalDetails Freehand parameters to be sent with the purchase (if validated).</param>
public void validateAndSendInAppPurchase(AFPurchaseDetailsAndroid details, Dictionary<string, string> purchaseAdditionalDetails, MonoBehaviour gameObject)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("validateAndTrackInAppPurchaseV2", (int)details.purchaseType, details.purchaseToken, details.productId, convertDictionaryToJavaMap(purchaseAdditionalDetails), gameObject ? gameObject.name : null);
#endif
}
/// <summary>
/// Was the stopSDK(boolean) API set to true.
/// </summary>
/// <returns>boolean isSDKStopped.</returns>
public bool isSDKStopped()
{
#if !UNITY_EDITOR
return appsFlyerAndroid.CallStatic<bool>("isTrackingStopped");
#else
return false;
#endif
}
/// <summary>
/// Set a custom value for the minimum required time between sessions.
/// By default, at least 5 seconds must lapse between 2 app launches to count as separate 2 sessions.
/// </summary>
/// <param name="seconds">minimum time between 2 separate sessions in seconds.</param>
public void setMinTimeBetweenSessions(int seconds)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setMinTimeBetweenSessions", seconds);
#endif
}
/// <summary>
/// Set a custom host.
/// </summary>
/// <param name="hostPrefixName">Host prefix.</param>
/// <param name="hostName">Host name.</param>
public void setHost(string hostPrefixName, string hostName)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setHost", hostPrefixName, hostName);
#endif
}
/// <summary>
/// Get the host name.
/// Default value is "appsflyer.com".
/// </summary>
/// <returns>Host name.</returns>
public string getHostName()
{
#if !UNITY_EDITOR
return appsFlyerAndroid.CallStatic<string>("getHostName");
#else
return "";
#endif
}
/// <summary>
/// Get the custom host prefix.
/// </summary>
/// <returns>Host prefix.</returns>
public string getHostPrefix()
{
#if !UNITY_EDITOR
return appsFlyerAndroid.CallStatic<string>("getHostPrefix");
#else
return "";
#endif
}
/// <summary>
/// Used by advertisers to exclude all networks/integrated partners from getting data.
/// </summary>
public void setSharingFilterForAllPartners()
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setSharingFilterForAllPartners");
#endif
}
/// <summary>
/// Used by advertisers to set some (one or more) networks/integrated partners to exclude from getting data.
/// </summary>
/// <param name="partners">partners to exclude from getting data</param>
public void setSharingFilter(params string[] partners)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setSharingFilter", (object)partners);
#endif
}
/// <summary>
/// Lets you configure how which partners should the SDK exclude from data-sharing.
/// </summary>
/// <param name="partners">partners to exclude from getting data</param>
public static void setSharingFilterForPartners(params string[] partners)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setSharingFilterForPartners", (object)partners);
#endif
}
/// <summary>
/// Register a Conversion Data Listener.
/// Allows the developer to access the user attribution data in real-time for every new install, directly from the SDK level.
/// By doing this you can serve users with personalized content or send them to specific activities within the app,
/// which can greatly enhance their engagement with your app.
/// </summary>
public void getConversionData(string objectName)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("getConversionData", objectName);
#endif
}
/// <summary>
/// Register a validation listener for the validateAndSendInAppPurchase API.
/// </summary>
public void initInAppPurchaseValidatorListener(MonoBehaviour gameObject)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("initInAppPurchaseValidatorListener", gameObject ? gameObject.name : null);
#endif
}
/// <summary>
/// setCollectOaid
/// You must include the appsflyer oaid library for this api to work.
/// </summary>
/// <param name="isCollect">isCollect oaid - set fasle to opt out</param>
public void setCollectOaid(bool isCollect)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setCollectOaid", isCollect);
#endif
}
/// <summary>
/// Use the following API to attribute the click and launch the app store's app page.
/// </summary>
/// <param name="promoted_app_id">promoted App ID</param>
/// <param name="campaign">cross promotion campaign</param>
/// <param name="userParams">additional user params</param>
public void attributeAndOpenStore(string promoted_app_id, string campaign, Dictionary<string, string> userParams, MonoBehaviour gameObject)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("attributeAndOpenStore", promoted_app_id, campaign, convertDictionaryToJavaMap(userParams));
#endif
}
/// <summary>
/// To attribute an impression use the following API call.
/// Make sure to use the promoted App ID as it appears within the AppsFlyer dashboard.
/// </summary>
/// <param name="appID">promoted App ID.</param>
/// <param name="campaign">cross promotion campaign.</param>
/// <param name="parameters">parameters Dictionary.</param>
public void recordCrossPromoteImpression(string appID, string campaign, Dictionary<string, string> parameters)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("recordCrossPromoteImpression", appID, campaign, convertDictionaryToJavaMap(parameters));
#endif
}
/// <summary>
/// The LinkGenerator class builds the invite URL according to various setter methods which allow passing on additional information on the click.
/// See - https://support.appsflyer.com/hc/en-us/articles/115004480866-User-invite-attribution-
/// </summary>
/// <param name="parameters">parameters Dictionary.</param>
public void generateUserInviteLink(Dictionary<string, string> parameters, MonoBehaviour gameObject)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("createOneLinkInviteListener", convertDictionaryToJavaMap(parameters), gameObject ? gameObject.name : null);
#endif
}
/// <summary>
/// To measure push notifications as part of a retargeting campaign.
/// </summary>
public void handlePushNotifications(){
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("handlePushNotifications");
#endif
}
/// <summary>
/// Use this method if youre integrating your app with push providers
/// that dont use the default push notification JSON schema the SDK expects.
/// See docs for more info.
/// </summary>
/// <param name="paths">array of nested json path</param>
public void addPushNotificationDeepLinkPath(params string[] paths)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("addPushNotificationDeepLinkPath", (object)paths);
#endif
}
/// <summary>
/// subscribe to unified deep link callbacks
/// </summary>
public void subscribeForDeepLink(string objectName){
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("subscribeForDeepLink", objectName);
#endif
}
/// <summary>
/// Disables collection of various Advertising IDs by the SDK. This includes Google Advertising ID (GAID), OAID and Amazon Advertising ID (AAID)
/// </summary>
/// <param name="disable">disable boolean.</param>
public void setDisableAdvertisingIdentifiers(bool disable)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setDisableAdvertisingIdentifiers", disable);
#endif
}
/// <summary>
/// Allows sending custom data for partner integration purposes.
/// </summary>
public void setPartnerData(string partnerId, Dictionary<string, string> partnerInfo)
{
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setPartnerData", partnerId, convertDictionaryToJavaMap(partnerInfo));
#endif
}
/// <summary>
/// Use to opt-out of collecting the network operator name (carrier) and sim operator name from the device.
/// </summary>
public void setDisableNetworkData(bool disable) {
#if !UNITY_EDITOR
appsFlyerAndroid.CallStatic("setDisableNetworkData", disable);
#endif
}
/// <summary>
/// Internal Helper Method.
/// </summary>
private static AndroidJavaObject getEmailType(EmailCryptType cryptType)
{
AndroidJavaClass emailsCryptTypeEnum = new AndroidJavaClass("com.appsflyer.AppsFlyerProperties$EmailsCryptType");
AndroidJavaObject emailsCryptType;
switch (cryptType)
{
case EmailCryptType.EmailCryptTypeSHA256:
emailsCryptType = emailsCryptTypeEnum.GetStatic<AndroidJavaObject>("SHA256");
break;
default:
emailsCryptType = emailsCryptTypeEnum.GetStatic<AndroidJavaObject>("NONE");
break;
}
return emailsCryptType;
}
/// <summary>
/// Internal Helper Method.
/// </summary>
private static AndroidJavaObject getMediationNetwork(MediationNetwork mediationNetwork)
{
AndroidJavaClass mediationNetworkEnumClass = new AndroidJavaClass("com.appsflyer.MediationNetwork");
AndroidJavaObject mediationNetworkObject;
switch (mediationNetwork)
{
case MediationNetwork.IronSource:
mediationNetworkObject = mediationNetworkEnumClass.GetStatic<AndroidJavaObject>("IRONSOURCE");
break;
case MediationNetwork.ApplovinMax:
mediationNetworkObject = mediationNetworkEnumClass.GetStatic<AndroidJavaObject>("APPLOVIN_MAX");
break;
case MediationNetwork.GoogleAdMob:
mediationNetworkObject = mediationNetworkEnumClass.GetStatic<AndroidJavaObject>("GOOGLE_ADMOB");
break;
case MediationNetwork.Fyber:
mediationNetworkObject = mediationNetworkEnumClass.GetStatic<AndroidJavaObject>("FYBER");
break;
case MediationNetwork.Appodeal:
mediationNetworkObject = mediationNetworkEnumClass.GetStatic<AndroidJavaObject>("APPODEAL");
break;
case MediationNetwork.Admost:
mediationNetworkObject = mediationNetworkEnumClass.GetStatic<AndroidJavaObject>("ADMOST");
break;
case MediationNetwork.Topon:
mediationNetworkObject = mediationNetworkEnumClass.GetStatic<AndroidJavaObject>("TOPON");
break;
case MediationNetwork.Tradplus:
mediationNetworkObject = mediationNetworkEnumClass.GetStatic<AndroidJavaObject>("TRADPLUS");
break;
case MediationNetwork.Yandex:
mediationNetworkObject = mediationNetworkEnumClass.GetStatic<AndroidJavaObject>("YANDEX");
break;
case MediationNetwork.ChartBoost:
mediationNetworkObject = mediationNetworkEnumClass.GetStatic<AndroidJavaObject>("CHARTBOOST");
break;
case MediationNetwork.Unity:
mediationNetworkObject = mediationNetworkEnumClass.GetStatic<AndroidJavaObject>("UNITY");
break;
case MediationNetwork.ToponPte:
mediationNetworkObject = mediationNetworkEnumClass.GetStatic<AndroidJavaObject>("TOPON_PTE");
break;
case MediationNetwork.Custom:
mediationNetworkObject = mediationNetworkEnumClass.GetStatic<AndroidJavaObject>("CUSTOM_MEDIATION");
break;
case MediationNetwork.DirectMonetization:
mediationNetworkObject = mediationNetworkEnumClass.GetStatic<AndroidJavaObject>("DIRECT_MONETIZATION_NETWORK");
break;
default:
mediationNetworkObject = mediationNetworkEnumClass.GetStatic<AndroidJavaObject>("NONE");
break;
}
return mediationNetworkObject;
}
/// <summary>
/// Internal Helper Method.
/// </summary>
private static AndroidJavaObject convertDictionaryToJavaMap(Dictionary<string, string> dictionary)
{
AndroidJavaObject map = new AndroidJavaObject("java.util.HashMap");
IntPtr putMethod = AndroidJNIHelper.GetMethodID(map.GetRawClass(), "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
jvalue[] val;
if (dictionary != null)
{
foreach (var entry in dictionary)
{
val = AndroidJNIHelper.CreateJNIArgArray(new object[] { entry.Key, entry.Value });
AndroidJNI.CallObjectMethod(map.GetRawObject(), putMethod,val);
AndroidJNI.DeleteLocalRef(val[0].l);
AndroidJNI.DeleteLocalRef(val[1].l);
}
}
return map;
}
}
#endif
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 172d18dd98e7e4ed3b30110568b0fae4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
namespace AppsFlyerSDK
{
/// <summary>
// Data class representing a user's consent for data processing in accordance with GDPR and DMA
// (Digital Markets Act) compliance, specifically regarding advertising preferences.
// This class should be used to notify and record the user's applicability
// under GDPR, their general consent to data usage, and their consent to personalized
// advertisements based on user data.
/// ## Properties:
/// - `isUserSubjectToGDPR` (optional) - Indicates whether GDPR regulations apply to the user.
/// This may also serve as a general compliance flag for other regional regulations.
/// - `hasConsentForDataUsage` (optional) - Indicates whether the user consents to the processing
/// of their data for advertising purposes.
/// - `hasConsentForAdsPersonalization` (optional) - Indicates whether the user consents to the
/// use of their data for personalized advertising.
/// - `hasConsentForAdStorage` (optional) - Indicates whether the user consents to ad-related storage access.
///
/// **Usage Example:**
/// ```csharp
/// var consent = new AppsFlyerConsent(
/// isUserSubjectToGDPR: true,
/// hasConsentForDataUsage: true,
/// hasConsentForAdsPersonalization: false,
/// hasConsentForAdStorage: true
/// );
/// **Deprecated APIs:**
/// - `ForGDPRUser(...)` and `ForNonGDPRUser(...)` should no longer be used.
/// - Use `new AppsFlyerConsent(...)` instead with relevant consent fields.
///
/// </summary>
public class AppsFlyerConsent
{
public bool? isUserSubjectToGDPR { get; private set; }
public bool? hasConsentForDataUsage { get; private set; }
public bool? hasConsentForAdsPersonalization { get; private set; }
public bool? hasConsentForAdStorage { get; private set; }
public AppsFlyerConsent( bool? isUserSubjectToGDPR = null, bool? hasConsentForDataUsage = null, bool? hasConsentForAdsPersonalization = null, bool? hasConsentForAdStorage = null)
{
this.isUserSubjectToGDPR = isUserSubjectToGDPR;
this.hasConsentForDataUsage = hasConsentForDataUsage;
this.hasConsentForAdsPersonalization = hasConsentForAdsPersonalization;
this.hasConsentForAdStorage = hasConsentForAdStorage;
}
[Obsolete("Use the new constructor with optional booleans instead.")]
private AppsFlyerConsent(bool isGDPR, bool hasForDataUsage, bool hasForAdsPersonalization)
{
isUserSubjectToGDPR = isGDPR;
hasConsentForDataUsage = hasForDataUsage;
hasConsentForAdsPersonalization = hasForAdsPersonalization;
}
[Obsolete("Use new AppsFlyerConsent(...) instead.")]
public static AppsFlyerConsent ForGDPRUser(bool hasConsentForDataUsage, bool hasConsentForAdsPersonalization)
{
return new AppsFlyerConsent(true, hasConsentForDataUsage, hasConsentForAdsPersonalization);
}
[Obsolete("Use new AppsFlyerConsent(...) instead.")]
public static AppsFlyerConsent ForNonGDPRUser()
{
return new AppsFlyerConsent(false);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a97c986fe4ee0461badf7042e08db3f3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,218 @@
using System;
using System.Collections.Generic;
namespace AppsFlyerSDK
{
/// <summary>
/// Event args for AppsFlyer requests.
/// Used for sessions and in-app events.
/// Used to handle post request logic.
///
/// Examples:
/// statusCode / errorDescription
///
/// 200 - null
///
/// 10 - "Event timeout. Check 'minTimeBetweenSessions' param"
/// 11 - "Skipping event because 'isStopTracking' enabled"
/// 40 - Network error: Error description comes from Android
/// 41 - "No dev key"
/// 50 - "Status code failure" + actual response code from the server
///
/// </summary>
public class AppsFlyerRequestEventArgs : EventArgs
{
public AppsFlyerRequestEventArgs(int code, string description)
{
statusCode = code;
errorDescription = description;
}
public int statusCode { get; }
public string errorDescription { get; }
}
/// <summary>
/// Event args for OnDeepLinkReceived.
/// Used to handle deep linking results.
/// </summary>
public class DeepLinkEventsArgs : EventArgs
{
/// <summary>
/// DeepLink dictionary to get additional parameters
/// </summary>
public Dictionary<string, object> deepLink;
/// <summary>
/// DeepLink status: FOUND, NOT_FOUND, ERROR
/// </summary>
public DeepLinkStatus status { get; }
/// <summary>
/// DeepLink error: TIMEOUT, NETWORK, HTTP_STATUS_CODE, UNEXPECTED
/// </summary>
public DeepLinkError error { get; }
public string getMatchType()
{
return getDeepLinkParameter("match_type");
}
public string getDeepLinkValue()
{
return getDeepLinkParameter("deep_link_value");
}
public string getClickHttpReferrer()
{
return getDeepLinkParameter("click_http_referrer");
}
public string getMediaSource()
{
return getDeepLinkParameter("media_source");
}
public string getCampaign()
{
return getDeepLinkParameter("campaign");
}
public string getCampaignId()
{
return getDeepLinkParameter("campaign_id");
}
public string getAfSub1()
{
return getDeepLinkParameter("af_sub1");
}
public string getAfSub2()
{
return getDeepLinkParameter("af_sub2");
}
public string getAfSub3()
{
return getDeepLinkParameter("af_sub3");
}
public string getAfSub4()
{
return getDeepLinkParameter("af_sub4");
}
public string getAfSub5()
{
return getDeepLinkParameter("af_sub5");
}
public bool isDeferred()
{
if (deepLink != null && deepLink.ContainsKey("is_deferred"))
{
try
{
return (bool)deepLink["is_deferred"];
}
catch (Exception e)
{
AppsFlyer.AFLog("DeepLinkEventsArgs.isDeferred", String.Format("{0} Exception caught.", e));
}
}
return false;
}
public Dictionary<string, object> getDeepLinkDictionary()
{
return deepLink;
}
public DeepLinkEventsArgs(string str)
{
try
{
Dictionary<string, object> dictionary = AppsFlyer.CallbackStringToDictionary(str);
string status = "";
string error = "";
if (dictionary.ContainsKey("status") && dictionary["status"] != null)
{
status = dictionary["status"].ToString();
}
if (dictionary.ContainsKey("error") && dictionary["error"] != null)
{
error = dictionary["error"].ToString();
}
if (dictionary.ContainsKey("deepLink") && dictionary["deepLink"] != null)
{
this.deepLink = AppsFlyer.CallbackStringToDictionary(dictionary["deepLink"].ToString());
}
if (dictionary.ContainsKey("is_deferred"))
{
this.deepLink["is_deferred"] = dictionary["is_deferred"];
}
switch (status)
{
case "FOUND":
this.status = DeepLinkStatus.FOUND;
break;
case "NOT_FOUND":
this.status = DeepLinkStatus.NOT_FOUND;
break;
default:
this.status = DeepLinkStatus.ERROR;
break;
}
switch (error)
{
case "TIMEOUT":
this.error = DeepLinkError.TIMEOUT;
break;
case "NETWORK":
this.error = DeepLinkError.NETWORK;
break;
case "HTTP_STATUS_CODE":
this.error = DeepLinkError.HTTP_STATUS_CODE;
break;
default:
this.error = DeepLinkError.UNEXPECTED;
break;
}
}
catch (Exception e)
{
AppsFlyer.AFLog("DeepLinkEventsArgs.parseDeepLink", String.Format("{0} Exception caught.", e));
}
}
private string getDeepLinkParameter(string name)
{
if (deepLink != null && deepLink.ContainsKey(name) && deepLink[name] != null)
{
return deepLink[name].ToString();
}
return null;
}
}
public enum DeepLinkStatus {
FOUND, NOT_FOUND, ERROR
}
public enum DeepLinkError {
TIMEOUT, NETWORK, HTTP_STATUS_CODE, UNEXPECTED
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a0fc241ad5a9b43a7b461a6147dbc74c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,52 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &7315102894599890749
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6207133488976360133}
- component: {fileID: 4405976200006927252}
m_Layer: 0
m_Name: AppsFlyerObject
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &6207133488976360133
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7315102894599890749}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &4405976200006927252
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7315102894599890749}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2a2ec6ba1ee8b48749524f015ed572a6, type: 3}
m_Name:
m_EditorClassIdentifier:
devKey:
appID: 6775403944
UWPAppID:
macOSAppID:
isDebug: 0
getConversionData: 1

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 0bfe3b149145747cc92dc53bb4df4e9b
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,70 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using AppsFlyerSDK;
// This class is intended to be used the the AppsFlyerObject.prefab
public class AppsFlyerObjectScript : MonoBehaviour , IAppsFlyerConversionData
{
// These fields are set from the editor so do not modify!
//******************************//
public string devKey;
public string appID;
public string UWPAppID;
public string macOSAppID;
public bool isDebug;
public bool getConversionData;
//******************************//
void Start()
{
// These fields are set from the editor so do not modify!
//******************************//
AppsFlyer.setIsDebug(isDebug);
#if UNITY_WSA_10_0 && !UNITY_EDITOR
AppsFlyer.initSDK(devKey, UWPAppID, getConversionData ? this : null);
#elif UNITY_STANDALONE_OSX && !UNITY_EDITOR
AppsFlyer.initSDK(devKey, macOSAppID, getConversionData ? this : null);
#else
AppsFlyer.initSDK(devKey, appID, getConversionData ? this : null);
#endif
//******************************/
AppsFlyer.startSDK();
}
void Update()
{
}
// Mark AppsFlyer CallBacks
public void onConversionDataSuccess(string conversionData)
{
AppsFlyer.AFLog("didReceiveConversionData", conversionData);
Dictionary<string, object> conversionDataDictionary = AppsFlyer.CallbackStringToDictionary(conversionData);
// add deferred deeplink logic here
}
public void onConversionDataFail(string error)
{
AppsFlyer.AFLog("didReceiveConversionDataWithError", error);
}
public void onAppOpenAttribution(string attributionData)
{
AppsFlyer.AFLog("onAppOpenAttribution", attributionData);
Dictionary<string, object> attributionDataDictionary = AppsFlyer.CallbackStringToDictionary(attributionData);
// add direct deeplink logic here
}
public void onAppOpenAttributionFailure(string error)
{
AppsFlyer.AFLog("onAppOpenAttributionFailure", error);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2a2ec6ba1ee8b48749524f015ed572a6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,426 @@
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
using System;
namespace AppsFlyerSDK
{
public interface IAppsFlyerPurchaseRevenueDataSource
{
Dictionary<string, object> PurchaseRevenueAdditionalParametersForProducts(HashSet<object> products, HashSet<object> transactions);
}
public interface IAppsFlyerPurchaseRevenueDataSourceStoreKit2
{
Dictionary<string, object> PurchaseRevenueAdditionalParametersStoreKit2ForProducts(HashSet<object> products, HashSet<object> transactions);
}
public class AppsFlyerPurchaseRevenueBridge : MonoBehaviour
{
#if UNITY_IOS && !UNITY_EDITOR
[DllImport("__Internal")]
private static extern void RegisterUnityPurchaseRevenueParamsCallback(Func<string, string, string> callback);
[DllImport("__Internal")]
private static extern void RegisterUnityPurchaseRevenueParamsCallbackSK2(Func<string, string, string> callback);
#endif
private static IAppsFlyerPurchaseRevenueDataSource _dataSource;
private static IAppsFlyerPurchaseRevenueDataSourceStoreKit2 _dataSourceSK2;
public static void RegisterDataSource(IAppsFlyerPurchaseRevenueDataSource dataSource)
{
_dataSource = dataSource;
#if UNITY_IOS && !UNITY_EDITOR
RegisterUnityPurchaseRevenueParamsCallback(GetAdditionalParameters);
#elif UNITY_ANDROID && !UNITY_EDITOR
using (AndroidJavaClass jc = new AndroidJavaClass("com.appsflyer.unity.PurchaseRevenueBridge"))
{
jc.CallStatic("setUnityBridge", new UnityPurchaseRevenueBridgeProxy());
}
#endif
}
public static void RegisterDataSourceStoreKit2(IAppsFlyerPurchaseRevenueDataSourceStoreKit2 dataSource)
{
#if UNITY_IOS && !UNITY_EDITOR
_dataSourceSK2 = dataSource;
RegisterUnityPurchaseRevenueParamsCallbackSK2(GetAdditionalParametersSK2);
#endif
}
public static Dictionary<string, object> GetAdditionalParametersForAndroid(HashSet<object> products, HashSet<object> transactions)
{
return _dataSource?.PurchaseRevenueAdditionalParametersForProducts(products, transactions)
?? new Dictionary<string, object>();
}
#if UNITY_IOS && !UNITY_EDITOR
[AOT.MonoPInvokeCallback(typeof(Func<string, string, string>))]
public static string GetAdditionalParameters(string productsJson, string transactionsJson)
{
try
{
HashSet<object> products = new HashSet<object>();
HashSet<object> transactions = new HashSet<object>();
if (!string.IsNullOrEmpty(productsJson))
{
var dict = AFMiniJSON.Json.Deserialize(productsJson) as Dictionary<string, object>;
if (dict != null)
{
if (dict.TryGetValue("products", out var productsObj) && productsObj is List<object> productList)
products = new HashSet<object>(productList);
if (dict.TryGetValue("transactions", out var transactionsObj) && transactionsObj is List<object> transactionList)
transactions = new HashSet<object>(transactionList);
}
}
var parameters = _dataSource?.PurchaseRevenueAdditionalParametersForProducts(products, transactions)
?? new Dictionary<string, object>();
return AFMiniJSON.Json.Serialize(parameters);
}
catch (Exception e)
{
Debug.LogError($"[AppsFlyer] Exception in GetAdditionalParameters: {e}");
return "{}";
}
}
#endif
#if UNITY_IOS && !UNITY_EDITOR
[AOT.MonoPInvokeCallback(typeof(Func<string, string, string>))]
public static string GetAdditionalParametersSK2(string productsJson, string transactionsJson)
{
try
{
HashSet<object> products = new HashSet<object>();
HashSet<object> transactions = new HashSet<object>();
if (!string.IsNullOrEmpty(productsJson))
{
var dict = AFMiniJSON.Json.Deserialize(productsJson) as Dictionary<string, object>;
if (dict != null && dict.TryGetValue("products", out var productsObj) && productsObj is List<object> productList)
products = new HashSet<object>(productList);
}
if (!string.IsNullOrEmpty(transactionsJson))
{
var dict = AFMiniJSON.Json.Deserialize(transactionsJson) as Dictionary<string, object>;
if (dict != null && dict.TryGetValue("transactions", out var transactionsObj) && transactionsObj is List<object> transactionList)
transactions = new HashSet<object>(transactionList);
}
var parameters = _dataSourceSK2?.PurchaseRevenueAdditionalParametersStoreKit2ForProducts(products, transactions)
?? new Dictionary<string, object>();
return AFMiniJSON.Json.Serialize(parameters);
}
catch (Exception e)
{
Debug.LogError($"[AppsFlyer] Exception in GetAdditionalParametersSK2: {e}");
return "{}";
}
}
#endif
}
public class UnityPurchaseRevenueBridgeProxy : AndroidJavaProxy
{
public UnityPurchaseRevenueBridgeProxy() : base("com.appsflyer.unity.PurchaseRevenueBridge$UnityPurchaseRevenueBridge") { }
public string getAdditionalParameters(string productsJson, string transactionsJson)
{
try
{
// Create empty sets if JSON is null or empty
HashSet<object> products = new HashSet<object>();
HashSet<object> transactions = new HashSet<object>();
// Only try to parse if we have valid JSON
if (!string.IsNullOrEmpty(productsJson))
{
try
{
// First try to parse as a simple array
var parsedProducts = AFMiniJSON.Json.Deserialize(productsJson);
if (parsedProducts is List<object> productList)
{
products = new HashSet<object>(productList);
}
else if (parsedProducts is Dictionary<string, object> dict)
{
if (dict.ContainsKey("events") && dict["events"] is List<object> eventsList)
{
products = new HashSet<object>(eventsList);
}
else
{
// If it's a dictionary but doesn't have events, add the whole dict
products.Add(dict);
}
}
}
catch (Exception e)
{
Debug.LogError($"Error parsing products JSON: {e.Message}\nJSON: {productsJson}");
}
}
if (!string.IsNullOrEmpty(transactionsJson))
{
try
{
// First try to parse as a simple array
var parsedTransactions = AFMiniJSON.Json.Deserialize(transactionsJson);
if (parsedTransactions is List<object> transactionList)
{
transactions = new HashSet<object>(transactionList);
}
else if (parsedTransactions is Dictionary<string, object> dict)
{
if (dict.ContainsKey("events") && dict["events"] is List<object> eventsList)
{
transactions = new HashSet<object>(eventsList);
}
else
{
// If it's a dictionary but doesn't have events, add the whole dict
transactions.Add(dict);
}
}
}
catch (Exception e)
{
Debug.LogError($"Error parsing transactions JSON: {e.Message}\nJSON: {transactionsJson}");
}
}
var parameters = AppsFlyerPurchaseRevenueBridge.GetAdditionalParametersForAndroid(products, transactions);
return AFMiniJSON.Json.Serialize(parameters);
}
catch (Exception e)
{
Debug.LogError($"Error in getAdditionalParameters: {e.Message}\nProducts JSON: {productsJson}\nTransactions JSON: {transactionsJson}");
return "{}";
}
}
}
public class AppsFlyerPurchaseConnector : MonoBehaviour {
private static AppsFlyerPurchaseConnector instance;
private Dictionary<string, object> pendingParameters;
private Action<Dictionary<string, object>> pendingCallback;
public static AppsFlyerPurchaseConnector Instance
{
get
{
if (instance == null)
{
GameObject go = new GameObject("AppsFlyerPurchaseConnector");
instance = go.AddComponent<AppsFlyerPurchaseConnector>();
DontDestroyOnLoad(go);
}
return instance;
}
}
private void Awake()
{
if (instance == null)
{
instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
#if UNITY_ANDROID && !UNITY_EDITOR
private static AndroidJavaClass appsFlyerAndroidConnector = new AndroidJavaClass("com.appsflyer.unity.AppsFlyerAndroidWrapper");
#endif
public static void init(MonoBehaviour unityObject, Store s) {
#if UNITY_IOS && !UNITY_EDITOR
_initPurchaseConnector(unityObject.name);
#elif UNITY_ANDROID && !UNITY_EDITOR
int store = mapStoreToInt(s);
appsFlyerAndroidConnector.CallStatic("initPurchaseConnector", unityObject ? unityObject.name : null, store);
#endif
}
public static void build() {
#if UNITY_IOS && !UNITY_EDITOR
//not for iOS
#elif UNITY_ANDROID && !UNITY_EDITOR
appsFlyerAndroidConnector.CallStatic("build");
#else
#endif
}
public static void startObservingTransactions() {
#if UNITY_IOS && !UNITY_EDITOR
_startObservingTransactions();
#elif UNITY_ANDROID && !UNITY_EDITOR
appsFlyerAndroidConnector.CallStatic("startObservingTransactions");
#else
#endif
}
public static void stopObservingTransactions() {
#if UNITY_IOS && !UNITY_EDITOR
_stopObservingTransactions();
#elif UNITY_ANDROID && !UNITY_EDITOR
appsFlyerAndroidConnector.CallStatic("stopObservingTransactions");
#else
#endif
}
public static void setIsSandbox(bool isSandbox) {
#if UNITY_IOS && !UNITY_EDITOR
_setIsSandbox(isSandbox);
#elif UNITY_ANDROID && !UNITY_EDITOR
appsFlyerAndroidConnector.CallStatic("setIsSandbox", isSandbox);
#else
#endif
}
public static void setPurchaseRevenueValidationListeners(bool enableCallbacks) {
#if UNITY_IOS && !UNITY_EDITOR
_setPurchaseRevenueDelegate();
#elif UNITY_ANDROID && !UNITY_EDITOR
appsFlyerAndroidConnector.CallStatic("setPurchaseRevenueValidationListeners", enableCallbacks);
#else
#endif
}
public static void setAutoLogPurchaseRevenue(params AppsFlyerAutoLogPurchaseRevenueOptions[] autoLogPurchaseRevenueOptions) {
#if UNITY_IOS && !UNITY_EDITOR
int option = 0;
foreach (AppsFlyerAutoLogPurchaseRevenueOptions op in autoLogPurchaseRevenueOptions) {
option = option | (int)op;
}
_setAutoLogPurchaseRevenue(option);
#elif UNITY_ANDROID && !UNITY_EDITOR
if (autoLogPurchaseRevenueOptions.Length == 0) {
return;
}
foreach (AppsFlyerAutoLogPurchaseRevenueOptions op in autoLogPurchaseRevenueOptions) {
switch(op) {
case AppsFlyerAutoLogPurchaseRevenueOptions.AppsFlyerAutoLogPurchaseRevenueOptionsDisabled:
break;
case AppsFlyerAutoLogPurchaseRevenueOptions.AppsFlyerAutoLogPurchaseRevenueOptionsAutoRenewableSubscriptions:
appsFlyerAndroidConnector.CallStatic("setAutoLogSubscriptions", true);
break;
case AppsFlyerAutoLogPurchaseRevenueOptions.AppsFlyerAutoLogPurchaseRevenueOptionsInAppPurchases:
appsFlyerAndroidConnector.CallStatic("setAutoLogInApps", true);
break;
default:
break;
}
}
#else
#endif
}
public static void setPurchaseRevenueDataSource(IAppsFlyerPurchaseRevenueDataSource dataSource)
{
#if UNITY_IOS && !UNITY_EDITOR
if (dataSource != null)
{
_setPurchaseRevenueDataSource(dataSource.GetType().Name);
AppsFlyerPurchaseRevenueBridge.RegisterDataSource(dataSource);
}
#elif UNITY_ANDROID && !UNITY_EDITOR
if (dataSource != null)
{
AppsFlyerPurchaseRevenueBridge.RegisterDataSource(dataSource);
}
#endif
}
public static void setPurchaseRevenueDataSourceStoreKit2(IAppsFlyerPurchaseRevenueDataSourceStoreKit2 dataSourceSK2)
{
#if UNITY_IOS && !UNITY_EDITOR
if (dataSourceSK2 != null)
{
AppsFlyerPurchaseRevenueBridge.RegisterDataSourceStoreKit2(dataSourceSK2);
_setPurchaseRevenueDataSource("AppsFlyerObjectScript_StoreKit2");
}
#endif
}
private static int mapStoreToInt(Store s) {
switch(s) {
case(Store.GOOGLE):
return 0;
default:
return -1;
}
}
public static void setStoreKitVersion(StoreKitVersion storeKitVersion) {
#if UNITY_IOS && !UNITY_EDITOR
_setStoreKitVersion((int)storeKitVersion);
#elif UNITY_ANDROID && !UNITY_EDITOR
// Android doesn't use StoreKit
#else
#endif
}
public static void logConsumableTransaction(string transactionJson) {
#if UNITY_IOS && !UNITY_EDITOR
_logConsumableTransaction(transactionJson);
#elif UNITY_ANDROID && !UNITY_EDITOR
// Android doesn't use StoreKit
#else
#endif
}
#if UNITY_IOS && !UNITY_EDITOR
[DllImport("__Internal")]
private static extern void _startObservingTransactions();
[DllImport("__Internal")]
private static extern void _stopObservingTransactions();
[DllImport("__Internal")]
private static extern void _setIsSandbox(bool isSandbox);
[DllImport("__Internal")]
private static extern void _setPurchaseRevenueDelegate();
[DllImport("__Internal")]
private static extern void _setPurchaseRevenueDataSource(string dataSourceName);
[DllImport("__Internal")]
private static extern void _setAutoLogPurchaseRevenue(int option);
[DllImport("__Internal")]
private static extern void _initPurchaseConnector(string objectName);
[DllImport("__Internal")]
private static extern void _setStoreKitVersion(int storeKitVersion);
[DllImport("__Internal")]
private static extern void _logConsumableTransaction(string transactionJson);
#endif
}
public enum Store {
GOOGLE = 0
}
public enum AppsFlyerAutoLogPurchaseRevenueOptions
{
AppsFlyerAutoLogPurchaseRevenueOptionsDisabled = 0,
AppsFlyerAutoLogPurchaseRevenueOptionsAutoRenewableSubscriptions = 1 << 0,
AppsFlyerAutoLogPurchaseRevenueOptionsInAppPurchases = 1 << 1
}
public enum StoreKitVersion {
SK1 = 0,
SK2 = 1
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0636ea07d370d437183f3762280c08ce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b34371b3cc09641ebb007ffc4e9500f0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9f84c02aa78da4ac9b444d98d97f7cc4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
{
"name": "AppsFlyer.Editor",
"references": [
"AppsFlyer"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: d008146f00dea44d38752b4289e5f65b
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<dependencies>
<androidPackages>
<androidPackage spec="com.appsflyer:af-android-sdk:6.17.6"></androidPackage>
<androidPackage spec="com.appsflyer:unity-wrapper:6.17.91"></androidPackage>
<androidPackage spec="com.android.installreferrer:installreferrer:2.1"></androidPackage>
<androidPackage spec="com.appsflyer:purchase-connector:2.2.0"></androidPackage>
</androidPackages>
<iosPods>
<iosPod name="AppsFlyerFramework/Strict" version="6.17.9" minTargetSdk="12.0"></iosPod>
<iosPod name="PurchaseConnector/Strict" version="6.17.9" minTargetSdk="12.0"></iosPod>
</iosPods>
</dependencies>

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: a03558dbbfeac45db9afe9e9c2df5a85
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,104 @@
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(AppsFlyerObjectScript))]
[CanEditMultipleObjects]
public class AppsFlyerObjectEditor : Editor
{
SerializedProperty devKey;
SerializedProperty appID;
SerializedProperty UWPAppID;
SerializedProperty macOSAppID;
SerializedProperty isDebug;
SerializedProperty getConversionData;
void OnEnable()
{
devKey = serializedObject.FindProperty("devKey");
appID = serializedObject.FindProperty("appID");
UWPAppID = serializedObject.FindProperty("UWPAppID");
macOSAppID = serializedObject.FindProperty("macOSAppID");
isDebug = serializedObject.FindProperty("isDebug");
getConversionData = serializedObject.FindProperty("getConversionData");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
DrawLogo();
EditorGUILayout.Separator();
EditorGUILayout.HelpBox("Set your devKey and appID to init the AppsFlyer SDK and start tracking. You must modify these fields and provide:\ndevKey - Your application devKey provided by AppsFlyer.\nappId - For iOS only. Your iTunes Application ID.\nUWP app id - For UWP only. Your application app id \nMac OS app id - For MacOS app only.", MessageType.Info);
EditorGUILayout.PropertyField(devKey);
EditorGUILayout.PropertyField(appID);
EditorGUILayout.PropertyField(UWPAppID);
EditorGUILayout.PropertyField(macOSAppID);
EditorGUILayout.Separator();
EditorGUILayout.HelpBox("Enable get conversion data to allow your app to recive deeplinking callbacks", MessageType.None);
EditorGUILayout.PropertyField(getConversionData);
EditorGUILayout.Separator();
EditorGUILayout.HelpBox("Debugging should be restricted to development phase only.\n Do not distribute the app to app stores with debugging enabled", MessageType.Warning);
EditorGUILayout.PropertyField(isDebug);
EditorGUILayout.Separator();
EditorGUILayout.HelpBox("For more information on setting up AppsFlyer check out our relevant docs.", MessageType.None);
if (GUILayout.Button("AppsFlyer Unity Docs", new GUILayoutOption[] { GUILayout.Width(200) }))
{
Application.OpenURL("https://support.appsflyer.com/hc/en-us/articles/213766183-Unity-SDK-integration-for-developers");
}
if (GUILayout.Button("AppsFlyer Android Docs", new GUILayoutOption[] { GUILayout.Width(200) }))
{
Application.OpenURL("https://support.appsflyer.com/hc/en-us/articles/207032126-Android-SDK-integration-for-developers");
}
if (GUILayout.Button("AppsFlyer iOS Docs", new GUILayoutOption[] { GUILayout.Width(200) }))
{
Application.OpenURL("https://support.appsflyer.com/hc/en-us/articles/207032066-AppsFlyer-SDK-Integration-iOS");
}
if (GUILayout.Button("AppsFlyer Deeplinking Docs", new GUILayoutOption[] { GUILayout.Width(200) }))
{
Application.OpenURL("https://support.appsflyer.com/hc/en-us/articles/208874366-OneLink-deep-linking-guide#Setups");
}
if (GUILayout.Button("AppsFlyer Windows Docs", new GUILayoutOption[] { GUILayout.Width(200) }))
{
Application.OpenURL("https://support.appsflyer.com/hc/en-us/articles/207032026-Windows-and-Xbox-SDK-integration-for-developers");
}
serializedObject.ApplyModifiedProperties();
}
private void DrawLogo()
{
var guids = AssetDatabase.FindAssets("appsflyer_logo");
if (guids.Length == 0) return;
Texture logo = (Texture)AssetDatabase.LoadAssetAtPath(
AssetDatabase.GUIDToAssetPath(guids[0]),
typeof(Texture));
if (logo == null) return;
float maxWidth = Mathf.Min(200, EditorGUIUtility.currentViewWidth - 40);
float aspect = (float)logo.height / logo.width;
float height = maxWidth * aspect;
Rect rect = GUILayoutUtility.GetRect(maxWidth, height, GUILayout.ExpandWidth(false));
rect.x = (EditorGUIUtility.currentViewWidth - maxWidth) * 0.5f;
rect.width = maxWidth;
GUI.DrawTexture(rect, logo, ScaleMode.ScaleToFit);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d248a134cf494486fb1d6a2e95a05d87
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

@@ -0,0 +1,92 @@
fileFormatVersion: 2
guid: bc7fa5a6b64b944a4b2900fd877acb8b
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: -1
wrapV: -1
wrapW: -1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,31 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace AppsFlyerSDK
{
public interface IAppsFlyerAndroidBridge : IAppsFlyerNativeBridge
{
void updateServerUninstallToken(string token);
void setImeiData(string imei);
void setAndroidIdData(string androidId);
void waitForCustomerUserId(bool wait);
void setCustomerIdAndStartSDK(string id);
string getOutOfStore();
void setOutOfStore(string sourceName);
void setCollectAndroidID(bool isCollect);
void setCollectIMEI(bool isCollect);
void setIsUpdate(bool isUpdate);
void setPreinstallAttribution(string mediaSource, string campaign, string siteId);
bool isPreInstalledApp();
string getAttributionId();
void handlePushNotifications();
void validateAndSendInAppPurchase(string publicKey, string signature, string purchaseData, string price, string currency, Dictionary<string, string> additionalParameters, MonoBehaviour gameObject);
void validateAndSendInAppPurchase(AFPurchaseDetailsAndroid details, Dictionary<string, string> purchaseAdditionalDetails, MonoBehaviour gameObject);
void setCollectOaid(bool isCollect);
void setDisableAdvertisingIdentifiers(bool disable);
void setDisableNetworkData(bool disable);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cdf9d1bc41a8244b3bc2d249fb6cd7aa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,31 @@
namespace AppsFlyerSDK
{
public interface IAppsFlyerConversionData
{
/// <summary>
/// `conversionData` contains information about install. Organic/non-organic, etc.
/// <see>https://support.appsflyer.com/hc/en-us/articles/360000726098-Conversion-Data-Scenarios#Introduction</see>
/// </summary>
/// <param name="conversionData">JSON string of the returned conversion data.</param>
void onConversionDataSuccess(string conversionData);
/// <summary>
/// Any errors that occurred during the conversion request.
/// </summary>
/// <param name="error">A string describing the error.</param>
void onConversionDataFail(string error);
/// <summary>
/// `attributionData` contains information about OneLink, deeplink.
/// <see>https://support.appsflyer.com/hc/en-us/articles/208874366-OneLink-Deep-Linking-Guide#Intro</see>
/// </summary>
/// <param name="attributionData">JSON string of the returned deeplink data.</param>
void onAppOpenAttribution(string attributionData);
/// <summary>
/// Any errors that occurred during the attribution request.
/// </summary>
/// <param name="error">A string describing the error.</param>
void onAppOpenAttributionFailure(string error);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d2f1d4dadb7cb44628f25f1ffd8fc104
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,26 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace AppsFlyerSDK
{
public interface IAppsFlyerIOSBridge : IAppsFlyerNativeBridge
{
void setDisableCollectAppleAdSupport(bool disable);
void setShouldCollectDeviceName(bool shouldCollectDeviceName);
void setDisableCollectIAd(bool disableCollectIAd);
void setUseReceiptValidationSandbox(bool useReceiptValidationSandbox);
void setUseUninstallSandbox(bool useUninstallSandbox);
void validateAndSendInAppPurchase(string productIdentifier, string price, string currency, string transactionId, Dictionary<string, string> additionalParameters, MonoBehaviour gameObject);
void validateAndSendInAppPurchase(AFSDKPurchaseDetailsIOS details, Dictionary<string, string> purchaseAdditionalDetails, MonoBehaviour gameObject);
void registerUninstall(byte[] deviceToken);
void handleOpenUrl(string url, string sourceApplication, string annotation);
void waitForATTUserAuthorizationWithTimeoutInterval(int timeoutInterval);
void setCurrentDeviceLanguage(string language);
void disableSKAdNetwork(bool isDisabled);
void disableIDFVCollection(bool isDisabled);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6847fb337898040288c165e3667101a3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,76 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace AppsFlyerSDK
{
public interface IAppsFlyerNativeBridge
{
bool isInit { get; set; }
void startSDK(bool onRequestResponse, string CallBackObjectName);
void sendEvent(string eventName, Dictionary<string, string> eventValues, bool onInAppResponse, string CallBackObjectName);
void stopSDK(bool isSDKStopped);
bool isSDKStopped();
string getSdkVersion();
void setCustomerUserId(string id);
void setAppInviteOneLinkID(string oneLinkId);
void setAdditionalData(Dictionary<string, string> customData);
void setDeepLinkTimeout(long deepLinkTimeout);
void setResolveDeepLinkURLs(params string[] urls);
void setOneLinkCustomDomain(params string[] domains);
void setCurrencyCode(string currencyCode);
void recordLocation(double latitude, double longitude);
void anonymizeUser(bool shouldAnonymizeUser);
string getAppsFlyerId();
void enableTCFDataCollection(bool shouldCollectTcfData);
void setConsentData(AppsFlyerConsent appsFlyerConsent);
void logAdRevenue(AFAdRevenueData adRevenueData, Dictionary<string, string> additionalParameters);
void setMinTimeBetweenSessions(int seconds);
void setHost(string hostPrefixName, string hostName);
void setPhoneNumber(string phoneNumber);
void setSharingFilterForAllPartners();
void setSharingFilter(params string[] partners);
void getConversionData(string objectName);
void attributeAndOpenStore(string appID, string campaign, Dictionary<string, string> userParams, MonoBehaviour gameObject);
void recordCrossPromoteImpression(string appID, string campaign, Dictionary<string, string> parameters);
void generateUserInviteLink(Dictionary<string, string> parameters, MonoBehaviour gameObject);
void addPushNotificationDeepLinkPath(params string[] paths);
void setUserEmails(EmailCryptType cryptType, params string[] userEmails);
void subscribeForDeepLink(string objectName);
void setIsDebug(bool shouldEnable);
void setPartnerData(string partnerId, Dictionary<string, string> partnerInfo);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 409b8302434664a3785ce55d075e7f58
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
namespace AppsFlyerSDK
{
public interface IAppsFlyerPurchaseValidation
{
void didReceivePurchaseRevenueValidationInfo(string validationInfo);
void didReceivePurchaseRevenueError(string error);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7c60f499ae0d048b1be8ffd6878a184c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,26 @@
namespace AppsFlyerSDK
{
public interface IAppsFlyerUserInvite
{
/// <summary>
/// The success callback for generating OneLink URLs.
/// </summary>
/// <param name="link">A string of the newly created url.</param>
void onInviteLinkGenerated(string link);
/// <summary>
/// The error callback for generating OneLink URLs
/// </summary>
/// <param name="error">A string describing the error.</param>
void onInviteLinkGeneratedFailure(string error);
/// <summary>
/// (ios only) iOS allows you to utilize the StoreKit component to open
/// the App Store while remaining in the context of your app.
/// More details at <see>https://support.appsflyer.com/hc/en-us/articles/115004481946-Cross-Promotion-Tracking#tracking-cross-promotion-impressions</see>
/// </summary>
/// <param name="link">openStore callback Contains promoted `clickURL`</param>
void onOpenStoreLinkGenerated(string link);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5a4cdfa023cb8497b94bb39720052fef
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,19 @@
namespace AppsFlyerSDK
{
public interface IAppsFlyerValidateAndLog
{
/// <summary>
/// The success callback for validateAndSendInAppPurchase API.
/// For Android : the callback will return JSON string.
/// For iOS : the callback will return a JSON string from apples verifyReceipt API.
/// </summary>
/// <param name="result"></param>
void onValidateAndLogComplete(string result);
/// <summary>
/// The error callback for validateAndSendInAppPurchase API.
/// </summary>
/// <param name="error">A string describing the error.</param>
void onValidateAndLogFailure(string error);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1a5adb7eab3284dd39a76ec56c06457c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,19 @@
namespace AppsFlyerSDK
{
public interface IAppsFlyerValidateReceipt
{
/// <summary>
/// The success callback for validateAndSendInAppPurchase API.
/// For Android : the callback will return "Validate success".
/// For iOS : the callback will return a JSON string from apples verifyReceipt API.
/// </summary>
/// <param name="result"></param>
void didFinishValidateReceipt(string result);
/// <summary>
/// The error callback for validateAndSendInAppPurchase API.
/// </summary>
/// <param name="error">A string describing the error.</param>
void didFinishValidateReceiptWithError(string error);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6385b1d184efa400a98515735e1f17bc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 96a328019e42349aabc478b546b8605e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 682114f7790724ab3b9410e89bbc076c
folderAsset: yes
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Standalone: OSXUniversal
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>20G417</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>AppsFlyerBundle</string>
<key>CFBundleIdentifier</key>
<string>com.appsflyer.support.two.AppsFlyerBundle</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>AppsFlyerBundle</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string>13A1030d</string>
<key>DTPlatformName</key>
<string>macosx</string>
<key>DTPlatformVersion</key>
<string>12.0</string>
<key>DTSDKBuild</key>
<string>21A344</string>
<key>DTSDKName</key>
<string>macosx12.0</string>
<key>DTXcode</key>
<string>1310</string>
<key>DTXcodeBuild</key>
<string>13A1030d</string>
<key>LSMinimumSystemVersion</key>
<string>11.6</string>
<key>NSHumanReadableCopyright</key>
<string></string>
</dict>
</plist>

View File

@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict/>
<key>files2</key>
<dict/>
<key>rules</key>
<dict>
<key>^Resources/</key>
<true/>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^.*</key>
<true/>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^[^/]+$</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 66297743248ab4e47abdc371a59f1111
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d8325c12a80ff4323b82e2833a8fc287
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,79 @@
import Foundation
import StoreKit
#if canImport(PurchaseConnector)
import PurchaseConnector
@available(iOS 15.0, *)
@objc
public class AFUnityStoreKit2Bridge: NSObject {
@objc
public static func fetchAFSDKTransactionSK2(withTransactionId transactionId: String, completion: @escaping (AFSDKTransactionSK2?) -> Void) {
guard let transactionIdUInt64 = UInt64(transactionId) else {
print("Invalid transaction ID format.")
completion(nil)
return
}
Task {
for await result in StoreKit.Transaction.all {
if case .verified(let transaction) = result, transaction.id == transactionIdUInt64 {
let afTransaction = AFSDKTransactionSK2(transaction: transaction)
DispatchQueue.main.async {
completion(afTransaction)
}
return
}
}
DispatchQueue.main.async {
completion(nil)
}
}
}
@objc
public static func extractSK2ProductInfo(_ products: [AFSDKProductSK2]) -> NSArray {
var result: [[String: Any]] = []
for product in products {
if let swiftProduct = Mirror(reflecting: product).children.first(where: { $0.label == "product" })?.value {
let productId = (swiftProduct as? NSObject)?.value(forKey: "id") as? String ?? ""
let title = (swiftProduct as? NSObject)?.value(forKey: "displayName") as? String ?? ""
let desc = (swiftProduct as? NSObject)?.value(forKey: "description") as? String ?? ""
let price = (swiftProduct as? NSObject)?.value(forKey: "price") as? NSDecimalNumber ?? 0
result.append([
"productIdentifier": productId,
"localizedTitle": title,
"localizedDescription": desc,
"price": price
])
}
}
return result as NSArray
}
@objc
public static func extractSK2TransactionInfo(_ transactions: [AFSDKTransactionSK2]) -> NSArray {
var result: [[String: Any]] = []
for txn in transactions {
guard let mirrorChild = Mirror(reflecting: txn).children.first(where: { $0.label == "transaction" }),
let swiftTxn = mirrorChild.value as? StoreKit.Transaction else {
continue
}
let transactionId = "\(swiftTxn.id)"
let date = NSNumber(value: swiftTxn.purchaseDate.timeIntervalSince1970)
result.append([
"transactionIdentifier": transactionId,
"transactionState": "verified", // or skip this line
"transactionDate": date
])
}
return result as NSArray
}
}
#endif

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 5652805602a6b4273a6e527b00aea272

View File

@@ -0,0 +1,24 @@
//
// AFUnityUtils.h
//
// Created by Andrii H. and Dmitry O. on 16 Oct 2023
//
#if __has_include(<AppsFlyerLib/AppsFlyerLib.h>)
#import <AppsFlyerLib/AppsFlyerLib.h>
#else
#import "AppsFlyerLib.h"
#endif
static NSString* stringFromChar(const char *str);
static NSDictionary* dictionaryFromJson(const char *jsonString);
static const char* stringFromdictionary(NSDictionary* dictionary);
static NSArray<NSString*> *NSArrayFromCArray(int length, const char **arr);
static char* getCString(const char* string);
static AppsFlyerLinkGenerator* generatorFromDictionary(NSDictionary* dictionary, AppsFlyerLinkGenerator* generator);
static EmailCryptType emailCryptTypeFromInt(int emailCryptTypeInt);
static AppsFlyerAdRevenueMediationNetworkType mediationNetworkTypeFromInt(int mediationNetwork);
static NSNumber *intFromNullableBool(const char *cStr);
static NSString* stringFromDeepLinkResultStatus(AFSDKDeepLinkResultStatus deepLinkResult);
static NSString* stringFromDeepLinkResultError(AppsFlyerDeepLinkResult *result);

View File

@@ -0,0 +1,27 @@
fileFormatVersion: 2
guid: 4b0609ff467554f2088aee1c52bf54a2
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,219 @@
//
// AFUnityUtils.mm
// Unity-iPhone
//
// Created by Jonathan Wesfield on 24/07/2019.
//
#import "AFUnityUtils.h"
static NSString* stringFromChar(const char *str) {
return str ? [NSString stringWithUTF8String:str] : nil;
}
static NSDictionary* dictionaryFromJson(const char *jsonString) {
if(jsonString){
NSData *jsonData = [[NSData alloc] initWithBytes:jsonString length:strlen(jsonString)];
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:nil];
return dictionary;
}
return nil;
}
static const char* stringFromdictionary(NSDictionary* dictionary) {
if(dictionary){
NSError * err;
NSData * jsonData = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:&err];
NSString * myString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
return [myString UTF8String];
}
return nil;
}
static NSDictionary* dictionaryFromNSError(NSError* error) {
if(error){
NSMutableDictionary *errorDictionary = [NSMutableDictionary dictionary];
errorDictionary[@"code"] = @(error.code);
errorDictionary[@"localizedDescription"] = error.localizedDescription ?: @"";
// Include userInfo fields for enhanced error reporting (iOS SDK 6.17.8+)
if (error.userInfo[@"error_code"]) {
errorDictionary[@"error_code"] = error.userInfo[@"error_code"];
}
if (error.userInfo[@"error_message"]) {
errorDictionary[@"error_message"] = error.userInfo[@"error_message"];
}
if (error.userInfo[@"invalid_fields"]) {
errorDictionary[@"invalid_fields"] = error.userInfo[@"invalid_fields"];
}
return errorDictionary;
}
return nil;
}
static NSArray<NSString*> *NSArrayFromCArray(int length, const char **arr) {
NSMutableArray<NSString *> *res = [[NSMutableArray alloc] init];
for(int i = 0; i < length; i++) {
if (arr[i]) {
[res addObject:[NSString stringWithUTF8String:arr[i]]];
}
}
return res;
}
static char* getCString(const char* string){
if (string == NULL){
return NULL;
}
char* res = (char*)malloc(strlen(string) + 1);
strcpy(res, string);
return res;
}
static AppsFlyerLinkGenerator* generatorFromDictionary(NSDictionary* dictionary, AppsFlyerLinkGenerator* generator) {
NSArray* generatorKeys = @[@"channel", @"customerID", @"campaign", @"referrerName", @"referrerImageUrl", @"deeplinkPath", @"baseDeeplink", @"brandDomain"];
NSMutableDictionary* mutableDictionary = [dictionary mutableCopy];
[generator setChannel:[dictionary objectForKey: @"channel"]];
[generator setReferrerCustomerId:[dictionary objectForKey: @"customerID"]];
[generator setCampaign:[dictionary objectForKey: @"campaign"]];
[generator setReferrerName:[dictionary objectForKey: @"referrerName"]];
[generator setReferrerImageURL:[dictionary objectForKey: @"referrerImageUrl"]];
[generator setDeeplinkPath:[dictionary objectForKey: @"deeplinkPath"]];
[generator setBaseDeeplink:[dictionary objectForKey: @"baseDeeplink"]];
[generator setBrandDomain:[dictionary objectForKey: @"brandDomain"]];
[mutableDictionary removeObjectsForKeys:generatorKeys];
[generator addParameters:mutableDictionary];
return generator;
}
static EmailCryptType emailCryptTypeFromInt(int emailCryptTypeInt){
EmailCryptType emailCryptType;
switch (emailCryptTypeInt){
case 1:
emailCryptType = EmailCryptTypeSHA256;
break;
default:
emailCryptType = EmailCryptTypeNone;
break;
}
return emailCryptType;
}
static NSNumber *intFromNullableBool(const char *cStr) {
if (!cStr) return nil;
NSString *str = [NSString stringWithUTF8String:cStr];
if ([str caseInsensitiveCompare:@"true"] == NSOrderedSame) {
return @YES;
} else if ([str caseInsensitiveCompare:@"false"] == NSOrderedSame) {
return @NO;
}
return nil;
}
static AppsFlyerAdRevenueMediationNetworkType mediationNetworkTypeFromInt(int mediationNetworkInt){
AppsFlyerAdRevenueMediationNetworkType mediationNetworkType;
switch (mediationNetworkInt){
case 1:
mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeGoogleAdMob;
break;
case 2:
mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeIronSource;
break;
case 3:
mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeApplovinMax;
break;
case 4:
mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeFyber;
break;
case 5:
mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeAppodeal;
break;
case 6:
mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeAdmost;
break;
case 7:
mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeTopon;
break;
case 8:
mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeTradplus;
break;
case 9:
mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeYandex;
break;
case 10:
mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeChartBoost;
break;
case 11:
mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeUnity;
break;
case 12:
mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeToponPte;
break;
case 13:
mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeCustom;
break;
case 14:
mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeDirectMonetization;
break;
default:
mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeCustom;
break;
}
return mediationNetworkType;
}
static NSString* stringFromDeepLinkResultStatus(AFSDKDeepLinkResultStatus deepLinkResult){
NSString* result;
switch (deepLinkResult){
case AFSDKDeepLinkResultStatusFound:
result = @"FOUND";
break;
case AFSDKDeepLinkResultStatusFailure:
result = @"ERROR";
break;
case AFSDKDeepLinkResultStatusNotFound:
result = @"NOT_FOUND";
break;
default:
result = @"ERROR";
break;
}
return result;
}
static NSString* stringFromDeepLinkResultError(AppsFlyerDeepLinkResult *result){
NSString* res;
if (result && result.error){
if ([[result.error userInfo][NSUnderlyingErrorKey] code] == -1001) {
res = @"TIMEOUT";
} else if ([[result.error userInfo][NSUnderlyingErrorKey] code] == -1009) {
res = @"NETWORK";
}
}
res = @"UNKNOWN";
return res;
}

View File

@@ -0,0 +1,37 @@
fileFormatVersion: 2
guid: 18a03931864e84d86bedcc99c440e060
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
iPhone: iOS
second:
enabled: 1
settings: {}
- first:
tvOS: tvOS
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,164 @@
//
// AppsFlyer+AppController.m
// Unity-iPhone
//
// Created by Jonathan Wesfield on 24/07/2019.
//
#import <objc/runtime.h>
#import "UnityAppController.h"
#import "AppsFlyeriOSWrapper.h"
#if __has_include(<AppsFlyerLib/AppsFlyerLib.h>)
#import <AppsFlyerLib/AppsFlyerLib.h>
#else
#import "AppsFlyerLib.h"
#endif
@implementation UnityAppController (AppsFlyerSwizzledAppController)
static BOOL didEnteredBackGround __unused;
static IMP __original_applicationDidBecomeActive_Imp __unused;
static IMP __original_applicationDidEnterBackground_Imp __unused;
static IMP __original_didReceiveRemoteNotification_Imp __unused;
static IMP __original_continueUserActivity_Imp __unused;
static IMP __original_openUrl_Imp __unused;
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
#if !AFSDK_SHOULD_SWIZZLE
id swizzleFlag = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"AppsFlyerShouldSwizzle"];
BOOL shouldSwizzle = swizzleFlag ? [swizzleFlag boolValue] : NO;
if(shouldSwizzle){
Method method1 = class_getInstanceMethod([self class], @selector(applicationDidBecomeActive:));
__original_applicationDidBecomeActive_Imp = method_setImplementation(method1, (IMP)__swizzled_applicationDidBecomeActive);
Method method2 = class_getInstanceMethod([self class], @selector(applicationDidEnterBackground:));
__original_applicationDidEnterBackground_Imp = method_setImplementation(method2, (IMP)__swizzled_applicationDidEnterBackground);
Method method3 = class_getInstanceMethod([self class], @selector(didReceiveRemoteNotification:));
__original_didReceiveRemoteNotification_Imp = method_setImplementation(method3, (IMP)__swizzled_didReceiveRemoteNotification);
Method method4 = class_getInstanceMethod([self class], @selector(application:openURL:options:));
__original_openUrl_Imp = method_setImplementation(method4, (IMP)__swizzled_openURL);
if (_AppsFlyerdelegate == nil) {
_AppsFlyerdelegate = [[AppsFlyeriOSWarpper alloc] init];
}
[self swizzleContinueUserActivity:[self class]];
}
#elif AFSDK_SHOULD_SWIZZLE
Method method1 = class_getInstanceMethod([self class], @selector(applicationDidBecomeActive:));
__original_applicationDidBecomeActive_Imp = method_setImplementation(method1, (IMP)__swizzled_applicationDidBecomeActive);
Method method2 = class_getInstanceMethod([self class], @selector(applicationDidEnterBackground:));
__original_applicationDidEnterBackground_Imp = method_setImplementation(method2, (IMP)__swizzled_applicationDidEnterBackground);
Method method3 = class_getInstanceMethod([self class], @selector(didReceiveRemoteNotification:));
__original_didReceiveRemoteNotification_Imp = method_setImplementation(method3, (IMP)__swizzled_didReceiveRemoteNotification);
Method method4 = class_getInstanceMethod([self class], @selector(application:openURL:options:));
__original_openUrl_Imp = method_setImplementation(method4, (IMP)__swizzled_openURL);
if (_AppsFlyerdelegate == nil) {
_AppsFlyerdelegate = [[AppsFlyeriOSWarpper alloc] init];
}
[self swizzleContinueUserActivity:[self class]];
#endif
});
}
+(void)swizzleContinueUserActivity:(Class)class {
SEL originalSelector = @selector(application:continueUserActivity:restorationHandler:);
Method defaultMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, @selector(__swizzled_continueUserActivity));
BOOL isMethodExists = !class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
if (isMethodExists) {
__original_continueUserActivity_Imp = method_setImplementation(defaultMethod, (IMP)__swizzled_continueUserActivity);
} else {
class_replaceMethod(class, originalSelector, (IMP)__swizzled_continueUserActivity, method_getTypeEncoding(swizzledMethod));
}
}
BOOL __swizzled_continueUserActivity(id self, SEL _cmd, UIApplication* application, NSUserActivity* userActivity, void (^restorationHandler)(NSArray*)) {
NSLog(@"swizzled continueUserActivity");
[[AppsFlyerAttribution shared] continueUserActivity:userActivity restorationHandler:restorationHandler];
if(__original_continueUserActivity_Imp){
return ((BOOL(*)(id, SEL, UIApplication*, NSUserActivity*, void (^)(NSArray*)))__original_continueUserActivity_Imp)(self, _cmd, application, userActivity, NULL);
}
return YES;
}
void __swizzled_applicationDidBecomeActive(id self, SEL _cmd, UIApplication* launchOptions) {
NSLog(@"swizzled applicationDidBecomeActive");
[[AppsFlyerLib shared] setDelegate:_AppsFlyerdelegate];
if(didEnteredBackGround && AppsFlyeriOSWarpper.didCallStart == YES){
[[AppsFlyerLib shared] start];
}
if(__original_applicationDidBecomeActive_Imp){
((void(*)(id,SEL, UIApplication*))__original_applicationDidBecomeActive_Imp)(self, _cmd, launchOptions);
}
}
void __swizzled_applicationDidEnterBackground(id self, SEL _cmd, UIApplication* application) {
NSLog(@"swizzled applicationDidEnterBackground");
didEnteredBackGround = YES;
if(__original_applicationDidEnterBackground_Imp){
((void(*)(id,SEL, UIApplication*))__original_applicationDidEnterBackground_Imp)(self, _cmd, application);
}
}
BOOL __swizzled_didReceiveRemoteNotification(id self, SEL _cmd, UIApplication* application, NSDictionary* userInfo,void (^UIBackgroundFetchResult)(void) ) {
NSLog(@"swizzled didReceiveRemoteNotification");
[[AppsFlyerLib shared] handlePushNotification:userInfo];
if(__original_didReceiveRemoteNotification_Imp){
return ((BOOL(*)(id, SEL, UIApplication*, NSDictionary*, int(UIBackgroundFetchResult)))__original_didReceiveRemoteNotification_Imp)(self, _cmd, application, userInfo, nil);
}
return YES;
}
BOOL __swizzled_openURL(id self, SEL _cmd, UIApplication* application, NSURL* url, NSDictionary * options) {
NSLog(@"swizzled openURL");
[[AppsFlyerAttribution shared] handleOpenUrl:url options:options];
if(__original_openUrl_Imp){
return ((BOOL(*)(id, SEL, UIApplication*, NSURL*, NSDictionary*))__original_openUrl_Imp)(self, _cmd, application, url, options);
}
return NO;
}
@end

View File

@@ -0,0 +1,37 @@
fileFormatVersion: 2
guid: 6ae9e1f7daef2427588fab2fbf8d35d5
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
iPhone: iOS
second:
enabled: 1
settings: {}
- first:
tvOS: tvOS
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,131 @@
//
// AppsFlyerAppController.mm
// Unity-iPhone
//
// Created by Jonathan Wesfield on 30/07/2019.
//
#import <Foundation/Foundation.h>
#import "UnityAppController.h"
#import "AppDelegateListener.h"
#import "AppsFlyeriOSWrapper.h"
#if __has_include(<AppsFlyerLib/AppsFlyerLib.h>)
#import <AppsFlyerLib/AppsFlyerLib.h>
#else
#import "AppsFlyerLib.h"
#endif
#import <objc/message.h>
/**
Note if you would like to use method swizzeling see AppsFlyer+AppController.m
If you are using swizzeling then comment out the method that is being swizzeled in AppsFlyerAppController.mm
Only use swizzeling if there are conflicts with other plugins that needs to be resolved.
*/
@interface AppsFlyerAppController : UnityAppController <AppDelegateListener>
{
BOOL didEnteredBackGround;
}
@end
@implementation AppsFlyerAppController
- (instancetype)init
{
self = [super init];
if (self) {
id swizzleFlag = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"AppsFlyerShouldSwizzle"];
BOOL shouldSwizzle = swizzleFlag ? [swizzleFlag boolValue] : NO;
if(!shouldSwizzle){
UnityRegisterAppDelegateListener(self);
}
}
return self;
}
- (void)didFinishLaunching:(NSNotification*)notification {
NSLog(@"got didFinishLaunching = %@",notification.userInfo);
if (_AppsFlyerdelegate == nil) {
_AppsFlyerdelegate = [[AppsFlyeriOSWarpper alloc] init];
}
[[AppsFlyerLib shared] setDelegate:_AppsFlyerdelegate];
if (notification.userInfo[@"url"]) {
[self onOpenURL:notification];
}
}
-(void)didBecomeActive:(NSNotification*)notification {
NSLog(@"got didBecomeActive(out) = %@", notification.userInfo);
if (didEnteredBackGround == YES && AppsFlyeriOSWarpper.didCallStart == YES) {
[[AppsFlyerLib shared] start];
didEnteredBackGround = NO;
}
}
- (void)didEnterBackground:(NSNotification*)notification {
NSLog(@"got didEnterBackground = %@", notification.userInfo);
didEnteredBackGround = YES;
}
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler {
[[AppsFlyerAttribution shared] continueUserActivity:userActivity restorationHandler:restorationHandler];
return YES;
}
- (void)onOpenURL:(NSNotification*)notification {
NSLog(@"got onOpenURL = %@", notification.userInfo);
NSURL *url = notification.userInfo[@"url"];
NSString *sourceApplication = notification.userInfo[@"sourceApplication"];
if (sourceApplication == nil) {
sourceApplication = @"";
}
if (url != nil) {
[[AppsFlyerAttribution shared] handleOpenUrl:url sourceApplication:sourceApplication annotation:nil];
}
}
- (void)didReceiveRemoteNotification:(NSNotification*)notification {
NSLog(@"got didReceiveRemoteNotification = %@", notification.userInfo);
[[AppsFlyerLib shared] handlePushNotification:notification.userInfo];
}
@end
#if !(AFSDK_SHOULD_SWIZZLE)
IMPL_APP_CONTROLLER_SUBCLASS(AppsFlyerAppController)
#endif
/**
Note if you would not like to use IMPL_APP_CONTROLLER_SUBCLASS you can replace it with the code below.
<code>
+(void)load
{
[AppsFlyerAppController plugin];
}
// Singleton accessor.
+ (AppsFlyerAppController *)plugin
{
static AppsFlyerAppController *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[AppsFlyerAppController alloc] init];
});
return sharedInstance;
}
</code>
**/

View File

@@ -0,0 +1,37 @@
fileFormatVersion: 2
guid: 2d1497a1493b24fecaa58bd3a7b707f9
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
iPhone: iOS
second:
enabled: 1
settings: {}
- first:
tvOS: tvOS
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,34 @@
//
// AppsFlyerAttribution.h
// UnityFramework
//
// Created by Margot Guetta on 11/04/2021.
//
#ifndef AppsFlyerAttribution_h
#define AppsFlyerAttribution_h
#endif /* AppsFlyerAttribution_h */
#if __has_include(<AppsFlyerLib/AppsFlyerLib.h>)
#import <AppsFlyerLib/AppsFlyerLib.h>
#else
#import "AppsFlyerLib.h"
#endif
@interface AppsFlyerAttribution : NSObject
@property NSUserActivity*_Nullable userActivity;
@property (nonatomic, copy) void (^ _Nullable restorationHandler)(NSArray *_Nullable );
@property NSURL * _Nullable url;
@property NSDictionary * _Nullable options;
@property NSString* _Nullable sourceApplication;
@property id _Nullable annotation;
@property BOOL isBridgeReady;
+ (AppsFlyerAttribution *_Nullable)shared;
- (void) continueUserActivity: (NSUserActivity*_Nullable) userActivity restorationHandler: (void (^_Nullable)(NSArray * _Nullable))restorationHandler;
- (void) handleOpenUrl:(NSURL*_Nullable)url options:(NSDictionary*_Nullable) options;
- (void) handleOpenUrl: (NSURL *_Nonnull)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation;
@end
static NSString * _Nullable const AF_BRIDGE_SET = @"bridge is set";

View File

@@ -0,0 +1,27 @@
fileFormatVersion: 2
guid: 8544dc3b3c7bb40d397b2de568df1058
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,86 @@
//
// NSObject+AppsFlyerAttribution.m
// UnityFramework
//
// Created by Margot Guetta on 11/04/2021.
//
#import <Foundation/Foundation.h>
#import "AppsFlyerAttribution.h"
@implementation AppsFlyerAttribution
+ (id)shared {
static AppsFlyerAttribution *shared = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shared = [[self alloc] init];
});
return shared;
}
- (id)init {
if (self = [super init]) {
self.options = nil;
self.restorationHandler = nil;
self.url = nil;
self.userActivity = nil;
self.annotation = nil;
self.sourceApplication = nil;
self.isBridgeReady = NO;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(receiveBridgeReadyNotification:)
name:AF_BRIDGE_SET
object:nil];
}
return self;
}
- (void) continueUserActivity: (NSUserActivity*_Nullable) userActivity restorationHandler: (void (^_Nullable)(NSArray * _Nullable))restorationHandler{
if(self.isBridgeReady == YES){
[[AppsFlyerLib shared] continueUserActivity:userActivity restorationHandler:restorationHandler];
}else{
[AppsFlyerAttribution shared].userActivity = userActivity;
[AppsFlyerAttribution shared].restorationHandler = restorationHandler;
}
}
- (void) handleOpenUrl:(NSURL *)url options:(NSDictionary *)options{
if(self.isBridgeReady == YES){
[[AppsFlyerLib shared] handleOpenUrl:url options:options];
}else{
[AppsFlyerAttribution shared].url = url;
[AppsFlyerAttribution shared].options = options;
}
}
- (void) handleOpenUrl:(NSURL *)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation{
if(self.isBridgeReady == YES){
[[AppsFlyerLib shared] handleOpenURL:url sourceApplication:sourceApplication withAnnotation:annotation];
}else{
[AppsFlyerAttribution shared].url = url;
[AppsFlyerAttribution shared].sourceApplication = sourceApplication;
[AppsFlyerAttribution shared].annotation = annotation;
}
}
- (void) receiveBridgeReadyNotification:(NSNotification *) notification
{
NSLog (@"AppsFlyer Debug: handle deep link");
if(self.url && self.sourceApplication){
[[AppsFlyerLib shared] handleOpenURL:self.url sourceApplication:self.sourceApplication withAnnotation:self.annotation];
self.url = nil;
self.sourceApplication = nil;
self.annotation = nil;
}else if(self.options && self.url){
[[AppsFlyerLib shared] handleOpenUrl:self.url options:self.options];
self.options = nil;
self.url = nil;
}else if(self.userActivity){
[[AppsFlyerLib shared] continueUserActivity:self.userActivity restorationHandler:nil];
self.userActivity = nil;
self.restorationHandler = nil;
}
}
@end

View File

@@ -0,0 +1,37 @@
fileFormatVersion: 2
guid: 1060e47d7b9e2453ba575f0b455b2bf8
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
iPhone: iOS
second:
enabled: 1
settings: {}
- first:
tvOS: tvOS
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,71 @@
//
// AppsFlyeriOSWarpper.h
// Unity-iPhone
//
// Created by Jonathan Wesfield on 24/07/2019.
//
#import "AFUnityUtils.mm"
#import "UnityAppController.h"
#import "AppsFlyerAttribution.h"
#if __has_include(<AppsFlyerLib/AppsFlyerLib.h>)
#import <AppsFlyerLib/AppsFlyerLib.h>
#else
#import "AppsFlyerLib.h"
#endif
#if __has_include(<PurchaseConnector/PurchaseConnector.h>)
#import <PurchaseConnector/PurchaseConnector.h>
#else
#import "PurchaseConnector.h"
#endif
#import <PurchaseConnector/PurchaseConnector-Swift.h>
// Add StoreKit 2 support
#if __has_include(<StoreKit/StoreKit.h>)
#import <StoreKit/StoreKit.h>
#endif
@interface AppsFlyeriOSWarpper : NSObject <AppsFlyerLibDelegate, AppsFlyerDeepLinkDelegate, AppsFlyerPurchaseRevenueDelegate, AppsFlyerPurchaseRevenueDataSource, AppsFlyerPurchaseRevenueDataSourceStoreKit2>
+ (BOOL) didCallStart;
+ (void) setDidCallStart:(BOOL)val;
// Add StoreKit 2 methods
- (void)setStoreKitVersion:(int)storeKitVersion;
- (void)logConsumableTransaction:(id)transaction;
@end
static AppsFlyeriOSWarpper *_AppsFlyerdelegate;
static const int kPushNotificationSize = 32;
static NSString* ConversionDataCallbackObject = @"AppsFlyerObject";
static const char* VALIDATE_CALLBACK = "didFinishValidateReceipt";
static const char* VALIDATE_ERROR_CALLBACK = "didFinishValidateReceiptWithError";
static const char* GCD_CALLBACK = "onConversionDataSuccess";
static const char* GCD_ERROR_CALLBACK = "onConversionDataFail";
static const char* OAOA_CALLBACK = "onAppOpenAttribution";
static const char* OAOA_ERROR_CALLBACK = "onAppOpenAttributionFailure";
static const char* GENERATE_LINK_CALLBACK = "onInviteLinkGenerated";
static const char* OPEN_STORE_LINK_CALLBACK = "onOpenStoreLinkGenerated";
static const char* START_REQUEST_CALLBACK = "requestResponseReceived";
static const char* IN_APP_RESPONSE_CALLBACK = "inAppResponseReceived";
static const char* ON_DEEPLINKING = "onDeepLinking";
static const char* VALIDATE_AND_LOG_V2_CALLBACK = "onValidateAndLogComplete";
static const char* VALIDATE_AND_LOG_V2_ERROR_CALLBACK = "onValidateAndLogFailure";
static NSString* validateObjectName = @"";
static NSString* openStoreObjectName = @"";
static NSString* generateInviteObjectName = @"";
static NSString* validateAndLogObjectName = @"";
static NSString* startRequestObjectName = @"";
static NSString* inAppRequestObjectName = @"";
static NSString* onDeeplinkingObjectName = @"";
static const char* PURCHASE_REVENUE_VALIDATION_CALLBACK = "didReceivePurchaseRevenueValidationInfo";
static const char* PURCHASE_REVENUE_ERROR_CALLBACK = "didReceivePurchaseRevenueError";
static NSString* onPurchaseValidationObjectName = @"";

View File

@@ -0,0 +1,27 @@
fileFormatVersion: 2
guid: 147104b04b5794eaa92b4195cc328e13
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,601 @@
//
// AppsFlyeriOSWarpper.mm
// Unity-iPhone
//
// Created by Jonathan Wesfield on 24/07/2019.
//
#import "AppsFlyeriOSWrapper.h"
#import <objc/runtime.h>
#import <StoreKit/StoreKit.h>
#import "UnityFramework/UnityFramework-Swift.h"
#if __has_include(<PurchaseConnector/PurchaseConnector-Swift.h>)
#import <PurchaseConnector/PurchaseConnector-Swift.h>
#elif __has_include("PurchaseConnector-Swift.h")
#import "PurchaseConnector-Swift.h"
#endif
#if __has_include(<UnityFramework/UnityFramework-Swift.h>)
#import <UnityFramework/UnityFramework-Swift.h>
#elif __has_include("UnityFramework-Swift.h")
#import "UnityFramework-Swift.h"
#endif
static void unityCallBack(NSString* objectName, const char* method, const char* msg) {
if(objectName){
UnitySendMessage([objectName UTF8String], method, msg);
}
}
extern "C" {
const void _startSDK(bool shouldCallback, const char* objectName) {
[[AppsFlyerLib shared] setPluginInfoWith: AFSDKPluginUnity
pluginVersion:@"6.17.91"
additionalParams:nil];
startRequestObjectName = stringFromChar(objectName);
AppsFlyeriOSWarpper.didCallStart = YES;
[AppsFlyerAttribution shared].isBridgeReady = YES;
[[NSNotificationCenter defaultCenter] postNotificationName:AF_BRIDGE_SET object: [AppsFlyerAttribution shared]];
[[AppsFlyerLib shared] startWithCompletionHandler:^(NSDictionary<NSString *,id> *dictionary, NSError *error) {
if(shouldCallback){
if (error) {
NSDictionary *callbackDictionary = @{@"statusCode":[NSNumber numberWithLong:[error code]]};
unityCallBack(startRequestObjectName, START_REQUEST_CALLBACK, stringFromdictionary(callbackDictionary));
return;
}
if (dictionary) {
unityCallBack(startRequestObjectName, START_REQUEST_CALLBACK, stringFromdictionary(dictionary));
return;
}
}
}];
}
const void _setCustomerUserID (const char* customerUserID) {
[[AppsFlyerLib shared] setCustomerUserID:stringFromChar(customerUserID)];
}
const void _setAdditionalData (const char* customData) {
[[AppsFlyerLib shared] setAdditionalData:dictionaryFromJson(customData)];
}
const void _setAppsFlyerDevKey (const char* appsFlyerDevKey) {
[AppsFlyerLib shared].appsFlyerDevKey = stringFromChar(appsFlyerDevKey);
}
const void _setAppleAppID (const char* appleAppID) {
[AppsFlyerLib shared].appleAppID = stringFromChar(appleAppID);
}
const void _setCurrencyCode (const char* currencyCode) {
[[AppsFlyerLib shared] setCurrencyCode:stringFromChar(currencyCode)];
}
const void _setDisableCollectAppleAdSupport (bool disableAdvertisingIdentifier) {
//[AppsFlyerLib shared].disableAdvertisingIdentifier = disableAdvertisingIdentifier;
}
const void _setIsDebug (bool isDebug) {
[AppsFlyerLib shared].isDebug = isDebug;
}
const void _setShouldCollectDeviceName (bool shouldCollectDeviceName) {
[AppsFlyerLib shared].shouldCollectDeviceName = shouldCollectDeviceName;
}
const void _setAppInviteOneLinkID (const char* appInviteOneLinkID) {
[[AppsFlyerLib shared] setAppInviteOneLink:stringFromChar(appInviteOneLinkID)];
}
const void _setDeepLinkTimeout (long deepLinkTimeout) {
[AppsFlyerLib shared].deepLinkTimeout = deepLinkTimeout;
}
const void _anonymizeUser (bool anonymizeUser) {
[AppsFlyerLib shared].anonymizeUser = anonymizeUser;
}
const void _enableTCFDataCollection (bool shouldCollectTcfData) {
[[AppsFlyerLib shared] enableTCFDataCollection:shouldCollectTcfData];
}
const void _setConsentData(const char* isUserSubjectToGDPR, const char* hasConsentForDataUsage, const char* hasConsentForAdsPersonalization, const char* hasConsentForAdStorage) {
NSNumber *gdpr = intFromNullableBool(isUserSubjectToGDPR);
NSNumber *dataUsage = intFromNullableBool(hasConsentForDataUsage);
NSNumber *adsPersonalization = intFromNullableBool(hasConsentForAdsPersonalization);
NSNumber *adStorage = intFromNullableBool(hasConsentForAdStorage);
AppsFlyerConsent *consentData = [[AppsFlyerConsent alloc] initWithIsUserSubjectToGDPR:gdpr
hasConsentForDataUsage:dataUsage
hasConsentForAdsPersonalization:adsPersonalization
hasConsentForAdStorage:adStorage];
[[AppsFlyerLib shared] setConsentData:consentData];
}
const void _logAdRevenue(const char* monetizationNetwork, int mediationNetworkInt, const char* currencyIso4217Code, double eventRevenue, const char* additionalParameters) {
AppsFlyerAdRevenueMediationNetworkType mediationNetwork = mediationNetworkTypeFromInt(mediationNetworkInt);
NSNumber *number = [NSNumber numberWithDouble:eventRevenue];
AFAdRevenueData *adRevenue = [[AFAdRevenueData alloc] initWithMonetizationNetwork:stringFromChar(monetizationNetwork) mediationNetwork:mediationNetwork currencyIso4217Code:stringFromChar(currencyIso4217Code) eventRevenue:number];
[[AppsFlyerLib shared] logAdRevenue: adRevenue additionalParameters:dictionaryFromJson(additionalParameters)];
}
const void _setDisableCollectIAd (bool disableCollectASA) {
[AppsFlyerLib shared].disableCollectASA = disableCollectASA;
}
const void _setUseReceiptValidationSandbox (bool useReceiptValidationSandbox) {
[AppsFlyerLib shared].useReceiptValidationSandbox = useReceiptValidationSandbox;
}
const void _setUseUninstallSandbox (bool useUninstallSandbox) {
[AppsFlyerLib shared].useUninstallSandbox = useUninstallSandbox;
}
const void _setResolveDeepLinkURLs (int length, const char **resolveDeepLinkURLs) {
if(length > 0 && resolveDeepLinkURLs) {
[[AppsFlyerLib shared] setResolveDeepLinkURLs:NSArrayFromCArray(length, resolveDeepLinkURLs)];
}
}
const void _setOneLinkCustomDomains (int length, const char **oneLinkCustomDomains) {
if(length > 0 && oneLinkCustomDomains) {
[[AppsFlyerLib shared] setOneLinkCustomDomains:NSArrayFromCArray(length, oneLinkCustomDomains)];
}
}
const void _afSendEvent (const char* eventName, const char* eventValues, bool shouldCallback, const char* objectName) {
inAppRequestObjectName = stringFromChar(objectName);
[[AppsFlyerLib shared] logEventWithEventName:stringFromChar(eventName) eventValues:dictionaryFromJson(eventValues) completionHandler:^(NSDictionary<NSString *,id> *dictionary, NSError *error) {
if(shouldCallback){
if (error) {
NSDictionary *callbackDictionary = @{@"statusCode":[NSNumber numberWithLong:[error code]]};
unityCallBack(inAppRequestObjectName, IN_APP_RESPONSE_CALLBACK, stringFromdictionary(callbackDictionary));
return;
}
if (dictionary) {
unityCallBack(inAppRequestObjectName, IN_APP_RESPONSE_CALLBACK, stringFromdictionary(dictionary));
return;
}
}
}];
}
const void _recordLocation (double longitude, double latitude) {
[[AppsFlyerLib shared] logLocation:longitude latitude:latitude];
}
const char* _getAppsFlyerId () {
return getCString([[[AppsFlyerLib shared] getAppsFlyerUID] UTF8String]);
}
const void _registerUninstall (unsigned char* deviceToken) {
if(deviceToken){
NSData* tokenData = [NSData dataWithBytes:(const void *)deviceToken length:sizeof(unsigned char)*kPushNotificationSize];
[[AppsFlyerLib shared] registerUninstall:tokenData];
}
}
const void _handlePushNotification (const char* pushPayload) {
[[AppsFlyerLib shared] handlePushNotification:dictionaryFromJson(pushPayload)];
}
const char* _getSDKVersion () {
return getCString([[[AppsFlyerLib shared] getSDKVersion] UTF8String]);
}
const void _setHost (const char* host, const char* hostPrefix) {
[[AppsFlyerLib shared] setHost:stringFromChar(host) withHostPrefix:stringFromChar(hostPrefix)];
}
const void _setMinTimeBetweenSessions (int minTimeBetweenSessions) {
[AppsFlyerLib shared].minTimeBetweenSessions = minTimeBetweenSessions;
}
const void _stopSDK (bool isStopped) {
[AppsFlyerLib shared].isStopped = isStopped;
}
const BOOL _isSDKStopped () {
return [AppsFlyerLib shared].isStopped;
}
const void _handleOpenUrl(const char *url, const char *sourceApplication, const char *annotation) {
[[AppsFlyerLib shared] handleOpenURL:[NSURL URLWithString:stringFromChar(url)] sourceApplication:stringFromChar(sourceApplication) withAnnotation:stringFromChar(annotation)]; }
const void _recordCrossPromoteImpression (const char* appID, const char* campaign, const char* parameters) {
[AppsFlyerCrossPromotionHelper logCrossPromoteImpression:stringFromChar(appID) campaign:stringFromChar(campaign) parameters:dictionaryFromJson(parameters)]; }
const void _attributeAndOpenStore (const char* appID, const char* campaign, const char* parameters, const char* objectName) {
openStoreObjectName = stringFromChar(objectName);
[AppsFlyerCrossPromotionHelper
logAndOpenStore:stringFromChar(appID)
campaign:stringFromChar(campaign)
parameters:dictionaryFromJson(parameters)
openStore:^(NSURLSession * _Nonnull urlSession, NSURL * _Nonnull clickURL) {
unityCallBack(openStoreObjectName, OPEN_STORE_LINK_CALLBACK, [clickURL.absoluteString UTF8String]);
}];
}
const void _generateUserInviteLink (const char* parameters, const char* objectName) {
generateInviteObjectName = stringFromChar(objectName);
[AppsFlyerShareInviteHelper generateInviteUrlWithLinkGenerator:^AppsFlyerLinkGenerator * _Nonnull(AppsFlyerLinkGenerator * _Nonnull generator) {
return generatorFromDictionary(dictionaryFromJson(parameters), generator);
} completionHandler:^(NSURL * _Nullable url) {
unityCallBack(generateInviteObjectName, GENERATE_LINK_CALLBACK, [url.absoluteString UTF8String]);
}];
}
const void _recordInvite (const char* channel, const char* parameters) {
[AppsFlyerShareInviteHelper logInvite:stringFromChar(channel) parameters:dictionaryFromJson(parameters)];
}
const void _setUserEmails (int emailCryptTypeInt , int length, const char **userEmails) {
if(length > 0 && userEmails) {
[[AppsFlyerLib shared] setUserEmails:NSArrayFromCArray(length, userEmails) withCryptType:emailCryptTypeFromInt(emailCryptTypeInt)];
}
}
const void _setPhoneNumber (const char* phoneNumber) {
[[AppsFlyerLib shared] setPhoneNumber:stringFromChar(phoneNumber)];
}
const void _setSharingFilterForAllPartners () {
[[AppsFlyerLib shared] setSharingFilterForAllPartners];
}
const void _setSharingFilter (int length, const char **partners) {
if(length > 0 && partners) {
[[AppsFlyerLib shared] setSharingFilter:NSArrayFromCArray(length, partners)];
}
}
const void _setSharingFilterForPartners (int length, const char **partners) {
if(length > 0 && partners) {
[[AppsFlyerLib shared] setSharingFilterForPartners:NSArrayFromCArray(length, partners)];
} else {
[[AppsFlyerLib shared] setSharingFilterForPartners:nil];
}
}
const void _validateAndSendInAppPurchase (const char* productIdentifier, const char* price, const char* currency, const char* transactionId, const char* additionalParameters, const char* objectName) {
validateObjectName = stringFromChar(objectName);
[[AppsFlyerLib shared]
validateAndLogInAppPurchase:stringFromChar(productIdentifier)
price:stringFromChar(price)
currency:stringFromChar(currency)
transactionId:stringFromChar(transactionId)
additionalParameters:dictionaryFromJson(additionalParameters)
success:^(NSDictionary *result){
unityCallBack(validateObjectName, VALIDATE_CALLBACK, stringFromdictionary(result));
} failure:^(NSError *error, id response) {
if(response && [response isKindOfClass:[NSDictionary class]]) {
NSDictionary* value = (NSDictionary*)response;
unityCallBack(validateObjectName, VALIDATE_ERROR_CALLBACK, stringFromdictionary(value));
} else {
unityCallBack(validateObjectName, VALIDATE_ERROR_CALLBACK, error ? [[error localizedDescription] UTF8String] : "error");
}
}];
}
const void _validateAndSendInAppPurchaseV2 (const char* product, const char* transactionId, int purchaseType, const char* purchaseAdditionalDetails, const char* objectName) {
validateAndLogObjectName = stringFromChar(objectName);
AFSDKPurchaseDetails *details = [[AFSDKPurchaseDetails alloc] initWithProductId:stringFromChar(product) transactionId:stringFromChar(transactionId) purchaseType:(AFSDKPurchaseType)purchaseType];
[[AppsFlyerLib shared]
validateAndLogInAppPurchase:details
purchaseAdditionalDetails:dictionaryFromJson(purchaseAdditionalDetails)
completion:^(NSDictionary * _Nullable response, NSError * _Nullable error) {
if (error) {
unityCallBack(validateAndLogObjectName, VALIDATE_AND_LOG_V2_ERROR_CALLBACK, stringFromdictionary(dictionaryFromNSError(error)));
} else {
unityCallBack(validateAndLogObjectName, VALIDATE_AND_LOG_V2_CALLBACK, stringFromdictionary(response));
}
}];
}
const void _getConversionData(const char* objectName) {
if (_AppsFlyerdelegate == nil) {
_AppsFlyerdelegate = [[AppsFlyeriOSWarpper alloc] init];
}
ConversionDataCallbackObject = stringFromChar(objectName);
[[AppsFlyerLib shared] setDelegate:_AppsFlyerdelegate];
}
const void _waitForATTUserAuthorizationWithTimeoutInterval (int timeoutInterval) {
//[[AppsFlyerLib shared] waitForATTUserAuthorizationWithTimeoutInterval:timeoutInterval];
}
const void _disableSKAdNetwork (bool isDisabled) {
[AppsFlyerLib shared].disableSKAdNetwork = isDisabled;
}
const void _addPushNotificationDeepLinkPath (int length, const char **paths) {
if(length > 0 && paths) {
[[AppsFlyerLib shared] addPushNotificationDeepLinkPath:NSArrayFromCArray(length, paths)];
}
}
const void _subscribeForDeepLink (const char* objectName) {
onDeeplinkingObjectName = stringFromChar(objectName);
if (_AppsFlyerdelegate == nil) {
_AppsFlyerdelegate = [[AppsFlyeriOSWarpper alloc] init];
}
[[AppsFlyerLib shared] setDeepLinkDelegate:_AppsFlyerdelegate];
}
const void _setCurrentDeviceLanguage(const char* language) {
[[AppsFlyerLib shared] setCurrentDeviceLanguage:stringFromChar(language)];
}
const void _setPartnerData(const char* partnerId, const char* partnerInfo) {
[[AppsFlyerLib shared] setPartnerDataWithPartnerId: stringFromChar(partnerId) partnerInfo:dictionaryFromJson(partnerInfo)];
}
const void _disableIDFVCollection(bool isDisabled) {
[AppsFlyerLib shared].disableIDFVCollection = isDisabled;
}
// Purchase connector
const void _startObservingTransactions() {
[[PurchaseConnector shared] startObservingTransactions];
}
const void _stopObservingTransactions() {
[[PurchaseConnector shared] stopObservingTransactions];
}
const void _setIsSandbox(bool isSandBox) {
[[PurchaseConnector shared] setIsSandbox:isSandBox];
}
const void _setPurchaseRevenueDelegate() {
if (_AppsFlyerdelegate== nil) {
_AppsFlyerdelegate = [[AppsFlyeriOSWarpper alloc] init];
}
[[PurchaseConnector shared] setPurchaseRevenueDelegate:_AppsFlyerdelegate];
}
const void _setAutoLogPurchaseRevenue(int option) {
[[PurchaseConnector shared] setAutoLogPurchaseRevenue:option];
}
const void _initPurchaseConnector(const char* objectName) {
if (_AppsFlyerdelegate == nil) {
_AppsFlyerdelegate = [[AppsFlyeriOSWarpper alloc] init];
}
onPurchaseValidationObjectName = stringFromChar(objectName);
}
const void _setPurchaseRevenueDataSource(const char* objectName) {
if (_AppsFlyerdelegate == nil) {
_AppsFlyerdelegate = [[AppsFlyeriOSWarpper alloc] init];
}
if (strstr(objectName, "StoreKit2") != NULL) {
// Force protocol conformance
Protocol *sk2Protocol = @protocol(AppsFlyerPurchaseRevenueDataSourceStoreKit2);
class_addProtocol([_AppsFlyerdelegate class], sk2Protocol);
if (![_AppsFlyerdelegate conformsToProtocol:@protocol(AppsFlyerPurchaseRevenueDataSourceStoreKit2)]) {
NSLog(@"[AppsFlyer] Warning: SK2 protocol not conformed!");
}
}
[PurchaseConnector shared].purchaseRevenueDataSource = _AppsFlyerdelegate;
}
const void _setStoreKitVersion(int storeKitVersion) {
[[PurchaseConnector shared] setStoreKitVersion:(AFSDKStoreKitVersion)storeKitVersion];
}
const void _logConsumableTransaction(const char* transactionId) {
if (@available(iOS 15.0, *)) {
NSString *transactionIdStr = [NSString stringWithUTF8String:transactionId];
[AFUnityStoreKit2Bridge fetchAFSDKTransactionSK2WithTransactionId:transactionIdStr completion:^(AFSDKTransactionSK2 *afTransaction) {
if (afTransaction) {
[[PurchaseConnector shared] logConsumableTransaction:afTransaction];
} else {
NSLog(@"No AFSDKTransactionSK2 found for id %@", transactionIdStr);
}
}];
}
}
#ifdef __cplusplus
extern "C" {
#endif
typedef const char *(*UnityPurchaseCallback)(const char *, const char *);
UnityPurchaseCallback UnityPurchasesGetAdditionalParamsCallback = NULL;
UnityPurchaseCallback UnityPurchasesGetAdditionalParamsCallbackSK2 = NULL;
__attribute__((visibility("default")))
void RegisterUnityPurchaseRevenueParamsCallback(UnityPurchaseCallback callback) {
UnityPurchasesGetAdditionalParamsCallback = callback;
}
__attribute__((visibility("default")))
void RegisterUnityPurchaseRevenueParamsCallbackSK2(UnityPurchaseCallback callback) {
UnityPurchasesGetAdditionalParamsCallbackSK2 = callback;
}
#ifdef __cplusplus
}
#endif
}
@implementation AppsFlyeriOSWarpper
static BOOL didCallStart;
+ (BOOL) didCallStart
{ @synchronized(self) { return didCallStart; } }
+ (void) setDidCallStart:(BOOL)val
{ @synchronized(self) { didCallStart = val; } }
- (void)onConversionDataSuccess:(NSDictionary *)installData {
unityCallBack(ConversionDataCallbackObject, GCD_CALLBACK, stringFromdictionary(installData));
}
- (void)onConversionDataFail:(NSError *)error {
unityCallBack(ConversionDataCallbackObject, GCD_ERROR_CALLBACK, [[error localizedDescription] UTF8String]);
}
- (void)onAppOpenAttribution:(NSDictionary *)attributionData {
unityCallBack(ConversionDataCallbackObject, OAOA_CALLBACK, stringFromdictionary(attributionData));
}
- (void)onAppOpenAttributionFailure:(NSError *)error {
unityCallBack(ConversionDataCallbackObject, OAOA_ERROR_CALLBACK, [[error localizedDescription] UTF8String]);
}
- (void)didResolveDeepLink:(AppsFlyerDeepLinkResult *)result{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:stringFromDeepLinkResultError(result) forKey:@"error"];
[dict setValue:stringFromDeepLinkResultStatus(result.status) forKey:@"status"];
if(result && result.deepLink){
[dict setValue:result.deepLink.description forKey:@"deepLink"];
[dict setValue:@(result.deepLink.isDeferred) forKey:@"is_deferred"];
}
unityCallBack(onDeeplinkingObjectName, ON_DEEPLINKING, stringFromdictionary(dict));
}
// Purchase Connector
- (void)didReceivePurchaseRevenueValidationInfo:(NSDictionary *)validationInfo error:(NSError *)error {
if (error != nil) {
unityCallBack(onPurchaseValidationObjectName, PURCHASE_REVENUE_ERROR_CALLBACK, [[error localizedDescription] UTF8String]);
} else {
unityCallBack(onPurchaseValidationObjectName, PURCHASE_REVENUE_VALIDATION_CALLBACK, stringFromdictionary(validationInfo));
}
}
- (NSDictionary *)purchaseRevenueAdditionalParametersForProducts:(NSSet<SKProduct *> *)products
transactions:(NSSet<SKPaymentTransaction *> *)transactions {
NSMutableArray *productsArray = [NSMutableArray array];
for (SKProduct *product in products) {
[productsArray addObject:@{
@"productIdentifier": product.productIdentifier ?: @"",
@"localizedTitle": product.localizedTitle ?: @"",
@"localizedDescription": product.localizedDescription ?: @"",
@"price": [product.price stringValue] ?: @""
}];
}
NSMutableArray *transactionsArray = [NSMutableArray array];
for (SKPaymentTransaction *txn in transactions) {
[transactionsArray addObject:@{
@"transactionIdentifier": txn.transactionIdentifier ?: @"",
@"transactionState": @(txn.transactionState),
@"transactionDate": txn.transactionDate ? [@(txn.transactionDate.timeIntervalSince1970) stringValue] : @""
}];
}
NSDictionary *input = @{
@"products": productsArray,
@"transactions": transactionsArray
};
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:input options:0 error:&error];
if (error || !jsonData) {
NSLog(@"[AppsFlyer] Failed to serialize Unity purchase data: %@", error);
return @{};
}
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
if (!jsonString || !UnityPurchasesGetAdditionalParamsCallback) {
NSLog(@"[AppsFlyer] Unity callback not registered");
return @{};
}
const char *resultCStr = UnityPurchasesGetAdditionalParamsCallback([jsonString UTF8String], "");
if (!resultCStr) {
NSLog(@"[AppsFlyer] Unity callback returned null");
return @{};
}
NSString *resultJson = [NSString stringWithUTF8String:resultCStr];
NSData *resultData = [resultJson dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *parsedResult = [NSJSONSerialization JSONObjectWithData:resultData options:0 error:&error];
if (error || ![parsedResult isKindOfClass:[NSDictionary class]]) {
NSLog(@"[AppsFlyer] Failed to parse Unity response: %@", error);
return @{};
}
return parsedResult;
}
#pragma mark - AppsFlyerPurchaseRevenueDataSourceStoreKit2
- (NSDictionary *)purchaseRevenueAdditionalParametersStoreKit2ForProducts:(NSSet<AFSDKProductSK2 *> *)products transactions:(NSSet<AFSDKTransactionSK2 *> *)transactions {
if (@available(iOS 15.0, *)) {
NSArray *productInfoArray = [AFUnityStoreKit2Bridge extractSK2ProductInfo:[products allObjects]];
NSArray *transactionInfoArray = [AFUnityStoreKit2Bridge extractSK2TransactionInfo:[transactions allObjects]];
NSDictionary *input = @{
@"products": productInfoArray,
@"transactions": transactionInfoArray
};
if (UnityPurchasesGetAdditionalParamsCallbackSK2) {
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:input options:0 error:&error];
if (error || !jsonData) {
NSLog(@"[AppsFlyer] Failed to serialize Unity purchase data: %@", error);
return @{};
}
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
const char *resultCStr = UnityPurchasesGetAdditionalParamsCallbackSK2([jsonString UTF8String], "");
if (!resultCStr) {
NSLog(@"[AppsFlyer] Unity callback returned null");
return @{};
}
NSString *resultJson = [NSString stringWithUTF8String:resultCStr];
NSData *resultData = [resultJson dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *parsedResult = [NSJSONSerialization JSONObjectWithData:resultData options:0 error:&error];
if (error || ![parsedResult isKindOfClass:[NSDictionary class]]) {
NSLog(@"[AppsFlyer] Failed to parse Unity response: %@", error);
return @{};
}
return parsedResult;
} else {
NSLog(@"[AppsFlyer] SK2 - Unity callback is NOT registered");
}
} else {
NSLog(@"[AppsFlyer] SK2 - iOS version not supported");
}
return @{};
}
@end

View File

@@ -0,0 +1,37 @@
fileFormatVersion: 2
guid: 2bff3788f3d8747fe9679bd3818e1d76
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
iPhone: iOS
second:
enabled: 1
settings: {}
- first:
tvOS: tvOS
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,184 @@
#nullable enable
using System.Collections;
using System.Collections.Generic;
using System;
using UnityEngine;
[System.Serializable]
public class InAppPurchaseValidationResult : EventArgs
{
public bool success;
public ProductPurchase? productPurchase;
public ValidationFailureData? failureData;
public string? token;
}
[System.Serializable]
public class ProductPurchase
{
public string? kind;
public string? purchaseTimeMillis;
public int purchaseState;
public int consumptionState;
public string? developerPayload;
public string? orderId;
public int purchaseType;
public int acknowledgementState;
public string? purchaseToken;
public string? productId;
public int quantity;
public string? obfuscatedExternalAccountId;
public string? obfuscatedExternalProfil;
public string? regionCode;
}
[System.Serializable]
public class ValidationFailureData
{
public int status;
public string? description;
}
[System.Serializable]
public class SubscriptionValidationResult
{
public bool success;
public SubscriptionPurchase? subscriptionPurchase;
public ValidationFailureData? failureData;
public string? token;
}
[System.Serializable]
public class SubscriptionPurchase
{
public string? acknowledgementState;
public CanceledStateContext? canceledStateContext;
public ExternalAccountIdentifiers? externalAccountIdentifiers;
public string? kind;
public string? latestOrderId;
public List<SubscriptionPurchaseLineItem>? lineItems;
public string? linkedPurchaseToken;
public PausedStateContext? pausedStateContext;
public string? regionCode;
public string? startTime;
public SubscribeWithGoogleInfo? subscribeWithGoogleInfo;
public string? subscriptionState;
public TestPurchase? testPurchase;
}
[System.Serializable]
public class CanceledStateContext
{
public DeveloperInitiatedCancellation? developerInitiatedCancellation;
public ReplacementCancellation? replacementCancellation;
public SystemInitiatedCancellation? systemInitiatedCancellation;
public UserInitiatedCancellation? userInitiatedCancellation;
}
[System.Serializable]
public class ExternalAccountIdentifiers
{
public string? externalAccountId;
public string? obfuscatedExternalAccountId;
public string? obfuscatedExternalProfileId;
}
[System.Serializable]
public class SubscriptionPurchaseLineItem
{
public AutoRenewingPlan? autoRenewingPlan;
public DeferredItemReplacement? deferredItemReplacement;
public string? expiryTime;
public OfferDetails? offerDetails;
public PrepaidPlan? prepaidPlan;
public string? productId;
}
[System.Serializable]
public class PausedStateContext
{
public string? autoResumeTime;
}
[System.Serializable]
public class SubscribeWithGoogleInfo
{
public string? emailAddress;
public string? familyName;
public string? givenName;
public string? profileId;
public string? profileName;
}
[System.Serializable]
public class TestPurchase{}
[System.Serializable]
public class DeveloperInitiatedCancellation{}
[System.Serializable]
public class ReplacementCancellation{}
[System.Serializable]
public class SystemInitiatedCancellation{}
[System.Serializable]
public class UserInitiatedCancellation
{
public CancelSurveyResult? cancelSurveyResult;
public string? cancelTime;
}
[System.Serializable]
public class AutoRenewingPlan
{
public string? autoRenewEnabled;
public SubscriptionItemPriceChangeDetails? priceChangeDetails;
}
[System.Serializable]
public class DeferredItemReplacement
{
public string? productId;
}
[System.Serializable]
public class OfferDetails
{
public List<string>? offerTags;
public string? basePlanId;
public string? offerId;
}
[System.Serializable]
public class PrepaidPlan
{
public string? allowExtendAfterTime;
}
[System.Serializable]
public class CancelSurveyResult
{
public string? reason;
public string? reasonUserInput;
}
[System.Serializable]
public class SubscriptionItemPriceChangeDetails
{
public string? expectedNewPriceChargeTime;
public Money? newPrice;
public string? priceChangeMode;
public string? priceChangeState;
}
[System.Serializable]
public class Money
{
public string? currencyCode;
public long nanos;
public long units;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9a1435104a69d4c8ebcc6f237cc29a54
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1fbfcb6aeaa7f40e69a0daff450a2450
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: c621896ec81267f478e98555031271ef
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,38 @@
fileFormatVersion: 2
guid: 5186898c6f4665f438e46763d4cff3ae
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
XboxOne: XboxOne
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,145 @@
//#define AFSDK_WIN_DEBUG
//#define UNITY_WSA_10_0
//#define ENABLE_WINMD_SUPPORT
#if UNITY_WSA_10_0
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.ComponentModel;
using UnityEngine;
using System.Threading.Tasks;
#if ENABLE_WINMD_SUPPORT
using AppsFlyerLib;
#endif
namespace AppsFlyerSDK
{
public class AppsFlyerWindows
{
#if ENABLE_WINMD_SUPPORT
static private MonoBehaviour _gameObject = null;
#endif
public static void InitSDK(string devKey, string appId, MonoBehaviour gameObject)
{
#if ENABLE_WINMD_SUPPORT
#if AFSDK_WIN_DEBUG
// Remove callstack
Application.SetStackTraceLogType(LogType.Log, StackTraceLogType.None);
#endif
Log("[InitSDK]: devKey: {0}, appId: {1}, gameObject: {2}", devKey, appId, gameObject == null ? "null" : gameObject.ToString());
AppsFlyerTracker tracker = AppsFlyerTracker.GetAppsFlyerTracker();
tracker.devKey = devKey;
tracker.appId = appId;
// Interface
_gameObject = gameObject;
#endif
}
public static string GetAppsFlyerId()
{
#if ENABLE_WINMD_SUPPORT
Log("[GetAppsFlyerId]");
return AppsFlyerTracker.GetAppsFlyerTracker().GetAppsFlyerUID();
#else
return "";
#endif
}
public static void SetCustomerUserId(string customerUserId)
{
#if ENABLE_WINMD_SUPPORT
Log("[SetCustomerUserId] customerUserId: {0}", customerUserId);
if (customerUserId.Contains("test_device:"))
{
string testDeviceId = customerUserId.Substring(12);
AppsFlyerTracker.GetAppsFlyerTracker().testDeviceId = testDeviceId;
}
AppsFlyerTracker.GetAppsFlyerTracker().customerUserId = customerUserId;
#endif
}
public static void Start()
{
#if ENABLE_WINMD_SUPPORT
Log("[Start]");
AppsFlyerTracker.GetAppsFlyerTracker().TrackAppLaunchAsync(Callback);
#endif
}
#if ENABLE_WINMD_SUPPORT
public static void Callback(AppsFlyerLib.ServerStatusCode code)
{
Log("[Callback]: {0}", code.ToString());
AppsFlyerRequestEventArgs eventArgs = new AppsFlyerRequestEventArgs((int)code, code.ToString());
if (_gameObject != null) {
var method = _gameObject.GetType().GetMethod("AppsFlyerOnRequestResponse");
if (method != null) {
method.Invoke(_gameObject, new object[] { AppsFlyerTracker.GetAppsFlyerTracker(), eventArgs });
}
}
}
#endif
public static void LogEvent(string eventName, Dictionary<string, string> eventValues)
{
#if ENABLE_WINMD_SUPPORT
if (eventValues == null)
{
eventValues = new Dictionary<string, string>();
}
IDictionary<string, object> result = new Dictionary<string, object>();
foreach (KeyValuePair<string, string> kvp in eventValues)
{
result.Add(kvp.Key.ToString(), kvp.Value);
}
Log("[LogEvent]: eventName: {0} result: {1}", eventName, result.ToString());
AppsFlyerTracker tracker = AppsFlyerTracker.GetAppsFlyerTracker();
tracker.TrackEvent(eventName, result);
#endif
}
public static void GetConversionData(string _reserved)
{
#if ENABLE_WINMD_SUPPORT
Task.Run(async () =>
{
AppsFlyerLib.AppsFlyerTracker tracker = AppsFlyerLib.AppsFlyerTracker.GetAppsFlyerTracker();
string conversionData = await tracker.GetConversionDataAsync();
IAppsFlyerConversionData conversionDataHandler = _gameObject as IAppsFlyerConversionData;
if (conversionDataHandler != null)
{
Log("[GetConversionData] Will call `onConversionDataSuccess` with: {0}", conversionData);
conversionDataHandler.onConversionDataSuccess(conversionData);
} else {
Log("[GetConversionData] Object with `IAppsFlyerConversionData` interface not found! Check `InitSDK` implementation");
}
// _gameObject.GetType().GetMethod("onConversionDataSuccess").Invoke(_gameObject, new[] { conversionData });
});
#endif
}
private static void Log(string format, params string[] args)
{
#if AFSDK_WIN_DEBUG
#if ENABLE_WINMD_SUPPORT
Debug.Log("AF_UNITY_WSA_10_0" + String.Format(format, args));
#endif
#endif
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 034d11e52b599954181d7f08c0d89ca8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
{
"name": "appsflyer-unity-plugin",
"displayName": "AppsFlyer",
"description": "AppsFlyer Unity plugin",
"version": "6.17.91",
"unity": "2019.4",
"license": "MIT"
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: a2b3ab5da7dda473b8791503604647b4
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 6880507913a57f545843f7e5b2412e6a
folderAsset: yes
timeCreated: 1541820628
licenseType: Store
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b45c7b45c9256a745978ed0eb3cf5663
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,233 @@
using UnityEngine;
namespace Crystal
{
/// <summary>
/// Safe area implementation for notched mobile devices. Usage:
/// (1) Add this component to the top level of any GUI panel.
/// (2) If the panel uses a full screen background image, then create an immediate child and put the component on that instead, with all other elements childed below it.
/// This will allow the background image to stretch to the full extents of the screen behind the notch, which looks nicer.
/// (3) For other cases that use a mixture of full horizontal and vertical background stripes, use the Conform X & Y controls on separate elements as needed.
/// </summary>
public class SafeArea : MonoBehaviour
{
#region Simulations
/// <summary>
/// Simulation device that uses safe area due to a physical notch or software home bar. For use in Editor only.
/// </summary>
public enum SimDevice
{
/// <summary>
/// Don't use a simulated safe area - GUI will be full screen as normal.
/// </summary>
None,
/// <summary>
/// Simulate the iPhone X and Xs (identical safe areas).
/// </summary>
iPhoneX,
/// <summary>
/// Simulate the iPhone Xs Max and XR (identical safe areas).
/// </summary>
iPhoneXsMax,
/// <summary>
/// Simulate the Google Pixel 3 XL using landscape left.
/// </summary>
Pixel3XL_LSL,
/// <summary>
/// Simulate the Google Pixel 3 XL using landscape right.
/// </summary>
Pixel3XL_LSR
}
/// <summary>
/// Simulation mode for use in editor only. This can be edited at runtime to toggle between different safe areas.
/// </summary>
public static SimDevice Sim = SimDevice.None;
/// <summary>
/// Normalised safe areas for iPhone X with Home indicator (ratios are identical to Xs, 11 Pro). Absolute values:
/// PortraitU x=0, y=102, w=1125, h=2202 on full extents w=1125, h=2436;
/// PortraitD x=0, y=102, w=1125, h=2202 on full extents w=1125, h=2436 (not supported, remains in Portrait Up);
/// LandscapeL x=132, y=63, w=2172, h=1062 on full extents w=2436, h=1125;
/// LandscapeR x=132, y=63, w=2172, h=1062 on full extents w=2436, h=1125.
/// Aspect Ratio: ~19.5:9.
/// </summary>
Rect[] NSA_iPhoneX = new Rect[]
{
new Rect (0f, 102f / 2436f, 1f, 2202f / 2436f), // Portrait
new Rect (132f / 2436f, 63f / 1125f, 2172f / 2436f, 1062f / 1125f) // Landscape
};
/// <summary>
/// Normalised safe areas for iPhone Xs Max with Home indicator (ratios are identical to XR, 11, 11 Pro Max). Absolute values:
/// PortraitU x=0, y=102, w=1242, h=2454 on full extents w=1242, h=2688;
/// PortraitD x=0, y=102, w=1242, h=2454 on full extents w=1242, h=2688 (not supported, remains in Portrait Up);
/// LandscapeL x=132, y=63, w=2424, h=1179 on full extents w=2688, h=1242;
/// LandscapeR x=132, y=63, w=2424, h=1179 on full extents w=2688, h=1242.
/// Aspect Ratio: ~19.5:9.
/// </summary>
Rect[] NSA_iPhoneXsMax = new Rect[]
{
new Rect (0f, 102f / 2688f, 1f, 2454f / 2688f), // Portrait
new Rect (132f / 2688f, 63f / 1242f, 2424f / 2688f, 1179f / 1242f) // Landscape
};
/// <summary>
/// Normalised safe areas for Pixel 3 XL using landscape left. Absolute values:
/// PortraitU x=0, y=0, w=1440, h=2789 on full extents w=1440, h=2960;
/// PortraitD x=0, y=0, w=1440, h=2789 on full extents w=1440, h=2960;
/// LandscapeL x=171, y=0, w=2789, h=1440 on full extents w=2960, h=1440;
/// LandscapeR x=0, y=0, w=2789, h=1440 on full extents w=2960, h=1440.
/// Aspect Ratio: 18.5:9.
/// </summary>
Rect[] NSA_Pixel3XL_LSL = new Rect[]
{
new Rect (0f, 0f, 1f, 2789f / 2960f), // Portrait
new Rect (0f, 0f, 2789f / 2960f, 1f) // Landscape
};
/// <summary>
/// Normalised safe areas for Pixel 3 XL using landscape right. Absolute values and aspect ratio same as above.
/// </summary>
Rect[] NSA_Pixel3XL_LSR = new Rect[]
{
new Rect (0f, 0f, 1f, 2789f / 2960f), // Portrait
new Rect (171f / 2960f, 0f, 2789f / 2960f, 1f) // Landscape
};
#endregion
RectTransform Panel;
Rect LastSafeArea = new Rect (0, 0, 0, 0);
Vector2Int LastScreenSize = new Vector2Int (0, 0);
ScreenOrientation LastOrientation = ScreenOrientation.AutoRotation;
[SerializeField] bool ConformX = true; // Conform to screen safe area on X-axis (default true, disable to ignore)
[SerializeField] bool ConformY = true; // Conform to screen safe area on Y-axis (default true, disable to ignore)
[SerializeField] bool Logging = false; // Conform to screen safe area on Y-axis (default true, disable to ignore)
void Awake ()
{
Panel = GetComponent<RectTransform> ();
if (Panel == null)
{
Debug.LogError ("Cannot apply safe area - no RectTransform found on " + name);
Destroy (gameObject);
}
Refresh ();
}
void Update ()
{
Refresh ();
}
void Refresh ()
{
Rect safeArea = GetSafeArea ();
if (safeArea != LastSafeArea
|| Screen.width != LastScreenSize.x
|| Screen.height != LastScreenSize.y
|| Screen.orientation != LastOrientation)
{
// Fix for having auto-rotate off and manually forcing a screen orientation.
// See https://forum.unity.com/threads/569236/#post-4473253 and https://forum.unity.com/threads/569236/page-2#post-5166467
LastScreenSize.x = Screen.width;
LastScreenSize.y = Screen.height;
LastOrientation = Screen.orientation;
ApplySafeArea (safeArea);
}
}
Rect GetSafeArea ()
{
Rect safeArea = Screen.safeArea;
if (Application.isEditor && Sim != SimDevice.None)
{
Rect nsa = new Rect (0, 0, Screen.width, Screen.height);
switch (Sim)
{
case SimDevice.iPhoneX:
if (Screen.height > Screen.width) // Portrait
nsa = NSA_iPhoneX[0];
else // Landscape
nsa = NSA_iPhoneX[1];
break;
case SimDevice.iPhoneXsMax:
if (Screen.height > Screen.width) // Portrait
nsa = NSA_iPhoneXsMax[0];
else // Landscape
nsa = NSA_iPhoneXsMax[1];
break;
case SimDevice.Pixel3XL_LSL:
if (Screen.height > Screen.width) // Portrait
nsa = NSA_Pixel3XL_LSL[0];
else // Landscape
nsa = NSA_Pixel3XL_LSL[1];
break;
case SimDevice.Pixel3XL_LSR:
if (Screen.height > Screen.width) // Portrait
nsa = NSA_Pixel3XL_LSR[0];
else // Landscape
nsa = NSA_Pixel3XL_LSR[1];
break;
default:
break;
}
safeArea = new Rect (Screen.width * nsa.x, Screen.height * nsa.y, Screen.width * nsa.width, Screen.height * nsa.height);
}
return safeArea;
}
void ApplySafeArea (Rect r)
{
LastSafeArea = r;
// Ignore x-axis?
if (!ConformX)
{
r.x = 0;
r.width = Screen.width;
}
// Ignore y-axis?
if (!ConformY)
{
r.y = 0;
r.height = Screen.height;
}
// Check for invalid screen startup state on some Samsung devices (see below)
if (Screen.width > 0 && Screen.height > 0)
{
// Convert safe area rectangle from absolute pixels to normalised anchor coordinates
Vector2 anchorMin = r.position;
Vector2 anchorMax = r.position + r.size;
anchorMin.x /= Screen.width;
anchorMin.y /= Screen.height;
anchorMax.x /= Screen.width;
anchorMax.y /= Screen.height;
// Fix for some Samsung devices (e.g. Note 10+, A71, S20) where Refresh gets called twice and the first time returns NaN anchor coordinates
// See https://forum.unity.com/threads/569236/page-2#post-6199352
if (anchorMin.x >= 0 && anchorMin.y >= 0 && anchorMax.x >= 0 && anchorMax.y >= 0)
{
Panel.anchorMin = anchorMin;
Panel.anchorMax = anchorMax;
}
}
if (Logging)
{
Debug.LogFormat ("New safe area applied to {0}: x={1}, y={2}, w={3}, h={4} on full extents w={5}, h={6}",
name, r.x, r.y, r.width, r.height, Screen.width, Screen.height);
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: c97afc556caea1c44969477eb7ddec74
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 130488
packageName: Safe Area Helper
packageVersion: 1.0.6
assetPath: Assets/CrystalFramework/Utility/SafeArea.cs
uploadId: 898916

View File

@@ -0,0 +1,390 @@
{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff0\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi0\deflang3081\deflangfe1041\themelang3081\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f34\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria Math;}
{\f37\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\f38\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0302020204030204}Calibri Light;}{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}
{\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhimajor\f31502\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0302020204030204}Calibri Light;}
{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}
{\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}
{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f49\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f50\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
{\f52\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f53\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f54\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f55\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
{\f56\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f57\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f59\fbidi \froman\fcharset238\fprq2 Cambria Math CE;}{\f60\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;}
{\f62\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f63\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f66\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;}{\f67\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);}
{\f69\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\f70\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\f72\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\f73\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}
{\f74\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}{\f75\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\f76\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\f77\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}
{\f79\fbidi \fswiss\fcharset238\fprq2 Calibri Light CE;}{\f80\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;}{\f82\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}{\f83\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;}
{\f84\fbidi \fswiss\fcharset177\fprq2 Calibri Light (Hebrew);}{\f85\fbidi \fswiss\fcharset178\fprq2 Calibri Light (Arabic);}{\f86\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;}{\f87\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);}
{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}
{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}
{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}
{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}
{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhimajor\f31528\fbidi \fswiss\fcharset238\fprq2 Calibri Light CE;}{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;}
{\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;}{\fhimajor\f31533\fbidi \fswiss\fcharset177\fprq2 Calibri Light (Hebrew);}
{\fhimajor\f31534\fbidi \fswiss\fcharset178\fprq2 Calibri Light (Arabic);}{\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;}{\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);}
{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}
{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}
{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}
{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}
{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}
{\fhiminor\f31573\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}{\fhiminor\f31574\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}
{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}}
{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;
\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;\red0\green0\blue0;\red0\green0\blue0;\red47\green84\blue150;\red5\green99\blue193;}{\*\defchp
\fs24\kerning2\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\sa160\sl278\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{\ql \li0\ri0\sa160\sl278\slmult1
\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0 \fs24\lang3081\langfe1041\kerning2\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp3081\langfenp1041 \snext0 \sqformat \spriority0
Normal;}{\s1\ql \li0\ri0\sb240\sa60\sl278\slmult1\keepn\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel0\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af0\afs32\alang1025 \ltrch\fcs0
\b\fs32\lang3081\langfe1041\kerning32\loch\f31502\hich\af31502\dbch\af31501\cgrid\langnp3081\langfenp1041 \sbasedon0 \snext0 \slink15 \sqformat \spriority9 \styrsid2914631 heading 1;}{\s2\ql \li0\ri0\sb240\sa60\sl278\slmult1
\keepn\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel1\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\ai\af0\afs28\alang1025 \ltrch\fcs0 \b\i\fs28\lang3081\langfe1041\kerning2\loch\f31502\hich\af31502\dbch\af31501\cgrid\langnp3081\langfenp1041
\sbasedon0 \snext0 \slink16 \sunhideused \sqformat \spriority9 \styrsid2914631 heading 2;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\*
\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl278\slmult1
\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0 \fs24\lang3081\langfe1041\kerning2\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp3081\langfenp1041
\snext11 \ssemihidden \sunhideused Normal Table;}{\*\cs15 \additive \rtlch\fcs1 \ab\af0\afs32 \ltrch\fcs0 \b\fs32\kerning32\loch\f31502\hich\af31502\dbch\af31501 \sbasedon10 \slink1 \spriority9 \styrsid2914631 Heading 1 Char;}{\*\cs16 \additive
\rtlch\fcs1 \ab\ai\af0\afs28 \ltrch\fcs0 \b\i\fs28\loch\f31502\hich\af31502\dbch\af31501 \sbasedon10 \slink2 \spriority9 \styrsid2914631 Heading 2 Char;}{\s17\ql \li0\ri0\sb240\sl259\slmult1
\keep\keepn\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs32\alang1025 \ltrch\fcs0 \fs32\cf19\lang1033\langfe1033\loch\f31502\hich\af31502\dbch\af31501\cgrid\langnp1033\langfenp1033
\sbasedon1 \snext0 \sunhideused \sqformat \spriority39 \styrsid2914631 TOC Heading;}{\s18\ql \li0\ri0\sa160\sl278\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0
\fs24\lang3081\langfe1041\kerning2\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp3081\langfenp1041 \sbasedon0 \snext0 \sautoupd \sunhideused \spriority39 \styrsid16206562 toc 1;}{\s19\ql \li240\ri0\sa160\sl278\slmult1
\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin240\itap0 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0 \fs24\lang3081\langfe1041\kerning2\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp3081\langfenp1041
\sbasedon0 \snext0 \sautoupd \sunhideused \spriority39 \styrsid16206562 toc 2;}{\*\cs20 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf20 \sbasedon10 \sunhideused \styrsid16206562 Hyperlink;}}{\*\listtable{\list\listtemplateid-1\listhybrid{\listlevel
\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid201916431\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li720\lin720 }{\listlevel\levelnfc4\levelnfcn4\leveljc0
\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid201916441\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2
\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid201916443\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-180\li2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0
\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid201916431\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1
\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid201916441\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative
\levelspace0\levelindent0{\leveltext\leveltemplateid201916443\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-180\li4320\lin4320 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0
\levelindent0{\leveltext\leveltemplateid201916431\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0
{\leveltext\leveltemplateid201916441\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext
\leveltemplateid201916443\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-180\li6480\lin6480 }{\listname ;}\listid491986554}{\list\listtemplateid-1\listhybrid{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1
\levelspace0\levelindent0{\leveltext\leveltemplateid201916431\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0 \fi-360\li720\lin720 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0
\levelindent0{\leveltext\leveltemplateid201916441\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0
{\leveltext\leveltemplateid201916443\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-180\li2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext
\leveltemplateid201916431\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext
\leveltemplateid201916441\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext
\leveltemplateid201916443\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-180\li4320\lin4320 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext
\leveltemplateid201916431\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext
\leveltemplateid201916441\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext
\leveltemplateid201916443\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-180\li6480\lin6480 }{\listname ;}\listid622276423}{\list\listtemplateid-1\listhybrid{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1
\levelspace0\levelindent0{\leveltext\leveltemplateid201916431\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0 \fi-360\li720\lin720 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0
\levelindent0{\leveltext\leveltemplateid201916441\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0
{\leveltext\leveltemplateid201916443\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-180\li2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext
\leveltemplateid201916431\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext
\leveltemplateid201916441\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext
\leveltemplateid201916443\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-180\li4320\lin4320 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext
\leveltemplateid201916431\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext
\leveltemplateid201916441\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext
\leveltemplateid201916443\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-180\li6480\lin6480 }{\listname ;}\listid764233946}{\list\listtemplateid-1\listhybrid{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1
\levelspace0\levelindent0{\leveltext\leveltemplateid201916431\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li720\lin720 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0
\levelindent0{\leveltext\leveltemplateid201916441\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0
{\leveltext\leveltemplateid201916443\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-180\li2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext
\leveltemplateid201916431\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext
\leveltemplateid201916441\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext
\leveltemplateid201916443\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-180\li4320\lin4320 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext
\leveltemplateid201916431\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext
\leveltemplateid201916441\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext
\leveltemplateid201916443\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-180\li6480\lin6480 }{\listname ;}\listid1044137377}}{\*\listoverridetable{\listoverride\listid1044137377\listoverridecount0\ls1}{\listoverride\listid622276423
\listoverridecount0\ls2}{\listoverride\listid491986554\listoverridecount0\ls3}{\listoverride\listid764233946\listoverridecount0\ls4}}{\*\rsidtbl \rsid1858992\rsid2914631\rsid4129176\rsid4542871\rsid5571476\rsid12976886\rsid16206562}{\mmathPr\mmathFont34
\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\operator Matthew Ota}{\creatim\yr2026\mo4\dy12\hr21\min50}{\revtim\yr2026\mo4\dy12\hr21\min55}{\version5}{\edmins4}{\nofpages2}
{\nofwords312}{\nofchars1780}{\nofcharsws2088}{\vern13}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect
\widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont0\relyonvml0\donotembedlingdata1\grfdocevents0\validatexml0\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors0\horzdoc\dghspace120\dgvspace120\dghorigin1701
\dgvorigin1984\dghshow0\dgvshow3\jcompress\viewkind1\viewscale100\rsidroot2914631 \fet0{\*\wgrffmtfilter 2450}\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2
\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6
\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang
{\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\s17\ql \li0\ri0\sb240\sl259\slmult1\keep\keepn\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs32\alang1025 \ltrch\fcs0
\fs32\cf19\lang1033\langfe1033\loch\af31502\hich\af31502\dbch\af31501\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid2914631 \hich\af31502\dbch\af31501\loch\f31502 Table of Contents
\par }\pard\plain \ltrpar\s18\ql \li0\ri0\sa160\sl278\slmult1\widctlpar\tqr\tldot\tx9350\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0
\fs24\lang3081\langfe1041\kerning2\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp3081\langfenp1041 {\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid2914631 \hich\af31506\dbch\af31505\loch\f31506 TOC \\o "1-3" \\h \\z \\u }}{\fldrslt
{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 \hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\insrsid1858992
\hich\af31506\dbch\af31505\loch\f31506 HYPERLINK \\l "_Toc226923347"}{\rtlch\fcs1 \af0 \ltrch\fcs0 \cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 \hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0
\cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 {\*\datafield 08d0c9ea79f9bace118c8200aa004ba90b02000000080000000e0000005f0054006f0063003200320036003900320033003300340037000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0
\cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 \hich\af31506\dbch\af31505\loch\f31506 Safe Area Helper}{\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992 \tab }{\field{\*\fldinst {\rtlch\fcs1 \af0
\ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992 \hich\af31506\dbch\af31505\loch\f31506 \hich\af31506\dbch\af31505\loch\f31506 PAGEREF \hich\af31506\dbch\af31505\loch\f31506 _Toc226923347 \\h\hich\af31506\dbch\af31505\loch\f31506 }{
\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992 {\*\datafield 08d0c9ea79f9bace118c8200aa004ba90b02000000080000000e0000005f0054006f0063003200320036003900320033003300340037000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0
\lang1024\langfe1024\noproof\webhidden\insrsid1858992 \hich\af31506\dbch\af31505\loch\f31506 1}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj }}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af31507 \ltrch\fcs0
\lang1024\langfe1024\noproof\insrsid1858992
\par }\pard\plain \ltrpar\s19\ql \li240\ri0\sa160\sl278\slmult1\widctlpar\tqr\tldot\tx9350\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin240\itap0 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0
\fs24\lang3081\langfe1041\kerning2\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp3081\langfenp1041 {\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681
\hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\insrsid1858992 \hich\af31506\dbch\af31505\loch\f31506 HYPERLINK \\l "_Toc226923348"}{\rtlch\fcs1 \af0 \ltrch\fcs0
\cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 \hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0 \cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 {\*\datafield
08d0c9ea79f9bace118c8200aa004ba90b02000000080000000e0000005f0054006f0063003200320036003900320033003300340038000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0 \cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681
\hich\af31506\dbch\af31505\loch\f31506 Usage}{\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992 \tab }{\field{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992
\hich\af31506\dbch\af31505\loch\f31506 \hich\af31506\dbch\af31505\loch\f31506 PAGEREF \hich\af31506\dbch\af31505\loch\f31506 _Toc226923348 \\h\hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0
\lang1024\langfe1024\noproof\webhidden\insrsid1858992 {\*\datafield 08d0c9ea79f9bace118c8200aa004ba90b02000000080000000e0000005f0054006f0063003200320036003900320033003300340038000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0
\lang1024\langfe1024\noproof\webhidden\insrsid1858992 \hich\af31506\dbch\af31505\loch\f31506 1}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj }}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af31507 \ltrch\fcs0
\lang1024\langfe1024\noproof\insrsid1858992
\par }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 \hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\insrsid1858992
\hich\af31506\dbch\af31505\loch\f31506 HYPERLINK \\l "_Toc226923349"}{\rtlch\fcs1 \af0 \ltrch\fcs0 \cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 \hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0
\cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 {\*\datafield 08d0c9ea79f9bace118c8200aa004ba90b02000000080000000e0000005f0054006f0063003200320036003900320033003300340039000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0
\cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 \hich\af31506\dbch\af31505\loch\f31506 Add your own simulated devices}{\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992 \tab }{\field{\*\fldinst {
\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992 \hich\af31506\dbch\af31505\loch\f31506 \hich\af31506\dbch\af31505\loch\f31506 PAGEREF \hich\af31506\dbch\af31505\loch\f31506 _Toc226923349 \\h
\hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992 {\*\datafield 08d0c9ea79f9bace118c8200aa004ba90b02000000080000000e0000005f0054006f0063003200320036003900320033003300340039000000}}
}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992 \hich\af31506\dbch\af31505\loch\f31506 1}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj }}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af31507
\ltrch\fcs0 \lang1024\langfe1024\noproof\insrsid1858992
\par }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 \hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\insrsid1858992
\hich\af31506\dbch\af31505\loch\f31506 HYPERLINK \\l "_Toc226923350"}{\rtlch\fcs1 \af0 \ltrch\fcs0 \cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 \hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0
\cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 {\*\datafield 08d0c9ea79f9bace118c8200aa004ba90b02000000080000000e0000005f0054006f0063003200320036003900320033003300350030000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0
\cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 \hich\af31506\dbch\af31505\loch\f31506 Other notes and suggestions}{\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992 \tab }{\field{\*\fldinst {
\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992 \hich\af31506\dbch\af31505\loch\f31506 \hich\af31506\dbch\af31505\loch\f31506 PAGEREF \hich\af31506\dbch\af31505\loch\f31506 _Toc226923350 \\h
\hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992 {\*\datafield 08d0c9ea79f9bace118c8200aa004ba90b02000000080000000e0000005f0054006f0063003200320036003900320033003300350030000000}}
}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992 \hich\af31506\dbch\af31505\loch\f31506 2}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj }}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af31507
\ltrch\fcs0 \lang1024\langfe1024\noproof\insrsid1858992
\par }\pard\plain \ltrpar\s18\ql \li0\ri0\sa160\sl278\slmult1\widctlpar\tqr\tldot\tx9350\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0
\fs24\lang3081\langfe1041\kerning2\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp3081\langfenp1041 {\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681
\hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\insrsid1858992 \hich\af31506\dbch\af31505\loch\f31506 HYPERLINK \\l "_Toc226923351"}{\rtlch\fcs1 \af0 \ltrch\fcs0
\cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 \hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0 \cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 {\*\datafield
08d0c9ea79f9bace118c8200aa004ba90b02000000080000000e0000005f0054006f0063003200320036003900320033003300350031000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0 \cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681
\hich\af31506\dbch\af31505\loch\f31506 Version History}{\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992 \tab }{\field{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992
\hich\af31506\dbch\af31505\loch\f31506 \hich\af31506\dbch\af31505\loch\f31506 PAGEREF \hich\af31506\dbch\af31505\loch\f31506 _Toc226923351 \\h\hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0
\lang1024\langfe1024\noproof\webhidden\insrsid1858992 {\*\datafield 08d0c9ea79f9bace118c8200aa004ba90b02000000080000000e0000005f0054006f0063003200320036003900320033003300350031000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0
\lang1024\langfe1024\noproof\webhidden\insrsid1858992 \hich\af31506\dbch\af31505\loch\f31506 2}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj }}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af31507 \ltrch\fcs0
\lang1024\langfe1024\noproof\insrsid1858992
\par }\pard\plain \ltrpar\s19\ql \li240\ri0\sa160\sl278\slmult1\widctlpar\tqr\tldot\tx9350\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin240\itap0 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0
\fs24\lang3081\langfe1041\kerning2\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp3081\langfenp1041 {\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681
\hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\insrsid1858992 \hich\af31506\dbch\af31505\loch\f31506 HYPERLINK \\l "_Toc226923352"}{\rtlch\fcs1 \af0 \ltrch\fcs0
\cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 \hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0 \cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 {\*\datafield
08d0c9ea79f9bace118c8200aa004ba90b02000000080000000e0000005f0054006f0063003200320036003900320033003300350032000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0 \cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681
\hich\af31506\dbch\af31505\loch\f31506 v1.0.8}{\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992 \tab }{\field{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992
\hich\af31506\dbch\af31505\loch\f31506 \hich\af31506\dbch\af31505\loch\f31506 PAGEREF \hich\af31506\dbch\af31505\loch\f31506 _Toc226923352 \\h\hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0
\lang1024\langfe1024\noproof\webhidden\insrsid1858992 {\*\datafield 08d0c9ea79f9bace118c8200aa004ba90b02000000080000000e0000005f0054006f0063003200320036003900320033003300350032000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0
\lang1024\langfe1024\noproof\webhidden\insrsid1858992 \hich\af31506\dbch\af31505\loch\f31506 2}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj }}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af31507 \ltrch\fcs0
\lang1024\langfe1024\noproof\insrsid1858992
\par }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 \hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\insrsid1858992
\hich\af31506\dbch\af31505\loch\f31506 HYPERLINK \\l "_Toc226923353"}{\rtlch\fcs1 \af0 \ltrch\fcs0 \cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 \hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0
\cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 {\*\datafield 08d0c9ea79f9bace118c8200aa004ba90b02000000080000000e0000005f0054006f0063003200320036003900320033003300350033000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0
\cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 \hich\af31506\dbch\af31505\loch\f31506 v1.0.7}{\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992 \tab }{\field{\*\fldinst {\rtlch\fcs1 \af0
\ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992 \hich\af31506\dbch\af31505\loch\f31506 \hich\af31506\dbch\af31505\loch\f31506 PAGEREF \hich\af31506\dbch\af31505\loch\f31506 _Toc226923353 \\h\hich\af31506\dbch\af31505\loch\f31506 }{
\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992 {\*\datafield 08d0c9ea79f9bace118c8200aa004ba90b02000000080000000e0000005f0054006f0063003200320036003900320033003300350033000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0
\lang1024\langfe1024\noproof\webhidden\insrsid1858992 \hich\af31506\dbch\af31505\loch\f31506 2}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj }}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af31507 \ltrch\fcs0
\lang1024\langfe1024\noproof\insrsid1858992
\par }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 \hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\insrsid1858992
\hich\af31506\dbch\af31505\loch\f31506 HYPERLINK \\l "_Toc226923354"}{\rtlch\fcs1 \af0 \ltrch\fcs0 \cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 \hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af0 \ltrch\fcs0
\cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 {\*\datafield 08d0c9ea79f9bace118c8200aa004ba90b02000000080000000e0000005f0054006f0063003200320036003900320033003300350034000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0
\cs20\ul\cf20\lang1024\langfe1024\noproof\insrsid1858992\charrsid283681 \hich\af31506\dbch\af31505\loch\f31506 v1.0.6}{\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992 \tab }{\field{\*\fldinst {\rtlch\fcs1 \af0
\ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992 \hich\af31506\dbch\af31505\loch\f31506 \hich\af31506\dbch\af31505\loch\f31506 PAGEREF \hich\af31506\dbch\af31505\loch\f31506 _Toc226923354 \\h\hich\af31506\dbch\af31505\loch\f31506 }{
\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1024\langfe1024\noproof\webhidden\insrsid1858992 {\*\datafield 08d0c9ea79f9bace118c8200aa004ba90b02000000080000000e0000005f0054006f0063003200320036003900320033003300350034000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0
\lang1024\langfe1024\noproof\webhidden\insrsid1858992 \hich\af31506\dbch\af31505\loch\f31506 2}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj }}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af31507 \ltrch\fcs0
\lang1024\langfe1024\noproof\insrsid1858992
\par }\pard\plain \ltrpar\ql \li0\ri0\sa160\sl278\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0
\fs24\lang3081\langfe1041\kerning2\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp3081\langfenp1041 }}\pard\plain \ltrpar\ql \li0\ri0\sa160\sl278\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1
\af0\afs24\alang1025 \ltrch\fcs0 \fs24\lang3081\langfe1041\kerning2\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp3081\langfenp1041 \sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid2914631
\par }\pard\plain \ltrpar\s1\ql \li0\ri0\sb240\sa60\sl278\slmult1\keepn\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel0\adjustright\rin0\lin0\itap0\pararsid2914631 \rtlch\fcs1 \ab\af0\afs32\alang1025 \ltrch\fcs0
\b\fs32\lang3081\langfe1041\kerning32\loch\af31502\hich\af31502\dbch\af31501\cgrid\langnp3081\langfenp1041 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid2914631 {\*\bkmkstart _Toc226923347}\hich\af31502\dbch\af31501\loch\f31502 Safe Area Helper
{\*\bkmkend _Toc226923347}
\par }\pard\plain \ltrpar\s2\ql \li0\ri0\sb240\sa60\sl278\slmult1\keepn\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel1\adjustright\rin0\lin0\itap0\pararsid2914631 \rtlch\fcs1 \ab\ai\af0\afs28\alang1025 \ltrch\fcs0
\b\i\fs28\lang3081\langfe1041\kerning2\loch\af31502\hich\af31502\dbch\af31501\cgrid\langnp3081\langfenp1041 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid2914631 {\*\bkmkstart _Toc226923348}\hich\af31502\dbch\af31501\loch\f31502 Usage{\*\bkmkend _Toc226923348}
\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \f31506\kerning2\insrsid2914631 \hich\af31506\dbch\af31505\loch\f31506 1.\tab}}\pard\plain \ltrpar\ql \fi-360\li720\ri0\sa160\sl278\slmult1
\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls2\adjustright\rin0\lin720\itap0\pararsid2914631 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0 \fs24\lang3081\langfe1041\kerning2\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp3081\langfenp1041 {
\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid2914631 \hich\af31506\dbch\af31505\loch\f31506 Create desired aspect ratios in the Game Window, e.g. 195:90 and 90:195 for iPhone X series.
\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \f31506\kerning2\insrsid2914631 \hich\af31506\dbch\af31505\loch\f31506 2.\tab}\hich\af31506\dbch\af31505\loch\f31506 Add the SafeArea.cs component to your GUI panels.
\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \f31506\kerning2\insrsid2914631 \hich\af31506\dbch\af31505\loch\f31506 3.\tab}\hich\af31506\dbch\af31505\loch\f31506 Add the SafeAreaDemo.cs once to a component in your scene.
\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \f31506\kerning2\insrsid2914631 \hich\af31506\dbch\af31505\loch\f31506 4.\tab}\hich\af31506\dbch\af31505\loch\f31506
Run the game and use the Toggle hotkey (default "A") to toggle between simulated devices. Be sure to match the simulated device with the correct aspect ratio in your Game Window.
\par }\pard\plain \ltrpar\s2\ql \li0\ri0\sb240\sa60\sl278\slmult1\keepn\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel1\adjustright\rin0\lin0\itap0\pararsid2914631 \rtlch\fcs1 \ab\ai\af0\afs28\alang1025 \ltrch\fcs0
\b\i\fs28\lang3081\langfe1041\kerning2\loch\af31502\hich\af31502\dbch\af31501\cgrid\langnp3081\langfenp1041 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid2914631 {\*\bkmkstart _Toc226923349}\hich\af31502\dbch\af31501\loch\f31502 Add your own simulated devices
{\*\bkmkend _Toc226923349}
\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \f31506\kerning2\insrsid2914631 \hich\af31506\dbch\af31505\loch\f31506 1.\tab}}\pard\plain \ltrpar\ql \fi-360\li720\ri0\sa160\sl278\slmult1
\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls4\adjustright\rin0\lin720\itap0\pararsid2914631 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0 \fs24\lang3081\langfe1041\kerning2\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp3081\langfenp1041 {
\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid2914631 \hich\af31506\dbch\af31505\loch\f31506 Run an empty scene to the target mobile device with one GUI panel containing the SafeArea.cs component.
\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \f31506\kerning2\insrsid2914631 \hich\af31506\dbch\af31505\loch\f31506 2.\tab}\hich\af31506\dbch\af31505\loch\f31506
Rotate the device to each of the four orientations. Copy the pixel coordinates for each orientation from the debug output.
\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \f31506\kerning2\insrsid2914631 \hich\af31506\dbch\af31505\loch\f31506 3.\tab}\hich\af31506\dbch\af31505\loch\f31506
Enter the simulated device into SafeArea.cs using the same technique for the iPhone X.
\par }\pard\plain \ltrpar\s2\ql \li0\ri0\sb240\sa60\sl278\slmult1\keepn\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel1\adjustright\rin0\lin0\itap0\pararsid1858992 \rtlch\fcs1 \ab\ai\af0\afs28\alang1025 \ltrch\fcs0
\b\i\fs28\lang3081\langfe1041\kerning2\loch\af31502\hich\af31502\dbch\af31501\cgrid\langnp3081\langfenp1041 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid1858992 {\*\bkmkstart _Toc226923350}\hich\af31502\dbch\af31501\loch\f31502 Other notes and suggestions
{\*\bkmkend _Toc226923350}
\par }\pard\plain \ltrpar\ql \li0\ri0\sa160\sl278\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid2914631 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0
\fs24\lang3081\langfe1041\kerning2\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp3081\langfenp1041 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid2914631 \hich\af31506\dbch\af31505\loch\f31506
See notes in SafeArea.cs and read our article online for a full breakdown of how the Safe Area works}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid5571476 .}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid2914631
\par \hich\af31506\dbch\af31505\loch\f31506 If you have a suggestion or request for a simluated device to be added to the list, please let us know in the Unity forums.
\par \hich\af31506\dbch\af31505\loch\f31506 If this asset helped you, please rate us on the Asset Store!
\par }\pard\plain \ltrpar\s1\ql \li0\ri0\sb240\sa60\sl278\slmult1\keepn\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel0\adjustright\rin0\lin0\itap0\pararsid2914631 \rtlch\fcs1 \ab\af0\afs32\alang1025 \ltrch\fcs0
\b\fs32\lang3081\langfe1041\kerning32\loch\af31502\hich\af31502\dbch\af31501\cgrid\langnp3081\langfenp1041 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid2914631 {\*\bkmkstart _Toc226923351}\hich\af31502\dbch\af31501\loch\f31502 Version History
{\*\bkmkend _Toc226923351}
\par }\pard\plain \ltrpar\s2\ql \li0\ri0\sb240\sa60\sl278\slmult1\keepn\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel1\adjustright\rin0\lin0\itap0\pararsid2914631 \rtlch\fcs1 \ab\ai\af0\afs28\alang1025 \ltrch\fcs0
\b\i\fs28\lang3081\langfe1041\kerning2\loch\af31502\hich\af31502\dbch\af31501\cgrid\langnp3081\langfenp1041 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16206562 {\*\bkmkstart _Toc226923352}\hich\af31502\dbch\af31501\loch\f31502 v}{\rtlch\fcs1 \af0 \ltrch\fcs0
\insrsid2914631 \hich\af31502\dbch\af31501\loch\f31502 1.0.8{\*\bkmkend _Toc226923352}
\par }\pard\plain \ltrpar\ql \li0\ri0\sa160\sl278\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid2914631 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0
\fs24\lang3081\langfe1041\kerning2\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp3081\langfenp1041 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid2914631 \hich\af31506\dbch\af31505\loch\f31506 Released: Apr 12, 2026}{\rtlch\fcs1 \af0 \ltrch\fcs0
\insrsid2914631\charrsid2914631
\par }{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid2914631 \hich\af31506\dbch\af31505\loch\f31506 Compatiblity with Unity 6.3 (no code changes).}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid2914631\charrsid2914631
\par }\pard\plain \ltrpar\s2\ql \li0\ri0\sb240\sa60\sl278\slmult1\keepn\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel1\adjustright\rin0\lin0\itap0\pararsid2914631 \rtlch\fcs1 \ab\ai\af0\afs28\alang1025 \ltrch\fcs0
\b\i\fs28\lang3081\langfe1041\kerning2\loch\af31502\hich\af31502\dbch\af31501\cgrid\langnp3081\langfenp1041 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16206562 {\*\bkmkstart _Toc226923353}\hich\af31502\dbch\af31501\loch\f31502 v}{\rtlch\fcs1 \af0 \ltrch\fcs0
\insrsid2914631 \hich\af31502\dbch\af31501\loch\f31502 1.0.7{\*\bkmkend _Toc226923353}
\par }\pard\plain \ltrpar\ql \li0\ri0\sa160\sl278\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid2914631 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0
\fs24\lang3081\langfe1041\kerning2\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp3081\langfenp1041 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid2914631 \hich\af31506\dbch\af31505\loch\f31506 Released: Aug 14, 2020
\par \hich\af31506\dbch\af31505\loch\f31506 Added startup sim device option to SafeAreaDemo component.
\par }\pard\plain \ltrpar\s2\ql \li0\ri0\sb240\sa60\sl278\slmult1\keepn\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel1\adjustright\rin0\lin0\itap0\pararsid2914631 \rtlch\fcs1 \ab\ai\af0\afs28\alang1025 \ltrch\fcs0
\b\i\fs28\lang3081\langfe1041\kerning2\loch\af31502\hich\af31502\dbch\af31501\cgrid\langnp3081\langfenp1041 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16206562 {\*\bkmkstart _Toc226923354}\hich\af31502\dbch\af31501\loch\f31502 v}{\rtlch\fcs1 \af0 \ltrch\fcs0
\insrsid2914631 \hich\af31502\dbch\af31501\loch\f31502 1.0.6{\*\bkmkend _Toc226923354}
\par }\pard\plain \ltrpar\ql \li0\ri0\sa160\sl278\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid2914631 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0
\fs24\lang3081\langfe1041\kerning2\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp3081\langfenp1041 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid2914631 \hich\af31506\dbch\af31505\loch\f31506 Released: Aug 14, 2020
\par \hich\af31506\dbch\af31505\loch\f31506 Fix for some Samsung devices (Note 10+, A71 and S20) that would startup with an error state making the SafeArea disappear.}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid4542871
\par }{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid2914631
\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a
9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad
5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6
b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0
0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6
a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f
c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512
0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462
a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865
6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b
4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b
4757e8d3f729e245eb2b260a0238fd010000ffff0300504b030414000600080000002100b6f4679893070000c9200000160000007468656d652f7468656d652f
7468656d65312e786d6cec59cd8b1bc915bf07f23f347d97f5d5ad8fc1f2a24fcfda33b6b164873dd648a5eef2547789aad28cc56208de532e81c026e49085bd
ed21842cecc22eb9e48f31d8249b3f22afaa5bdd5552c99e191c3061463074977eefd5afde7bf5de53d5ddcf5e26d4bbc05c1096f6fcfa9d9aefe174ce16248d
7afeb3d9a4d2f13d2151ba4094a5b8e76fb0f03fbbf7eb5fdd454732c609f6403e1547a8e7c752ae8eaa5531876124eeb0154ee1bb25e30992f0caa3ea82a34b
d09bd06aa3566b55134452df4b51026a1f2f97648ebd9952e9dfdb2a1f53784da5500373caa74a35b6243476715e5708b11143cabd0b447b3eccb3609733fc52
fa1e4542c2173dbfa6fffceabdbb5574940b517940d6909be8bf5c2e17589c37f49c3c3a2b260d823068f50bfd1a40e53e6edc1eb7c6ad429f06a0f91c569a71
b175b61bc320c71aa0ecd1a17bd41e35eb16ded0dfdce3dc0fd5c7c26b50a63fd8c34f2643b0a285d7a00c1feee1c3417730b2f56b50866fede1dbb5fe28685b
fa3528a6243ddf43d7c25673b85d6d0159327aec8477c360d26ee4ca4b144443115d6a8a254be5a1584bd00bc6270050408a24493db959e1259a43140f112567
9c7827248a21f056286502866b8ddaa4d684ffea13e827ed5174849121ad780113b137a4f87862cec94af6fc07a0d537206f7ffef9cdeb1fdfbcfee9cd575fbd
79fdf77c6eadca923b466964cafdf2dd1ffef3cd6fbd7ffff0ed2f5fff319b7a172f4cfcbbbffdeedd3ffef93ef5b0e2d2146ffff4fdbb1fbf7ffbe7dfffebaf
5f3bb4f7393a33e1339260e13dc297de5396c0021dfcf119bf9ec42c46c494e8a791402952b338f48f656ca11f6d10450edc00db767cce21d5b880f7d72f2cc2
d398af2571687c182716f094313a60dc6985876a2ec3ccb3751ab927e76b13f714a10bd7dc43945a5e1eaf579063894be530c616cd2714a5124538c5d253dfb1
738c1dabfb8210cbaea764ce99604be97d41bc01224e93ccc899154da5d03149c02f1b1741f0b7659bd3e7de8051d7aa47f8c246c2de40d4417e86a965c6fb68
2d51e252394309350d7e8264ec2239ddf0b9891b0b099e8e3065de78818570c93ce6b05ec3e90f21cdb8dd7e4a37898de4929cbb749e20c64ce4889d0f6394ac
5cd829496313fbb938871045de13265df05366ef10f50e7e40e941773f27d872f787b3c133c8b026a53240d4376beef0e57dccacf89d6ee8126157aae9f3c44a
b17d4e9cd131584756689f604cd1255a60ec3dfbdcc160c05696cd4bd20f62c82ac7d815580f901dabea3dc5027a25d5dcece7c91322ac909de2881de073bad9
493c1b9426881fd2fc08bc6eda7c0ca52e7105c0633a3f37818f08f480102f4ea33c16a0c308ee835a9fc4c82a60ea5db8e375c32dff5d658fc1be7c61d1b8c2
be04197c6d1948eca6cc7b6d3343d49aa00c9819822ec3956e41c4727f29a28aab165b3be596f6a62ddd00dd91d5f42424fd6007b4d3fb84ffbbde073a8cb77f
f9c6b10f3e4ebfe3566c25ab6b763a8792c9f14e7f7308b7dbd50c195f904fbfa919a175fa04431dd9cf58b73dcd6d4fe3ffdff73487f6f36d2773a8dfb8ed64
7ce8306e3b99fc70e5e3743265f3027d8d3af0c80e7af4b14f72f0d46749289dca0dc527421ffc08f83db398c0a092d3279eb838055cc5f0a8ca1c4c60e1228e
b48cc799fc0d91f134462b381daafb4a492472d591f0564cc0a1911e76ea5678ba4e4ed9223becacd7d5c16656590592e5782d2cc6e1a04a66e856bb3cc02bd4
6bb6913e68dd1250b2d721614c6693683a48b4b783ca48fa58178ce620a157f65158741d2c3a4afdd6557b2c805ae115f8c1edc1cff49e1f06200242701e07cd
f942f92973f5d6bbda991fd3d3878c69450034d8db08283ddd555c0f2e4fad2e0bb52b78da2261849b4d425b46377822869fc17974aad1abd0b8aeafbba54b2d
7aca147a3e08ad9246bbf33e1637f535c8ede6069a9a9982a6de65cf6f35430899395af5fc251c1ac363b282d811ea3717a211dcbccc25cf36fc4d32cb8a0b39
4222ce0cae934e960d122231f728497abe5a7ee1069aea1ca2b9d51b90103e59725d482b9f1a3970baed64bc5ce2b934dd6e8c284b67af90e1b35ce1fc568bdf
1cac24d91adc3d8d1797de195df3a708422c6cd795011744c0dd413db3e682c0655891c8caf8db294c79da356fa3740c65e388ae62945714339967709dca0b3a
faadb081f196af190c6a98242f8467912ab0a651ad6a5a548d8cc3c1aafb6121653923699635d3ca2aaa6abab39835c3b60cecd8f26645de60b53531e434b3c2
67a97b37e576b7b96ea74f28aa0418bcb09fa3ea5ea12018d4cac92c6a8af17e1a56393b1fb56bc776811fa07695226164fdd656ed8edd8a1ae19c0e066f54f9
416e376a6168b9ed2bb5a5f5adb979b1cdce5e40f2184197bba6526857c2c92e47d0104d754f92a50dd8222f65be35e0c95b73d2f3bfac85fd60d80887955a27
1c57826650ab74c27eb3d20fc3667d1cd66ba341e31514161927f530bbb19fc00506dde4f7f67a7cefee3ed9ded1dc99b3a4caf4dd7c5513d777f7f5c6e1bb7b
8f40d2f9b2d598749bdd41abd26df627956034e854bac3d6a0326a0ddba3c9681876ba9357be77a1c141bf390c5ae34ea5551f0e2b41aba6e877ba9576d068f4
8376bf330efaaff23606569ea58fdc16605ecdebde7f010000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468656d65
2f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4350d36
3f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d262452282e
3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe514173d985
0528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff0000001c020000130000000000000000000000
0000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b00000000000000000000
000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c0000000000000000000000000019020000
7468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d0014000600080000002100b6f4679893070000c92000001600000000000000
000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b01000027000000
000000000000000000009d0a00007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000980b00000000}
{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d
617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169
6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363
656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e}
{\*\latentstyles\lsdstimax376\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1;
\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4;
\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7;
\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9;
\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3;
\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6;
\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature;\lsdsemihidden1 \lsdunhideused1 \lsdpriority1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink;\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong;
\lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid;
\lsdsemihidden1 \lsdlocked0 Placeholder Text;\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid;
\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2;
\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List;\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1;
\lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;
\lsdsemihidden1 \lsdlocked0 Revision;\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1;
\lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2;
\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2;
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;
\lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3;
\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;
\lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4;
\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 4;
\lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdpriority62 \lsdlocked0 Light Grid Accent 5;
\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5;
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5;
\lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6;
\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6;
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6;
\lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis;
\lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography;
\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4;
\lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4;
\lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1;
\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1;
\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2;
\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2;
\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3;
\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4;
\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4;
\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5;
\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5;
\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6;
\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6;
\lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark;
\lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1;
\lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1;
\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2;
\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3;
\lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3;
\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4;
\lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4;
\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5;
\lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5;
\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6;
\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Mention;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hashtag;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Unresolved Mention;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Link;}}{\*\datastore 01050000
02000000180000004d73786d6c322e534158584d4c5265616465722e362e30000000000000000000000e0000
d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff0900060000000000000000000000010000000100000000000000001000000200000001000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
fffffffffffffffffdffffff04000000feffffff05000000fefffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffff010000000c6ad98892f1d411a65f0040963251e500000000000000000000000030f3
1c4473cadc0103000000c0020000000000004d0073006f004400610074006100530074006f0072006500000000000000000000000000000000000000000000000000000000000000000000000000000000001a000101ffffffffffffffff02000000000000000000000000000000000000000000000030f31c4473cadc01
30f31c4473cadc01000000000000000000000000d400c4003000c40032004b0049004a00d900d400d2004a004600d700c300d8005100cb00c600c200490041003d003d000000000000000000000000000000000032000101ffffffffffffffff03000000000000000000000000000000000000000000000030f31c4473ca
dc0130f31c4473cadc010000000000000000000000004900740065006d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000201ffffffff04000000ffffffff000000000000000000000000000000000000000000000000
00000000000000000000000000000000210100000000000001000000020000000300000004000000feffffff060000000700000008000000090000000a000000feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3c3f786d6c2076657273696f6e3d22312e3022207374616e64616c6f6e653d226e6f223f3e3c623a536f757263657320786d6c6e733a623d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f6f6666
696365446f63756d656e742f323030362f6269626c696f6772617068792220786d6c6e733d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f6f6666696365446f63756d656e742f323030362f6269626c696f677261706879222053656c65637465645374796c653d225c61706173
6978746865646974696f6e6f66666963656f6e6c696e652e78736c22205374796c654e616d653d22415041222056657273696f6e3d2236223e3c2f623a536f75726365733e000000000000000000000000000000000000000000000000000000000000003c3f786d6c2076657273696f6e3d22312e302220656e636f6469
6e673d225554462d3822207374616e64616c6f6e653d226e6f223f3e0d0a3c64733a6461746173746f72654974656d2064733a6974656d49443d227b37304134343644322d303941322d344345372d383931372d3738463834324239413232307d2220786d6c6e733a64733d22687474703a2f2f736368656d61732e6f70
656e786d6c666f726d6174732e6f72672f6f6666696365446f63756d656e742f323030362f637573500072006f007000650072007400690065007300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000200ffffffffffffffffffffffff000000000000
0000000000000000000000000000000000000000000000000000000000000500000055010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000746f6d586d6c223e3c64733a736368656d61526566733e3c64733a736368656d615265662064733a7572693d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f7267
2f6f6666696365446f63756d656e742f323030362f6269626c696f677261706879222f3e3c2f64733a736368656d61526566733e3c2f64733a6461746173746f72654974656d3e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000105000000000000}}

Some files were not shown because too many files have changed in this diff Show More