Exemple #1
0
        public void InsertByTime(VisualBubble b)
        {
            if (Bubbles.Count > BubblesCapSize)
            {
                Bubbles.RemoveAt(0);
            }

            for (int i = Bubbles.Count - 1; i >= 0; i--)
            {
                var nBubble = Bubbles[i];
                if (nBubble.Time <= b.Time)
                {
                    // adding it to the end, we can do a simple contract
                    if (i == Bubbles.Count - 1)
                    {
                        Bubbles.Add(b);
                        if (b.Direction == Bubble.BubbleDirection.Incoming)
                        {
                            BubbleGroupSettingsManager.SetUnread(this, true);
                        }
                    }
                    // inserting, do a full contract
                    else
                    {
                        Bubbles.Insert(i + 1, b);
                    }
                    break;
                }

                // could not find a valid place to insert, then skip insertion.
                if (i == 0)
                {
                    return;
                }
            }

            if (Unified == null)
            {
                _bubblesInsertedCount++;
                if (_bubblesInsertedCount % 100 == 0)
                {
                    if (BubbleGroupSync.SupportsSyncAndIsRunning(this))
                    {
                        Action doSync = async() =>
                        {
                            using (Platform.AquireWakeLock("DisaSync"))
                            {
                                await Utils.Delay(1000);

                                await BubbleGroupSync.Sync(this, true);
                            }
                        };
                        doSync();
                    }
                }
            }

            RaiseBubbleInserted(b);
            RaiseUnifiedBubblesUpdatedIfUnified(b);
        }
Exemple #2
0
        public static UnifiedBubbleGroup CreateUnified(List <BubbleGroup> groups, BubbleGroup primaryGroup)
        {
            lock (BubbleGroupDatabase.OperationLock)
            {
                var unifiedGroupsToKill = new HashSet <UnifiedBubbleGroup>();
                foreach (var group in groups)
                {
                    if (group.IsUnified)
                    {
                        unifiedGroupsToKill.Add(group.Unified);
                        @group.DeregisterUnified();
                    }
                }
                foreach (var unifiedGroup in unifiedGroupsToKill)
                {
                    BubbleGroupManager.BubbleGroupsRemove(unifiedGroup);
                }
                BubbleGroupIndex.RemoveUnified(unifiedGroupsToKill.Select(x => x.ID).ToArray());

                var unified = CreateUnifiedInternal(groups, primaryGroup);
                BubbleGroupIndex.AddUnified(unified);
                BubbleGroupManager.BubbleGroupsAdd(unified);
                BubbleGroupSettingsManager.SetUnreadIndicatorGuid(unified, unified.LastBubbleSafe().ID, false);
                return(unified);
            }
        }
        protected internal void OnBubbleUpdated(VisualBubble bubble, BubbleGroup group)
        {
            if (Bubbles.Count >= BubblesCapSize)
            {
                Bubbles.RemoveAt(0);
            }

            var addedToEnd = false;

            var unreadIndicatorGuid = BubbleGroupSettingsManager.GetUnreadIndicatorGuid(this);

            for (int i = Bubbles.Count - 1; i >= 0; i--)
            {
                var nBubble = Bubbles[i];

                var unreadIndicatorIndex = -1;
                if (unreadIndicatorGuid != null && unreadIndicatorGuid == nBubble.ID)
                {
                    unreadIndicatorIndex = i;
                }

                if (nBubble.Time <= bubble.Time)
                {
                    // adding it to the end, we can do a simple contract
                    if (i == Bubbles.Count - 1)
                    {
                        addedToEnd = true;
                        Bubbles.Add(bubble);
                        if (bubble.Direction == Bubble.BubbleDirection.Incoming)
                        {
                            BubbleGroupSettingsManager.SetUnread(this, true);
                        }
                    }
                    // inserting, do a full contract
                    else
                    {
                        Bubbles.Insert(i + 1, bubble);
                    }
                    if (i >= unreadIndicatorIndex && bubble.Direction == Bubble.BubbleDirection.Outgoing)
                    {
                        BubbleGroupSettingsManager.SetUnreadIndicatorGuid(this, bubble.ID, false);
                    }
                    break;
                }

                // could not find a valid place to insert, then skip insertion.
                if (i == 0)
                {
                    return;
                }
            }

            if (SendingGroup != group && addedToEnd)
            {
                SendingGroup = group;
                BubbleGroupEvents.RaiseSendingServiceChange(this);
            }

            RaiseBubbleInserted(bubble);
        }
Exemple #4
0
        public static void UpdateLastUnreadSetTime(Service service, string address)
        {
            var bubbleGroup = FindWithAddress(service, address);

            if (bubbleGroup != null)
            {
                BubbleGroupSettingsManager.SetLastUnreadSetTime(bubbleGroup, Time.GetNowUnixTimestamp());
            }
        }
