/// <summary>
        ///     Defines an asset bundle. If a Leanplum variable with the same name and type exists,
        ///     this will return the existing variable.
        /// </summary>
        /// <returns>Leanplum variable.</returns>
        /// <param name="name">Name of variable.</param>
        /// <param name="realtimeUpdating">Setting it to <c>false</c> will prevent Leanplum from
        /// reloading assetbundles as they change in development mode.</param>
        /// <param name="iosBundleName">Filename of iOS assetbundle.</param>
        /// <param name="androidBundleName">Filename of Android assetbundle.</param>
        /// <param name="standaloneBundleName">Filename of Standalone assetbundle.</param>
        public override Var <AssetBundle> DefineAssetBundle(string name,
                                                            bool realtimeUpdating       = true, string iosBundleName = "", string androidBundleName = "",
                                                            string standaloneBundleName = "")
        {
            string platform     = LeanplumNative.CompatibilityLayer.GetPlatformName();
            string resourceName = Constants.Values.RESOURCES_VARIABLE + '.' + platform +
                                  " Assets." + name;
            string bundleName = String.Empty;

            if (platform == "iOS")
            {
                bundleName = iosBundleName;
            }
            else if (platform == "Android")
            {
                bundleName = androidBundleName;
            }
            else if (platform == "Standalone")
            {
                bundleName = standaloneBundleName;
            }

            var variable = DefineHelper <AssetBundle>(resourceName, Constants.Kinds.FILE, null);

            if (variable != null)
            {
                variable.SetFilename(bundleName);
                variable.fileReady             = false;
                variable.realtimeAssetUpdating = realtimeUpdating;

                VarCache.RegisterVariable(variable);
                variable.Update();
            }
            return(variable);
        }
        private void Update()
        {
            // Workaround so that CheckVarsUpdate() is invoked on Unity's main thread.
            // This is called by Unity on every frame.
            if (VarCache.VarsNeedUpdate && developerModeEnabled && Leanplum.HasStarted)
            {
                VarCache.CheckVarsUpdate();
            }

            // Run deferred actions.
            List <Action> actions = null;

            lock (delayed)
            {
                if (delayed.Count > 0)
                {
                    actions = new List <Action>(delayed);
                    delayed.Clear();
                }
            }
            if (actions != null)
            {
                foreach (Action action in actions)
                {
                    action();
                }
            }
        }
        public override void DefineAction(string name, Constants.ActionKind kind, ActionArgs args, IDictionary <string, object> options, ActionContext.ActionResponder responder)
        {
            if (string.IsNullOrWhiteSpace(name))
            {
                CompatibilityLayer.LogError($"Empty name parameter.");
                return;
            }
            if (args == null)
            {
                CompatibilityLayer.LogError($"Args cannot be null.");
                return;
            }

            var ad = new ActionDefinition();

            ad.Name    = name;
            ad.Kind    = kind;
            ad.Args    = args;
            ad.Options = options;
            if (responder != null)
            {
                ad.Responder += responder;
            }

            VarCache.RegisterActionDefinition(ad);
        }
        private void OnSocketMessage(object obj, MessageEventArgs e)
        {
            if (e.Message.MessageType == SocketIOMessageTypes.Event &&
                !String.IsNullOrEmpty(e.Message.MessageText))
            {
                IDictionary <string, object> messageReceived =
                    Json.Deserialize(e.Message.MessageText) as IDictionary <string, object>;
                string eventName = messageReceived.ContainsKey("name") ? messageReceived["name"] as string: "";

                if (eventName == "updateVars")
                {
                    onUpdateVars();
                }
                else if (eventName == "getVariables")
                {
                    bool sentValues = VarCache.SendVariablesIfChanged();
                    Dictionary <string, bool> response = new Dictionary <string, bool>();
                    response.Add("updated", sentValues);
                    socketIOClient.Emit("getContentResponse", response);
                }
                else if (eventName == "getActions")
                {
                    // Unsupported in LeanplumNative.
                    Dictionary <string, bool> response = new Dictionary <string, bool>();
                    response.Add("updated", false);
                    socketIOClient.Emit("getContentResponse", response);
                }
                else if (eventName == "registerDevice")
                {
                    IDictionary <string, object> packetData = (IDictionary <string, object>)
                                                                  ((IList <object>)messageReceived[Constants.Keys.ARGS])[0];
                    string email = (string)packetData["email"];
                    LeanplumUnityHelper.QueueOnMainThread(() =>
                    {
                        LeanplumNative.OnHasStartedAndRegisteredAsDeveloper();
                        LeanplumNative.CompatibilityLayer.Log(
                            "Your device is registered to " + email + ".");
                    });
                }
                else if (eventName == "trigger")
                {
                    IDictionary <string, object> packetData = (IDictionary <string, object>)
                                                                  ((IList <object>)messageReceived[Constants.Keys.ARGS])[0];

                    // Trigger Preview
                    LeanplumUnityHelper.QueueOnMainThread(() =>
                    {
                        onActionTrigger(packetData);
                    });
                }
            }
        }
        /// <summary>
        ///     Defines a new variable with a default value. If a Leanplum variable with the
        ///     same name and type exists, this will return the existing variable.
        /// </summary>
        /// <param name="name"> Name of the variable. </param>
        /// <param name="defaultValue"> Default value of the variable. Can't be null. </param>
        public override Var <U> Define <U>(string name, U defaultValue)
        {
            string kind = null;

            if (defaultValue is int || defaultValue is long || defaultValue is short ||
                defaultValue is char || defaultValue is sbyte || defaultValue is byte)
            {
                kind = Constants.Kinds.INT;
            }
            else if (defaultValue is float || defaultValue is double || defaultValue is decimal)
            {
                kind = Constants.Kinds.FLOAT;
            }
            else if (defaultValue is string)
            {
                kind = Constants.Kinds.STRING;
            }
            else if (defaultValue is IList || defaultValue is Array)
            {
                kind = Constants.Kinds.ARRAY;
            }
            else if (defaultValue is IDictionary)
            {
                kind = Constants.Kinds.DICTIONARY;
            }
            else if (defaultValue is bool)
            {
                kind = Constants.Kinds.BOOLEAN;
            }
            else
            {
                Util.MaybeThrow(new LeanplumException(
                                    "Default value for \"" + name + "\" not recognized or supported."));
                return(null);
            }

            var variable = DefineHelper <U>(name, kind, defaultValue);

            if (variable != null)
            {
                variable.defaultClonedContainer = DeepCopyContainer(defaultValue);
                variable._defaultValue          = defaultValue;

                VarCache.RegisterVariable(variable);
                variable.Update();
            }


            return(variable);
        }
