internal static void OnBubbleReceived(Bubble b)
        {
            var visualBubble = b as VisualBubble;

            if (visualBubble != null)
            {
                try
                {
                    BubbleManager.Group(visualBubble);

                    Analytics.RaiseServiceEvent(
                        Analytics.EventAction.MessageReceived,
                        Analytics.EventCategory.Messaging,
                        visualBubble.Service);
                }
                catch (Exception ex)
                {
                    Utils.DebugPrint("Problem in OnBubbleReceived (VisualBubble) from service " +
                                     visualBubble.Service.Information.ServiceName + ": " + ex.Message);
                }

                if (visualBubble.Direction == Bubble.BubbleDirection.Incoming &&
                    IsRunning(visualBubble.Service) &&
                    BubbleQueueManager.HasQueuedBubbles(b.Service.Information.ServiceName, true, true))
                {
                    Utils.DebugPrint("Sending queued bubbles as we're getting some received.");
                    BubbleQueueManager.Send(new [] { b.Service.Information.ServiceName });
                }
            }
            else if (b is AbstractBubble)
            {
                var skipEvent = false;

                Utils.DebugPrint("We got an abstract bubble: " + b.GetType().Name + " Address: " + b.Address + " ParticipantAddress: " + b.ParticipantAddress);

                BubbleGroup group = null;

                var deliveredBubble = b as DeliveredBubble;
                if (deliveredBubble != null)
                {
                    @group = BubbleGroupManager.FindWithAddress(deliveredBubble.Service, deliveredBubble.Address);

                    if (@group != null)
                    {
                        BubbleGroupFactory.LoadFullyIfNeeded(@group);

                        var bubbles = @group.Bubbles;

                        for (var i = bubbles.Count - 1; i >= 0; i--)
                        {
                            var bubble = bubbles[i];

                            if (bubble.ID != deliveredBubble.VisualBubbleID)
                            {
                                continue;
                            }

                            BubbleManager.UpdateStatus(bubble, Bubble.BubbleStatus.Delivered, @group);

                            if (@group.Service.Information.DoesSupport(typeof(DeliveredBubbleReceipt)))
                            {
                                BubbleManager.Send(new DeliveredBubbleReceipt(Time.GetNowUnixTimestamp(), Bubble.BubbleDirection.Outgoing,
                                                                              @group.Service, bubble));
                            }

                            break;
                        }
                    }
                }

                var readBubble = b as ReadBubble;
                if (readBubble != null)
                {
                    @group = BubbleGroupManager.FindWithAddress(readBubble.Service, readBubble.Address);
                    if (@group != null)
                    {
                        BubbleGroupFactory.LoadFullyIfNeeded(@group);

                        if (@group.ReadTimes == null || [email protected])
                        {
                            @group.ReadTimes = new []
                            {
                                new DisaReadTime
                                {
                                    ParticipantAddress = readBubble.ParticipantAddress,
                                    Time = readBubble.ReadTime
                                }
                            };
                        }
                        else
                        {
                            var readTimes = @group.ReadTimes.ToList();

                            var duplicateReadTime = readTimes.FirstOrDefault(x =>
                                                                             @group.Service.BubbleGroupComparer(x.ParticipantAddress, readBubble.ParticipantAddress));
                            if (duplicateReadTime != null)
                            {
                                readTimes.Remove(duplicateReadTime);
                            }

                            readTimes.Add(new DisaReadTime
                            {
                                ParticipantAddress = readBubble.ParticipantAddress,
                                Time = readBubble.ReadTime
                            });

                            @group.ReadTimes = readTimes.ToArray();
                        }
                    }
                }

                var prescenceBubble = b as PresenceBubble;
                if (prescenceBubble != null)
                {
                    @group = BubbleGroupManager.Find(bubbleGroup =>
                                                     !bubbleGroup.IsParty &&
                                                     bubbleGroup.Service ==
                                                     prescenceBubble.Service &&
                                                     prescenceBubble.Service.BubbleGroupComparer(
                                                         bubbleGroup.Address,
                                                         prescenceBubble.Address));

                    if (@group != null)
                    {
                        if (group.Presence == prescenceBubble.Available)
                        {
                            skipEvent = true;
                        }
                        else
                        {
                            if (!prescenceBubble.Available)
                            {
                                @group.PresenceType = PresenceBubble.PresenceType.Unavailable;
                            }
                            else
                            {
                                @group.PresenceType         = prescenceBubble.Presence;
                                @group.PresencePlatformType = prescenceBubble.Platform;
                            }

                            if (!prescenceBubble.Available)
                            {
                                group.SendBubbleActions.Clear();
                            }
                        }
                    }
                }

                var typingBubble = b as TypingBubble;
                if (typingBubble != null)
                {
                    @group = BubbleGroupManager.Find(bubbleGroup =>
                                                     bubbleGroup.Service ==
                                                     typingBubble.Service &&
                                                     typingBubble.Service.BubbleGroupComparer(
                                                         bubbleGroup.Address,
                                                         typingBubble.Address));

                    if (@group != null)
                    {
                        if (!group.IsParty)
                        {
                            group.SendBubbleActions.Clear();
                            if (group.Presence)
                            {
                                group.SendBubbleActions.Add(new SendBubbleAction
                                {
                                    Type = typingBubble.Typing ? (typingBubble.IsAudio ?
                                                                  SendBubbleAction.ActionType.Recording : SendBubbleAction.ActionType.Typing) :
                                           SendBubbleAction.ActionType.Nothing,
                                    Address = group.Address,
                                });
                            }
                        }
                        else
                        {
                            var sendBubbleAction = new SendBubbleAction
                            {
                                Address = typingBubble.ParticipantAddress,
                                Type    = typingBubble.Typing ? (typingBubble.IsAudio ?
                                                                 SendBubbleAction.ActionType.Recording : SendBubbleAction.ActionType.Typing) :
                                          SendBubbleAction.ActionType.Nothing,
                            };
                            var skipAdd = false;
                            foreach (var item in group.SendBubbleActions)
                            {
                                if (group.Service.BubbleGroupComparer(item.Address, sendBubbleAction.Address))
                                {
                                    if (sendBubbleAction.Type == item.Type)
                                    {
                                        skipAdd = true;
                                    }
                                    else
                                    {
                                        group.SendBubbleActions.Remove(item);
                                    }
                                    break;
                                }
                            }
                            if (!skipAdd && sendBubbleAction.Type != SendBubbleAction.ActionType.Nothing)
                            {
                                group.SendBubbleActions.Add(sendBubbleAction);
                            }
                        }
                    }
                }

                try
                {
                    if (@group != null && !skipEvent)
                    {
                        BubbleGroupEvents.RaiseNewAbstractBubble(b as AbstractBubble, @group);
                    }
                }
                catch (Exception ex)
                {
                    Utils.DebugPrint("Problem in OnBubbleReceived (AbstractBubble) from service " +
                                     b.Service.Information.ServiceName + ": " + ex.Message);
                }
            }
        }