Exemple #5
0
        internal static UnifiedBubbleGroup CreateUnifiedInternal(List <BubbleGroup> groups, BubbleGroup primaryGroup, string id = null)
        {
            var service = ServiceManager.GetUnified();

            if (service == null)
            {
                return(null);
            }

            var newBubble = new NewBubble(Time.GetNowUnixTimestamp(), Bubble.BubbleDirection.Incoming,
                                          Guid.NewGuid() + "@unified", null, false, service);
            var unified = new UnifiedBubbleGroup(groups, primaryGroup, newBubble, id);

            if (id == null)
            {
                var unread = groups.FirstOrDefault(BubbleGroupSettingsManager.GetUnread) != null;
                BubbleGroupSettingsManager.SetUnread(unified, unread);
            }

            foreach (var group in groups)
            {
                @group.RegisterUnified(unified);
            }

            var associatedPartiallyLoadedGroups = groups.Where(x => x.PartiallyLoaded).ToList();

            if (associatedPartiallyLoadedGroups.Any())
            {
                VisualBubble latest = null;
                foreach (var innerGroup in unified.Groups)
                {
                    var current = innerGroup.Bubbles.Last();
                    if (latest == null || current.Time > latest.Time)
                    {
                        latest = current;
                    }
                }
                if (latest != null)
                {
                    unified.Bubbles.Clear();
                    unified.Bubbles.Add(latest);
                }
                return(unified);
            }

            Populate(unified);

            return(unified);
        }
        public static IEnumerable <VisualBubble> FindAllUnread(Service service, string address)
        {
            var bubbleGroup = BubbleGroupManager.FindWithAddress(service, address);

            if (bubbleGroup != null)
            {
                foreach (var bubble in bubbleGroup)
                {
                    if (bubble.Direction == Bubble.BubbleDirection.Incoming &&
                        bubble.Time >= BubbleGroupSettingsManager.GetLastUnreadSetTime(bubbleGroup))
                    {
                        yield return(bubble);
                    }
                }
            }
        }
Exemple #7
0
        private static void SetUnread(BubbleGroup group, bool unread, bool updateUi = true, bool skipSendReadBubble = false, bool onlySetIfServiceRunning = true)
        {
            var unifiedGroup = @group as UnifiedBubbleGroup;

            if (unifiedGroup != null)
            {
                foreach (var innerGroup in unifiedGroup.Groups)
                {
                    SetUnread(innerGroup, false, skipSendReadBubble);
                }
            }

            Action @do = () =>
            {
                var updatedSomething = BubbleGroupSettingsManager.GetUnread(@group);
                BubbleGroupSettingsManager.SetUnread(@group, unread);

                if (unifiedGroup == null)
                {
                    if (!skipSendReadBubble)
                    {
                        if (@group.Service.Information.DoesSupport(typeof(ReadBubble)) && ServiceManager.IsRunning(@group.Service))
                        {
                            BubbleManager.Send(new ReadBubble(Time.GetNowUnixTimestamp(), Bubble.BubbleDirection.Outgoing,
                                                              @group.Service, @group.Address, null, Time.GetNowUnixTimestamp(), @group.IsParty, updatedSomething));
                        }
                    }
                }

                if (updateUi)
                {
                    BubbleGroupEvents.RaiseRefreshed(@group);
                }
            };

            if (onlySetIfServiceRunning && ServiceManager.IsRunning(@group.Service))
            {
                @do();
            }
            else if (!onlySetIfServiceRunning)
            {
                @do();
            }
        }
        private static void SetUnread(BubbleGroup group, bool unread, bool updateUi = true, bool skipSendReadBubble = false, bool onlySetIfServiceRunning = true)
        {
            var unifiedGroup = @group as UnifiedBubbleGroup;

            if (unifiedGroup != null)
            {
                foreach (var innerGroup in unifiedGroup.Groups)
                {
                    SetUnread(innerGroup, unread, false, skipSendReadBubble, onlySetIfServiceRunning);
                }
            }

            var currentlyUnread = BubbleGroupSettingsManager.GetUnread(@group);

            if ((onlySetIfServiceRunning && ServiceManager.IsRunning(@group.Service)) || !onlySetIfServiceRunning)
            {
                BubbleGroupSettingsManager.SetUnread(@group, unread);
                if (unifiedGroup == null)
                {
                    if (!skipSendReadBubble)
                    {
                        if (@group.Service.Information.DoesSupport(typeof(ReadBubble)) && ServiceManager.IsRunning(@group.Service))
                        {
                            var readBubble = new ReadBubble(Time.GetNowUnixTimestamp(), Bubble.BubbleDirection.Outgoing,
                                                            @group.Service, @group.Address, null, Time.GetNowUnixTimestamp(), @group.IsParty, currentlyUnread);
                            if (@group.IsExtendedParty)
                            {
                                readBubble.ExtendedParty = true;
                            }
                            BubbleManager.Send(readBubble);
                        }
                    }
                }
                if (updateUi)
                {
                    BubbleGroupEvents.RaiseRefreshed(@group);
                }
            }
            else
            {
                BubbleGroupSettingsManager.SetUnreadOffline(@group, unread);
            }
        }