Example #6
0
        public object Traverse(string name)
        {
            if (!name.Contains('.'))
            {
                return(Util.GetValueOrDefault(vars, name));
            }

            string[] parts      = name.Split('.');
            object   components = vars;

            for (int i = 0; i < parts.Length - 1; i++)
            {
                components = VarCache.Traverse(vars, parts[i], false);
            }
            return(VarCache.Traverse(components, parts[parts.Length - 1], false));
        }
Example #7
0
        internal static void TriggerPreview(IDictionary <string, object> packetData)
        {
            var actionData = Util.GetValueOrDefault(packetData, Constants.Args.ACTION) as IDictionary <string, object>;

            if (actionData != null)
            {
                string actionName = Util.GetValueOrDefault(actionData, Constants.Args.ACTION_NAME)?.ToString();
                string messageId  = Util.GetValueOrDefault(packetData, Constants.Args.MESSAGE_ID)?.ToString();
                LeanplumNative.CompatibilityLayer.Log($"Preview of {actionName} Message with Id: {messageId}");
                if (!string.IsNullOrWhiteSpace(actionName))
                {
                    var newVars            = VarCache.MergeMessage(actionData);
                    NativeActionContext ac = new NativeActionContext(messageId, actionName, newVars);
                    TriggerAction(ac, newVars);
                }
            }
        }
        private static NativeVar <U> DefineHelper <U>(string name, string kind, U defaultValue)
        {
            NativeVar <U> existing = (NativeVar <U>)VarCache.GetVariable <U>(name);

            if (existing != null)
            {
                existing.ClearValueChangedCallbacks();
                return(existing);
            }
            // GetVariable(name) above will return null if the variable exists but of the wrong
            // type. Need to check if the name is not taken.
            if (VarCache.HasVariable(name))
            {
                LeanplumNative.CompatibilityLayer.LogWarning("Failed to define variable: \"" +
                                                             name + "\" refers to an " + "existing Leanplum variable of a different type.");
                return(null);
            }

            var variable = new NativeVar <U>(name, kind, defaultValue);

            return(variable);
        }
        /// <summary>
        ///     Updates the user ID and adds or modifies user attributes.
        /// </summary>
        /// <param name="newUserId">New user identifier.</param>
        /// <param name="value">User attributes.</param>
        public override void SetUserAttributes(string newUserId, IDictionary <string, object> value)
        {
            if (!calledStart)
            {
                CompatibilityLayer.LogWarning("Start was not called. Set user ID and attributes " +
                                              "as the arguments when calling Start.");
                return;
            }
            if (Constants.isNoop)
            {
                return;
            }

            var parameters = new Dictionary <string, string>();

            if (value != null)
            {
                ValidateAttributes(value);
                parameters[Constants.Params.USER_ATTRIBUTES] = Json.Serialize(value);
            }
            if (!String.IsNullOrEmpty(newUserId))
            {
                parameters[Constants.Params.NEW_USER_ID] = newUserId;
                VarCache.SaveDiffs();
            }
            LeanplumRequest.Post(Constants.Methods.SET_USER_ATTRIBUTES, parameters).Send();

            if (!String.IsNullOrEmpty(newUserId))
            {
                LeanplumRequest.UserId = newUserId;
                if (_hasStarted)
                {
                    VarCache.SaveDiffs();
                }
            }
        }