Exemple #9
0
        private static BubbleGroup AddNewInternal(VisualBubble newBubble, bool raiseBubbleInserted)
        {
            var group = new BubbleGroup(newBubble, null, false);

            BubbleGroupSettingsManager.SetUnreadIndicatorGuid(group, group.LastBubbleSafe().ID, true);

            if (ServiceManager.IsRunning(@group.Service))
            {
                newBubble.Service.NewBubbleGroupCreated(@group).ContinueWith(x =>
                {
                    // force the UI to refetch the photo
                    @group.IsPhotoSetFromService = false;
                    BubbleManager.SendSubscribe(@group, true);
                    BubbleGroupUpdater.Update(@group);
                });
            }

            BubbleGroupManager.BubbleGroupsAdd(@group);

            BubbleGroupDatabase.AddBubble(@group, newBubble);

            if (raiseBubbleInserted)
            {
                try
                {
                    BubbleGroupEvents.RaiseBubbleInserted(newBubble, @group);
                }
                catch (Exception ex)
                {
                    Utils.DebugPrint(
                        "Error in notifying the interface that the bubble group has been updated (" +
                        newBubble.Service.Information.ServiceName + "): " + ex.Message);
                }
            }

            BubbleGroupUpdater.Update(@group);

            return(@group);
        }
Exemple #10
0
 internal static void LoadAllPartiallyIfPossible()
 {
     BubbleGroupIndex.Load();
     BubbleGroupSettingsManager.Load();
     BubbleGroupCacheManager.LoadAll();
 }