Example #10
0
        public override void Update()
        {
            object newValue = VarCache.GetMergedValueFromComponentArray(NameComponents);

            if (newValue == null)
            {
                newValue = GetDefaultValue();
            }

            if (Kind == Constants.Kinds.FILE)
            {
                if (VarCache.IsSilent)
                {
                    return;
                }

                string newFile = newValue.ToString();
                if (string.IsNullOrEmpty(newFile))
                {
                    return;
                }

                string url = null;
                if (VarCache.FileAttributes != null && VarCache.FileAttributes.ContainsKey(newFile))
                {
                    IDictionary <string, object> currentFile =
                        (VarCache.FileAttributes[newFile] as IDictionary <string, object>)
                        [string.Empty] as IDictionary <string, object>;
                    if (currentFile.ContainsKey(Constants.Keys.URL))
                    {
                        url = GetResourceURL(newFile);
                    }
                }

                // Download new file if the file is different than:
                // - the current file for the varible
                // - the file currently being downloaded for the variable
                // - there is no file value and realtime updating is enabled
                // Do not download and update if realtime updating is disabled
                // Wait until the file is downloaded from the server
                if (currentlyDownloadingFile != newFile && !string.IsNullOrEmpty(url) &&
                    ((newFile != FileName && realtimeAssetUpdating && fileReady) ||
                     (Value == null && realtimeAssetUpdating)))
                {
                    currentlyDownloadingFile = newFile;
                    FileName  = newFile;
                    fileReady = false;

                    void OnFileResponse(object file)
                    {
                        _value = (T)file;
                        if (newFile == FileName && !fileReady)
                        {
                            fileReady = true;
                            OnValueChanged();
                            currentlyDownloadingFile = null;
                        }
                    }

                    void OnFileError(Exception ex)
                    {
                        if (newFile == FileName && !fileReady)
                        {
                            string errorMessage = $"Error downloading AssetBundle \"{Name}\" with \"{FileName}\". {ex}";
                            LeanplumNative.CompatibilityLayer.LogError(errorMessage);
                            currentlyDownloadingFile = null;
                        }
                    }

                    Leanplum.FileTransferManager.DownloadAssetResource(url, OnFileResponse, OnFileError);
                }
            }
            else
            {
                if (Json.Serialize(newValue) != Json.Serialize(Value))
                {
                    try
                    {
                        if (newValue is IDictionary || newValue is IList)
                        {
                            // If the value is a container, copy all the values from the newValue container to Value.
                            Util.FillInValues(newValue, Value);
                        }
                        else
                        {
                            _value = (T)Convert.ChangeType(newValue, typeof(T));
                        }

                        if (VarCache.IsSilent)
                        {
                            return;
                        }

                        OnValueChanged();
                    }
                    catch (Exception ex)
                    {
                        Util.MaybeThrow(new LeanplumException("Error parsing values from server. " + ex.ToString()));
                        return;
                    }
                }
            }
        }
 /// <summary>
 ///     Forces content to update from the server. If variables have changed, the
 ///     appropriate callbacks will fire. Use sparingly as if the app is updated,
 ///     you'll have to deal with potentially inconsistent state or user experience.
 ///     The provided callback will always fire regardless
 ///     of whether the variables have changed.
 /// </summary>
 ///
 public override void ForceContentUpdate(Action callback)
 {
     VarCache.CheckVarsUpdate(callback);
 }
        /// <summary>
        ///     Call this when your application starts.
        ///     This will initiate a call to Leanplum's servers to get the values
        ///     of the variables used in your app.
        /// </summary>
        public override void Start(string userId, IDictionary <string, object> attributes,
                                   Leanplum.StartHandler startResponseAction)
        {
            if (calledStart)
            {
                CompatibilityLayer.Log("Already called start");
                return;
            }
            if (String.IsNullOrEmpty(LeanplumRequest.AppId))
            {
                CompatibilityLayer.LogError("You cannot call Start without setting your app's " +
                                            "API keys.");
                return;
            }

            if (CompatibilityLayer.GetPlatformName() == "Standalone")
            {
                // TODO: Fix this.
                // This is a workaround for SSL issues when running on
                // standalone when connecting with the production server.
                // No issue on staging.
                Constants.API_SSL = false;
            }

            if (startResponseAction != null)
            {
                Started += startResponseAction;
            }
            if (Constants.isNoop)
            {
                _hasStarted     = true;
                startSuccessful = true;
                OnVariablesChanged();
                OnVariablesChangedAndNoDownloadsPending();
                OnStarted(true);
                VarCache.ApplyVariableDiffs(null, null);
                return;
            }

            ValidateAttributes(attributes);
            calledStart = true;

            // Load the variables that were stored on the device from the last session.
            VarCache.IsSilent = true;
            VarCache.LoadDiffs();
            VarCache.IsSilent = false;

            // Setup class members.
            VarCache.Update += delegate
            {
                OnVariablesChanged();
                if (LeanplumRequest.PendingDownloads == 0)
                {
                    OnVariablesChangedAndNoDownloadsPending();
                }
            };

            LeanplumRequest.NoPendingDownloads += delegate
            {
                OnVariablesChangedAndNoDownloadsPending();
            };

            string deviceId;

            if (customDeviceId != null)
            {
                deviceId = customDeviceId;
            }
            else
            {
                deviceId = CompatibilityLayer.GetDeviceId();
            }
            LeanplumRequest.DeviceId = deviceId;

            // Don't overwrite UserID if it was set previously if Start()
            // was called without a new UserID.
            if (!String.IsNullOrEmpty(userId))
            {
                LeanplumRequest.UserId = userId;
            }
            if (String.IsNullOrEmpty(LeanplumRequest.UserId))
            {
                LeanplumRequest.UserId = deviceId;
            }

            // Setup parameters.
            var parameters = new Dictionary <string, string>();

            parameters[Constants.Params.INCLUDE_DEFAULTS]      = false.ToString();
            parameters[Constants.Params.VERSION_NAME]          = CompatibilityLayer.VersionName ?? "";
            parameters[Constants.Params.DEVICE_NAME]           = CompatibilityLayer.GetDeviceName();
            parameters[Constants.Params.DEVICE_MODEL]          = CompatibilityLayer.GetDeviceModel();
            parameters[Constants.Params.DEVICE_SYSTEM_NAME]    = CompatibilityLayer.GetSystemName();
            parameters[Constants.Params.DEVICE_SYSTEM_VERSION] =
                CompatibilityLayer.GetSystemVersion();
            TimeZone timezone = System.TimeZone.CurrentTimeZone;

            if (timezone.IsDaylightSavingTime(DateTime.UtcNow))
            {
                parameters[Constants.Keys.TIMEZONE] = timezone.DaylightName;
            }
            else
            {
                parameters[Constants.Keys.TIMEZONE] = timezone.StandardName;
            }
            parameters[Constants.Keys.TIMEZONE_OFFSET_SECONDS] =
                timezone.GetUtcOffset(DateTime.UtcNow).TotalSeconds.ToString();
            parameters[Constants.Keys.COUNTRY]  = Constants.Values.DETECT;
            parameters[Constants.Keys.REGION]   = Constants.Values.DETECT;
            parameters[Constants.Keys.CITY]     = Constants.Values.DETECT;
            parameters[Constants.Keys.LOCATION] = Constants.Values.DETECT;
            if (attributes != null)
            {
                parameters[Constants.Params.USER_ATTRIBUTES] = Json.Serialize(attributes);
            }

            // Issue start API call.
            LeanplumRequest req = LeanplumRequest.Post(Constants.Methods.START, parameters);

            req.Response += delegate(object responsesObject)
            {
                IDictionary <string, object> response =
                    Util.GetLastResponse(responsesObject) as IDictionary <string, object>;
                IDictionary <string, object> values =
                    Util.GetValueOrDefault(response, Constants.Keys.VARS) as
                    IDictionary <string, object> ?? new Dictionary <string, object>();
                IDictionary <string, object> fileAttributes =
                    Util.GetValueOrDefault(response, Constants.Keys.FILE_ATTRIBUTES) as
                    IDictionary <string, object> ?? new Dictionary <string, object>();
                List <object> variants = Util.GetValueOrDefault(response, Constants.Keys.VARIANTS) as
                                         List <object> ?? new List <object>();
                bool isRegistered = (bool)Util.GetValueOrDefault(response,
                                                                 Constants.Keys.IS_REGISTERED, false);

                LeanplumRequest.Token = Util.GetValueOrDefault(response, Constants.Keys.TOKEN) as
                                        string ?? "";

                // Allow bidirectional realtime variable updates.
                if (Constants.isDevelopmentModeEnabled)
                {
                    VarCache.SetDevModeValuesFromServer(
                        Util.GetValueOrDefault(response, Constants.Keys.VARS_FROM_CODE) as
                        Dictionary <string, object>);

                    if (Constants.EnableRealtimeUpdatesInDevelopmentMode &&
                        SocketUtilsFactory.Utils.AreSocketsAvailable)
                    {
                        leanplumSocket = new LeanplumSocket(delegate() {
                            // Callback when we receive an updateVars command through the
                            // development socket.
                            // Set a flag so that the next time VarCache.CheckVarsUpdate() is
                            // called the variables are updated.
                            VarCache.VarsNeedUpdate = true;
                        });
                    }

                    // Register device.
                    if (isRegistered)
                    {
                        // Check for updates.
                        string latestVersion = Util.GetValueOrDefault(response,
                                                                      Constants.Keys.LATEST_VERSION) as string;
                        if (latestVersion != null)
                        {
                            CompatibilityLayer.Log("Leanplum Unity SDK " + latestVersion +
                                                   " available. Go to https://www.leanplum.com/dashboard to " +
                                                   "download it.");
                        }
                        OnHasStartedAndRegisteredAsDeveloper();
                    }
                }

                VarCache.ApplyVariableDiffs(values, fileAttributes, variants);
                _hasStarted     = true;
                startSuccessful = true;
                OnStarted(true);
                CompatibilityLayer.Init();
            };
            req.Error += delegate
            {
                VarCache.ApplyVariableDiffs(null, null);
                _hasStarted     = true;
                startSuccessful = false;
                OnStarted(false);
                CompatibilityLayer.Init();
            };
            req.SendIfConnected();
        }
 /// <summary>
 ///     Traverses the variable structure with the specified path.
 ///     Path components can be either strings representing keys in a dictionary,
 ///     or integers representing indices in a list.
 /// </summary>
 public override object ObjectForKeyPathComponents(object[] pathComponents)
 {
     return(VarCache.GetMergedValueFromComponentArray(pathComponents));
 }