Exemple #11
0
        public static bool LoadFullyIfNeeded(BubbleGroup group, bool sync = false)
        {
            if (@group == null)
            {
                return(false);
            }

            var loadedSomething = false;

            lock (BubbleGroupDatabase.OperationLock)
            {
                var unifiedGroup = @group as UnifiedBubbleGroup;

                var adjustUnifiedGroupUnreadIndicatorIfExists = false;
                var associatedGroups = unifiedGroup != null
                    ? unifiedGroup.Groups.ToList()
                    : new[] { @group }.ToList();
                var associatedPartiallyLoadedGroups = associatedGroups.Where(x => x.PartiallyLoaded).ToList();
                foreach (var partiallyLoadedGroup in associatedPartiallyLoadedGroups)
                {
                    var partiallyLoadedBubblesToRemove = partiallyLoadedGroup.Bubbles.ToList();
                    var rollingBack = false;
TryAgain:
                    try
                    {
                        loadedSomething = true;
                        partiallyLoadedGroup.PartiallyLoaded = false;
                        foreach (var bubble in BubbleGroupDatabase.FetchBubbles(partiallyLoadedGroup).Reverse())
                        {
                            partiallyLoadedGroup.Bubbles.Add(bubble);
                        }
                        foreach (var partiallyLoadedBubbleToRemove in partiallyLoadedBubblesToRemove)
                        {
                            partiallyLoadedGroup.Bubbles.Remove(partiallyLoadedBubbleToRemove);
                        }
                        if (partiallyLoadedGroup.Bubbles.Count < 1)
                        {
                            foreach (var partiallyLoadedBubbleToRemove in partiallyLoadedBubblesToRemove)
                            {
                                partiallyLoadedGroup.Bubbles.Add(partiallyLoadedBubbleToRemove);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Utils.DebugPrint("Failed to fully load partially loaded group " + partiallyLoadedGroup.ID + ": " + ex);
                        if (!rollingBack)
                        {
                            Utils.DebugPrint("Attempting to roll back the last transaction....");
                            BubbleGroupSettingsManager.SetUnreadIndicatorGuid(partiallyLoadedGroup, null, false);
                            adjustUnifiedGroupUnreadIndicatorIfExists = true;
                            rollingBack = true;
                            var lastModifiedIndex = BubbleGroupIndex.GetLastModifiedIndex(partiallyLoadedGroup.ID);
                            if (lastModifiedIndex.HasValue)
                            {
                                try
                                {
                                    BubbleGroupDatabase.RollBackTo(partiallyLoadedGroup, lastModifiedIndex.Value);
                                    goto TryAgain;
                                }
                                catch (Exception ex2)
                                {
                                    Utils.DebugPrint("Failed to rollback: " + ex2);
                                    // fall-through. It's unrecoverable!
                                }
                            }
                            else
                            {
                                // fall-through. It's unrecoverable!
                            }
                        }
                        Utils.DebugPrint("Partially loaded group is dead. Killing and restarting (lost data occurred).");
                        BubbleGroupDatabase.Kill(partiallyLoadedGroup);
                        BubbleGroupSync.RemoveFromSync(partiallyLoadedGroup);
                        BubbleGroupDatabase.AddBubbles(partiallyLoadedGroup,
                                                       partiallyLoadedBubblesToRemove.ToArray());
                    }
                }

                if (unifiedGroup != null && !unifiedGroup.UnifiedGroupLoaded)
                {
                    Populate(unifiedGroup);
                }
                if (unifiedGroup != null && adjustUnifiedGroupUnreadIndicatorIfExists)
                {
                    BubbleGroupSettingsManager.SetUnreadIndicatorGuid(unifiedGroup, null, false);
                }
            }

            if (!sync && loadedSomething)
            {
                Task.Factory.StartNew(() =>
                {
                    var unifiedGroup = @group as UnifiedBubbleGroup;
                    if (unifiedGroup != null)
                    {
                        BubbleQueueManager.Send(unifiedGroup.Groups.Where(x => ServiceManager.IsRunning(x.Service))
                                                .Select(x => x.Service.Information.ServiceName).ToArray());
                    }
                    else if (ServiceManager.IsRunning(@group.Service))
                    {
                        BubbleQueueManager.Send(new[] { @group.Service.Information.ServiceName });
                    }
                    BubbleQueueManager.SetNotQueuedToFailures(@group);
                });
            }

            return(loadedSomething);
        }
Exemple #12
0
        internal static BubbleGroup Group(VisualBubble vb, bool resend = false, bool insertAtBottom = false)
        {
            lock (BubbleGroupDatabase.OperationLock)
            {
                Utils.DebugPrint("Grouping an " + vb.Direction + " bubble on service " + vb.Service.Information.ServiceName);

                var theGroup =
                    BubbleGroupManager.FindWithAddress(vb.Service, vb.Address);

                BubbleGroupFactory.LoadFullyIfNeeded(theGroup);

                var duplicate = false;
                var newGroup  = false;
                if (theGroup == null)
                {
                    Utils.DebugPrint(vb.Service.Information.ServiceName + " unable to find suitable group. Creating a new one.");

                    theGroup = new BubbleGroup(vb, null, false);

                    newGroup = true;

                    Utils.DebugPrint("GUID of new group: " + theGroup.ID);

                    BubbleGroupSettingsManager.SetUnreadIndicatorGuid(theGroup, theGroup.LastBubbleSafe().ID, true);

                    vb.Service.NewBubbleGroupCreated(theGroup).ContinueWith(x =>
                    {
                        // force the UI to refetch the photo
                        theGroup.IsPhotoSetFromService = false;
                        SendSubscribe(theGroup, true);
                        BubbleGroupUpdater.Update(theGroup);
                    });

                    BubbleGroupManager.BubbleGroupsAdd(theGroup);
                }
                else
                {
                    if (resend)
                    {
                        if (vb.Status == Bubble.BubbleStatus.Failed)
                        {
                            UpdateStatus(vb, Bubble.BubbleStatus.Waiting, theGroup);
                        }
                        return(theGroup);
                    }

                    var visualBubbleServiceId = vb.Service as IVisualBubbleServiceId;
                    if (visualBubbleServiceId != null &&
                        visualBubbleServiceId.DisctinctIncomingVisualBubbleIdServices())
                    {
                        if (vb.IdService != null)
                        {
                            duplicate = theGroup.Bubbles.FirstOrDefault(x => x.GetType() == vb.GetType() && x.IdService == vb.IdService) != null;
                        }
                        if (!duplicate && vb.IdService2 != null)
                        {
                            duplicate = theGroup.Bubbles.FirstOrDefault(x => x.GetType() == vb.GetType() && x.IdService2 == vb.IdService2) != null;
                        }
                    }

                    if (!duplicate)
                    {
                        Utils.DebugPrint(vb.Service.Information.ServiceName + " found a group. Adding.");

                        if (insertAtBottom)
                        {
                            var lastBubble = theGroup.LastBubbleSafe();
                            if (lastBubble.Time > vb.Time)
                            {
                                vb.Time = lastBubble.Time;
                            }
                        }

                        theGroup.InsertByTime(vb);
                    }
                    else
                    {
                        Utils.DebugPrint("Yuck. It's a duplicate bubble. No need to readd: " + vb.IdService + ", " + vb.IdService2);
                    }
                }

                try
                {
                    if (theGroup.IsParty && !string.IsNullOrWhiteSpace(vb.ParticipantAddressNickname))
                    {
                        var participantAddressNicknamesArray = theGroup.ParticipantNicknames;
                        if (participantAddressNicknamesArray == null)
                        {
                            participantAddressNicknamesArray = new DisaParticipantNickname[0];
                        }
                        var participantAddressNicknames = participantAddressNicknamesArray.ToList();
                        var changed = false;
                        var adding  = true;
                        foreach (var participantAddressNickname in participantAddressNicknames)
                        {
                            if (theGroup.Service.BubbleGroupComparer(participantAddressNickname.Address, vb.ParticipantAddress))
                            {
                                if (participantAddressNickname.Nickname != vb.ParticipantAddressNickname)
                                {
                                    participantAddressNickname.Nickname = vb.ParticipantAddressNickname;
                                    changed = true;
                                }
                                adding = false;
                                break;
                            }
                        }
                        if (adding)
                        {
                            participantAddressNicknames.Add(new DisaParticipantNickname
                            {
                                Address  = vb.ParticipantAddress,
                                Nickname = vb.ParticipantAddressNickname,
                            });
                        }
                        if (changed || adding)
                        {
                            theGroup.ParticipantNicknames = participantAddressNicknames.ToArray();
                        }
                    }
                }
                catch (Exception ex)
                {
                    Utils.DebugPrint("Failed to insert/update participant nickname into cache: " + ex);
                }

                if (!duplicate)
                {
                    Utils.DebugPrint("Inserting bubble into database group!");

                    try
                    {
                        if (newGroup)
                        {
                            BubbleGroupDatabase.AddBubble(theGroup, vb);
                        }
                        else
                        {
                            BubbleGroupDatabase.InsertBubbleByTime(theGroup, vb);
                        }
                    }
                    catch (Exception ex)
                    {
                        Utils.DebugPrint("Bubble failed to be inserting/added into the group " + theGroup.ID + ": " + ex);
                    }

                    try
                    {
                        BubbleGroupEvents.RaiseBubbleInserted(vb, theGroup);
                    }
                    catch (Exception ex)
                    {
                        Utils.DebugPrint(
                            "Error in notifying the interface that the bubble group has been updated (" +
                            vb.Service.Information.ServiceName + "): " + ex.Message);
                    }
                }

                return(theGroup);
            }
        }
        internal static void LoadAllPartiallyIfPossible(int bubblesPerGroup = 100)
        {
            var corruptedGroups = new List <string>();

            var bubbleGroupsLocations       = Directory.GetFiles(BubbleGroupDatabase.GetBaseLocation(), "*.*", SearchOption.AllDirectories);
            var bubbleGroupsLocationsSorted =
                bubbleGroupsLocations.OrderByDescending(x => Time.GetUnixTimestamp(File.GetLastWriteTime(x))).ToList();

            foreach (var bubbleGroupLocation in bubbleGroupsLocationsSorted)
            {
                String groupId = null;
                try
                {
                    var groupHeader = Path.GetFileNameWithoutExtension(bubbleGroupLocation);

                    var groupDelimeter = groupHeader.IndexOf("^", StringComparison.Ordinal);
                    var serviceName    = groupHeader.Substring(0, groupDelimeter);
                    groupId = groupHeader.Substring(groupDelimeter + 1);

                    var service = ServiceManager.GetByName(serviceName);

                    if (service == null)
                    {
                        Utils.DebugPrint("Service " + serviceName +
                                         " not found in AllServices!");
                        continue;
                    }

                    var deserializedBubbleGroup = LoadPartiallyIfPossible(bubbleGroupLocation, service, groupId, bubblesPerGroup);
                    if (deserializedBubbleGroup == null)
                    {
                        throw new Exception("DeserializedBubbleGroup is nothing.");
                    }
                    BubbleGroupManager.BubbleGroupsAdd(deserializedBubbleGroup);
                }
                catch (Exception ex)
                {
                    Utils.DebugPrint("Group " + bubbleGroupLocation + " is corrupt. Deleting. " + ex);
                    File.Delete(bubbleGroupLocation);
                    if (groupId != null)
                    {
                        corruptedGroups.Add(groupId);
                    }
                }
            }

            var migrationResaveNeeded  = false;
            var corruptedUnifiedGroups =
                new List <SimpleDatabase <UnifiedBubbleGroup, DisaUnifiedBubbleGroupEntry> .Container>();
            var removeFromRuntime =
                new List <SimpleDatabase <UnifiedBubbleGroup, DisaUnifiedBubbleGroupEntry> .Container>();

            foreach (var group in UnifiedBubbleGroupsDatabase)
            {
                var innerGroupCorrupt = false;
                var innerGroups       = new List <BubbleGroup>();
                foreach (var innerGroupId in @group.Serializable.GroupIds)
                {
                    var innerGroup = BubbleGroupManager.Find(innerGroupId);
                    if (innerGroup == null)
                    {
                        Utils.DebugPrint("Unified group, inner group " + innerGroupId +
                                         " could not be related.");
                        if (corruptedGroups.Contains(innerGroupId))
                        {
                            Utils.DebugPrint(
                                "It was detected that this inner group was corrupted and deleted. Will delete unified group.");
                            innerGroupCorrupt = true;
                            corruptedUnifiedGroups.Add(@group);
                        }
                    }
                    else
                    {
                        innerGroups.Add(innerGroup);
                    }
                }
                if (!innerGroups.Any())
                {
                    Utils.DebugPrint("Yuck. This unified group has no inner groups. Removing from runtime.");
                    removeFromRuntime.Add(@group);
                    continue;
                }
                if (innerGroupCorrupt)
                {
                    continue;
                }

                var primaryGroup = innerGroups.FirstOrDefault(x => x.ID == @group.Serializable.PrimaryGroupId);
                if (primaryGroup == null)
                {
                    Utils.DebugPrint("Unified group, primary group " + @group.Serializable.PrimaryGroupId +
                                     " could not be related. Removing from runtime.");
                    removeFromRuntime.Add(@group);
                    continue;
                }

                var id = @group.Serializable.Id;

                var unifiedGroup = CreateUnifiedInternal(innerGroups, primaryGroup, id);
                if (id == null)
                {
                    migrationResaveNeeded  = true;
                    @group.Serializable.Id = unifiedGroup.ID;
                }

                var sendingGroup = innerGroups.FirstOrDefault(x => x.ID == @group.Serializable.SendingGroupId);
                if (sendingGroup != null)
                {
                    unifiedGroup.SendingGroup = sendingGroup;
                }

                @group.Object = unifiedGroup;
                BubbleGroupManager.BubbleGroupsAdd(unifiedGroup);
            }
            if (removeFromRuntime.Any())
            {
                foreach (var group in removeFromRuntime)
                {
                    UnifiedBubbleGroupsDatabase.Remove(@group);
                }
            }
            if (corruptedUnifiedGroups.Any())
            {
                foreach (var group in corruptedUnifiedGroups)
                {
                    UnifiedBubbleGroupsDatabase.Remove(@group);
                }
            }
            if (migrationResaveNeeded)
            {
                Utils.DebugPrint("It was detected that we need to save migration changes for UnifiedBubbleGroups.");
                UnifiedBubbleGroupsDatabase.SaveChanges();
            }

            try
            {
                foreach (var groupCache in BubbleGroupCacheManager.Load())
                {
                    var associatedGroup = BubbleGroupManager.Find(groupCache.Guid);
                    if (associatedGroup == null)
                    {
                        continue;
                    }

                    var unifiedGroup = associatedGroup as UnifiedBubbleGroup;
                    if (unifiedGroup != null)
                    {
                        associatedGroup = unifiedGroup.PrimaryGroup;
                    }

                    associatedGroup.Title = groupCache.Name;
                    associatedGroup.Photo = groupCache.Photo;
                    associatedGroup.IsPhotoSetInitiallyFromCache = true;
                    if (groupCache.Participants != null)
                    {
                        associatedGroup.Participants = groupCache.Participants.ToSynchronizedCollection();
                        foreach (var participant in associatedGroup.Participants)
                        {
                            participant.IsPhotoSetInitiallyFromCache = true;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Utils.DebugPrint("Failed to load bubble groups!: " + ex);
            }

            BubbleGroupSettingsManager.Load();
        }
Exemple #14
0
        internal static BubbleGroup Group(VisualBubble vb, bool resend = false, bool insertAtBottom = false)
        {
            lock (BubbleGroupDatabase.OperationLock)
            {
                Utils.DebugPrint("Grouping an " + vb.Direction + " bubble on service " + vb.Service.Information.ServiceName);

                AddUrlMarkupIfNeeded(vb);

                var theGroup =
                    BubbleGroupManager.FindWithAddress(vb.Service, vb.Address);

                BubbleGroupFactory.LoadFullyIfNeeded(theGroup);

                var duplicate = false;
                var newGroup  = false;
                if (theGroup == null)
                {
                    Utils.DebugPrint(vb.Service.Information.ServiceName + " unable to find suitable group. Creating a new one.");

                    theGroup = new BubbleGroup(vb, null, false);

                    newGroup = true;

                    Utils.DebugPrint("GUID of new group: " + theGroup.ID);

                    BubbleGroupSettingsManager.SetUnreadIndicatorGuid(theGroup, theGroup.LastBubbleSafe().ID, true);

                    vb.Service.NewBubbleGroupCreated(theGroup).ContinueWith(x =>
                    {
                        // force the UI to refetch the photo
                        theGroup.IsPhotoSetFromService = false;
                        SendSubscribe(theGroup, true);
                        BubbleGroupUpdater.Update(theGroup);
                    });

                    BubbleGroupManager.BubbleGroupsAdd(theGroup);
                }
                else
                {
                    if (resend)
                    {
                        if (vb.Status == Bubble.BubbleStatus.Failed)
                        {
                            UpdateStatus(vb, Bubble.BubbleStatus.Waiting, theGroup);
                        }
                        return(theGroup);
                    }

                    // Does the Service for this VisualBubble require that the VisualBubble's IdService and IdService2
                    // be distinct?
                    var visualBubbleServiceId = vb.Service as IVisualBubbleServiceId;
                    if (visualBubbleServiceId != null &&
                        visualBubbleServiceId.DisctinctIncomingVisualBubbleIdServices())
                    {
                        // Ok, we need to be distinct, BUT do the VisualBubble Type's have to be distinct as well?
                        var checkType = true;
                        if (!DisaFrameworkMethods.Missing(vb.Service, DisaFrameworkMethods.IVisualBubbleServiceIdCheckType))
                        {
                            checkType = visualBubbleServiceId.CheckType();
                        }

                        // Ok, now does the Service have special additional logic it wants to use for the distinction comparison?
                        // Example: For Telegram, we allow an ImageBubble immediately followed by a TextBubble to have the
                        //          same VisualBubble.IdService - as this represents an image with a caption in Telegram.
                        if (DisaFrameworkMethods.Missing(vb.Service, DisaFrameworkMethods.IVisualBubbleServiceIdVisualBubbleIdComparer))
                        {
                            // Normal distinction checks
                            if (vb.IdService != null)
                            {
                                duplicate = theGroup.Bubbles.FirstOrDefault(x =>
                                                                            (!checkType || x.GetType() == vb.GetType()) && x.IdService == vb.IdService) != null;
                            }
                            if (!duplicate && vb.IdService2 != null)
                            {
                                duplicate = theGroup.Bubbles.FirstOrDefault(x =>
                                                                            (!checkType || x.GetType() == vb.GetType()) && x.IdService2 == vb.IdService2) != null;
                            }
                        }
                        else
                        {
                            // Special additional Service defined distinction checks
                            if (vb.IdService != null)
                            {
                                var duplicateBubble = theGroup.Bubbles.FirstOrDefault(x =>
                                                                                      (!checkType || x.GetType() == vb.GetType()) && x.IdService == vb.IdService);

                                duplicate = duplicateBubble == null ? false :
                                            visualBubbleServiceId.VisualBubbleIdComparer(left: duplicateBubble, right: vb);
                            }
                            if (!duplicate && vb.IdService2 != null)
                            {
                                var duplicateBubble = theGroup.Bubbles.FirstOrDefault(x =>
                                                                                      (!checkType || x.GetType() == vb.GetType()) && x.IdService2 == vb.IdService2);

                                duplicate = duplicateBubble == null ? false :
                                            visualBubbleServiceId.VisualBubbleIdComparer(duplicateBubble, vb);
                            }
                        }
                    }

                    if (!duplicate)
                    {
                        Utils.DebugPrint(vb.Service.Information.ServiceName + " found a group. Adding.");

                        if (insertAtBottom)
                        {
                            var lastBubble = theGroup.LastBubbleSafe();
                            if (lastBubble.Time > vb.Time)
                            {
                                vb.Time = lastBubble.Time;
                            }
                        }

                        theGroup.InsertByTime(vb);
                    }
                    else
                    {
                        Utils.DebugPrint("Yuck. It's a duplicate bubble. No need to readd: " + vb.IdService + ", " + vb.IdService2);
                    }
                }

                try
                {
                    if (theGroup.IsParty && !string.IsNullOrWhiteSpace(vb.ParticipantAddressNickname))
                    {
                        var participantAddressNicknamesArray = theGroup.ParticipantNicknames;
                        if (participantAddressNicknamesArray == null)
                        {
                            participantAddressNicknamesArray = new DisaParticipantNickname[0];
                        }
                        var participantAddressNicknames = participantAddressNicknamesArray.ToList();
                        var changed = false;
                        var adding  = true;
                        foreach (var participantAddressNickname in participantAddressNicknames)
                        {
                            if (theGroup.Service.BubbleGroupComparer(participantAddressNickname.Address, vb.ParticipantAddress))
                            {
                                if (participantAddressNickname.Nickname != vb.ParticipantAddressNickname)
                                {
                                    participantAddressNickname.Nickname = vb.ParticipantAddressNickname;
                                    changed = true;
                                }
                                adding = false;
                                break;
                            }
                        }
                        if (adding)
                        {
                            participantAddressNicknames.Add(new DisaParticipantNickname
                            {
                                Address  = vb.ParticipantAddress,
                                Nickname = vb.ParticipantAddressNickname,
                            });
                        }
                        if (changed || adding)
                        {
                            theGroup.ParticipantNicknames = participantAddressNicknames.ToArray();
                        }
                    }
                }
                catch (Exception ex)
                {
                    Utils.DebugPrint("Failed to insert/update participant nickname into cache: " + ex);
                }

                if (!duplicate)
                {
                    Utils.DebugPrint("Inserting bubble into database group!");

                    try
                    {
                        if (newGroup)
                        {
                            BubbleGroupDatabase.AddBubble(theGroup, vb);
                        }
                        else
                        {
                            BubbleGroupDatabase.InsertBubbleByTime(theGroup, vb);
                        }
                    }
                    catch (Exception ex)
                    {
                        Utils.DebugPrint("Bubble failed to be inserting/added into the group " + theGroup.ID + ": " + ex);
                    }

                    try
                    {
                        BubbleGroupEvents.RaiseBubbleInserted(vb, theGroup);
                    }
                    catch (Exception ex)
                    {
                        Utils.DebugPrint(
                            "Error in notifying the interface that the bubble group has been updated (" +
                            vb.Service.Information.ServiceName + "): " + ex.Message);
                    }
                }

                return(theGroup);
            }
        }
Exemple #15
0
        public void InsertByTime(VisualBubble b)
        {
            if (Bubbles.Count >= BubblesCapSize)
            {
                Bubbles.RemoveAt(0);
            }

            if (!(this is ComposeBubbleGroup))
            {
                var unreadIndicatorGuid = BubbleGroupSettingsManager.GetUnreadIndicatorGuid(this);
                for (int i = Bubbles.Count - 1; i >= 0; i--)
                {
                    var nBubble = Bubbles[i];

                    var unreadIndicatorIndex = -1;
                    if (unreadIndicatorGuid != null && unreadIndicatorGuid == nBubble.ID)
                    {
                        unreadIndicatorIndex = i;
                    }

                    // IMPORTANT: Our Time field is specified in seconds, however scenarios are appearing
                    //            (e.g., bots) where messages are sent in on the same second but still require
                    //            proper ordering. In this case, Services may set a flag specifying a fallback
                    //            to the ID assigned by the Service (e.g. Telegram).
                    if ((nBubble.Time == b.Time) &&
                        (nBubble.IsServiceIdSequence && b.IsServiceIdSequence))
                    {
                        if (string.Compare(
                                strA: nBubble.IdService,
                                strB: b.IdService,
                                ignoreCase: false,
                                culture: CultureInfo.InvariantCulture) < 0)
                        {
                            //
                            // Incoming bubble must be placed AFTER current bubble we are evaluating
                            //

                            if (i == Bubbles.Count - 1)
                            {
                                Bubbles.Add(b);
                                if (b.Direction == Bubble.BubbleDirection.Incoming)
                                {
                                    BubbleGroupSettingsManager.SetUnread(this, true);
                                }
                            }
                            else
                            {
                                Bubbles.Insert(i + 1, b);
                                if (i >= unreadIndicatorIndex && b.Direction == Bubble.BubbleDirection.Outgoing)
                                {
                                    BubbleGroupSettingsManager.SetUnreadIndicatorGuid(this, b.ID, false);
                                }
                            }
                        }
                        else
                        {
                            //
                            // Incoming bubble must be placed BEFORE current bubble we are evaluating
                            //

                            Bubbles.Insert(i, b);
                            if (i >= unreadIndicatorIndex && b.Direction == Bubble.BubbleDirection.Outgoing)
                            {
                                BubbleGroupSettingsManager.SetUnreadIndicatorGuid(this, b.ID, false);
                            }
                        }
                        break;
                    }
                    // OK, simpler scenario, incoming bubble must be placed AFTER current bubble we are evaluating
                    else if (nBubble.Time <= b.Time)
                    {
                        // adding it to the end, we can do a simple contract
                        if (i == Bubbles.Count - 1)
                        {
                            Bubbles.Add(b);
                            if (b.Direction == Bubble.BubbleDirection.Incoming)
                            {
                                BubbleGroupSettingsManager.SetUnread(this, true);
                            }
                        }
                        // inserting, do a full contract
                        else
                        {
                            Bubbles.Insert(i + 1, b);
                        }
                        if (i >= unreadIndicatorIndex && b.Direction == Bubble.BubbleDirection.Outgoing)
                        {
                            BubbleGroupSettingsManager.SetUnreadIndicatorGuid(this, b.ID, false);
                        }
                        break;
                    }

                    // could not find a valid place to insert, then skip insertion.
                    if (i == 0)
                    {
                        return;
                    }
                }
            }

            if (Unified == null)
            {
                _bubblesInsertedCount++;
                if (_bubblesInsertedCount % 100 == 0)
                {
                    if (BubbleGroupSync.SupportsSyncAndIsRunning(this))
                    {
                        Action doSync = async() =>
                        {
                            using (Platform.AquireWakeLock("DisaSync"))
                            {
                                await Utils.Delay(1000);

                                await BubbleGroupSync.Sync(this, true);
                            }
                        };
                        doSync();
                    }
                }
            }

            RaiseBubbleInserted(b);
            RaiseUnifiedBubblesUpdatedIfUnified(b);
        }