Example #14
0
        public override void Update()
        {
            object newValue = VarCache.GetMergedValueFromComponentArray(NameComponents);

            if (newValue == null)
            {
                newValue = GetDefaultValue();
            }

            if (Kind == Constants.Kinds.FILE)
            {
                if (VarCache.IsSilent)
                {
                    return;
                }
                string newFile = newValue.ToString();
                string url     = null;

                if (String.IsNullOrEmpty(newFile))
                {
                    return;
                }

                if (VarCache.FileAttributes != null && VarCache.FileAttributes.ContainsKey(newFile))
                {
                    IDictionary <string, object> currentFile =
                        (VarCache.FileAttributes[newFile] as IDictionary <string, object>)
                        [String.Empty] as IDictionary <string, object>;
                    if (currentFile.ContainsKey(Constants.Keys.URL))
                    {
                        url = ((VarCache.FileAttributes[newFile] as IDictionary <string, object>)
                               [String.Empty] as IDictionary <string, object>)[Constants.Keys.URL] as string;
                    }
                }

                // Update if the file is different from what we currently have and if we have started downloading it
                // already. Also update if we don't have the file but don't update if realtime updating is disabled -
                // wait 'til we get the value from the serve so that the correct file is displayed.
                if (currentlyDownloadingFile != newFile && !String.IsNullOrEmpty(url) &&
                    ((newFile != FileName && realtimeAssetUpdating && fileReady) ||
                     (Value == null && realtimeAssetUpdating)))
                {
                    VarCache.downloadsPending++;
                    currentlyDownloadingFile = newFile;
                    FileName  = newFile;
                    fileReady = false;

                    LeanplumRequest downloadRequest = LeanplumRequest.Get(url.Substring(1));
                    downloadRequest.Response += delegate(object obj) {
                        _value = (T)obj;
                        if (newFile == FileName && !fileReady)
                        {
                            fileReady = true;
                            OnValueChanged();
                            currentlyDownloadingFile = null;
                        }
                        VarCache.downloadsPending--;
                    };
                    downloadRequest.Error += delegate(Exception obj) {
                        if (newFile == FileName && !fileReady)
                        {
                            LeanplumNative.CompatibilityLayer.LogError("Error downloading assetbundle \"" +
                                                                       FileName + "\". " + obj.ToString());
                            currentlyDownloadingFile = null;
                        }
                        VarCache.downloadsPending--;
                    };
                    downloadRequest.DownloadAssetNow();
                }
            }
            else
            {
                if (Json.Serialize(newValue) != Json.Serialize(Value))
                {
                    try
                    {
                        if (newValue is IDictionary || newValue is IList)
                        {
                            // If the value is a container, copy all the values from the newValue container to Value.
                            SharedUtil.FillInValues(newValue, Value);
                        }
                        else
                        {
                            _value = (T)Convert.ChangeType(newValue, typeof(T));
                        }
                        if (VarCache.IsSilent)
                        {
                            return;
                        }
                        OnValueChanged();
                    }
                    catch (Exception ex)
                    {
                        Util.MaybeThrow(new LeanplumException("Error parsing values from server. " + ex.ToString()));
                        return;
                    }
                }
            }
        }
 public override void ForceSyncVariables(Leanplum.SyncVariablesCompleted completedHandler)
 {
     VarCache.ForceSendVariables(completedHandler);
 }