private static List <VisualBubble> LoadBubblesIntoQueryAgent(Agent innerGroupAgent, BubbleGroup innerGroup, long currentTime, int maxLoadPerGroup, LoadBubblesIntoStateHolder innerGroupState) { try { var task = innerGroupAgent.LoadBubbles(innerGroup, currentTime, maxLoadPerGroup); task.Wait(); var loadedBubbles = task.Result; if (loadedBubbles == null || loadedBubbles.Count == 0) { innerGroupState.Dead = true; return(null); } else { BubbleManager.AddUrlMarkupIfNeeded(loadedBubbles); return(loadedBubbles); } } catch (Exception ex) { Utils.DebugPrint("Loading bubbles for service " + innerGroup.Service.Information.ServiceName + " on group " + innerGroup.ID + " failed: " + ex); innerGroupState.Dead = true; } return(null); }
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 associatedGroups = unifiedGroup != null ? unifiedGroup.Groups.ToList() : new[] { @group }.ToList(); var associatedPartiallyLoadedGroups = associatedGroups.Where(x => x.PartiallyLoaded).ToList(); foreach (var partiallyLoadedGroup in associatedPartiallyLoadedGroups) { loadedSomething = true; var partiallyLoadedBubblesToRemove = partiallyLoadedGroup.Bubbles.ToList(); foreach (var bubble in BubbleGroupDatabase.FetchBubbles(partiallyLoadedGroup).Reverse()) { partiallyLoadedGroup.Bubbles.Add(bubble); } foreach (var partiallyLoadedBubbleToRemove in partiallyLoadedBubblesToRemove) { partiallyLoadedGroup.Bubbles.Remove(partiallyLoadedBubbleToRemove); } partiallyLoadedGroup.PartiallyLoaded = false; } if (unifiedGroup != null && !unifiedGroup.UnifiedGroupLoaded) { Populate(unifiedGroup); } } 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 }); } BubbleManager.SetNotQueuedToFailures(@group); }); } return(loadedSomething); }
public Result(Type resultType, string newActionId, VisualBubble[] updates, VisualBubble[] inserts) { NewActionId = newActionId; Updates = updates; Inserts = inserts; ResultType = resultType; BubbleManager.AddUrlMarkupIfNeeded(updates); BubbleManager.AddUrlMarkupIfNeeded(inserts); }
internal static void UpdateLastOnline(BubbleGroup bubbleGroup, bool updateUi = true, bool fromProcessUpdateLastOnlineQueue = false) { var service = bubbleGroup.Service; if (!fromProcessUpdateLastOnlineQueue) { if (!ServiceManager.IsRunning(service) && !bubbleGroup.IsParty) { lock (UpdateLastOnlineQueue) UpdateLastOnlineQueue.Add(new Tuple <BubbleGroup, bool>(bubbleGroup, updateUi)); return; } } if (!ServiceManager.IsRunning(service) || bubbleGroup.IsParty) { return; } // reject the last online update if the Presence is currently available if (bubbleGroup.Presence) { return; } try { service.GetBubbleGroupLastOnline(bubbleGroup, time => { // reject the last online update if the Presence is currently available if (bubbleGroup.Presence) { return; } bubbleGroup.PresenceType = PresenceBubble.PresenceType.Unavailable; bubbleGroup.LastSeen = time; if (updateUi) { BubbleGroupEvents.RaiseInformationUpdated(bubbleGroup); } //we constantly need to subscribe to a bubble group. doing it //in last online method is the most effective. BubbleManager.SendSubscribe(bubbleGroup, true); //Presence(service, true); }); } catch (Exception ex) { Utils.DebugPrint("Error updating bubble group last online: " + service.Information.ServiceName + ": " + ex.Message); } }
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); } }
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); }
public static Task Start(Service service, bool smartStart = false, int smartStartSeconds = 10) { return(Task.Factory.StartNew(() => { using (var wakeLock = Platform.AquireWakeLock("DisaStart")) { if (IsRunning(service)) { Utils.DebugPrint( "The service is already running. Preventing possible deadlock. SmartStart? " + smartStart); return; } if (IsStarting(service)) { Utils.DebugPrint( "The service is being started. Preventing possible deadlock. SmartStart? " + smartStart); return; } Action epilogue = () => { GetFlags(service).Starting = false; }; lock (service) { GetFlags(service).Aborted = false; GetFlags(service).AbortedSpecial = false; GetFlags(service).Starting = true; GetFlags(service).ManualSettingsNeeded = false; Utils.DebugPrint("Loading settings for service " + service.Information.ServiceName); try { var settings = SettingsManager.Load(service); if (settings == null) { Utils.DebugPrint("Failed to load saved settings for " + service.Information.ServiceName + ". Will try to initialize with no settings..."); if (!service.InitializeDefault()) { Utils.DebugPrint( "Service doesn't allow initializing without settings. Needs manual input."); GetFlags(service).ManualSettingsNeeded = true; ServiceEvents.RaiseServiceManualSettingsNeeded(service); } else { Utils.DebugPrint("Service initialized under no settings."); } } else { Utils.DebugPrint("Loading saved settings! Initializing..."); if (service.Initialize(settings)) { Utils.DebugPrint("Successfully initialized service!"); } else { Utils.DebugPrint("Failed to initialize service. Needs manual input."); GetFlags(service).ManualSettingsNeeded = true; ServiceEvents.RaiseServiceManualSettingsNeeded(service); } } } catch (Exception ex) { Utils.DebugPrint("Failed: " + ex); epilogue(); return; } Utils.DebugPrint("Starting service " + service.Information.ServiceName); try { if (service.Information.UsesInternet && !Platform.HasInternetConnection()) { throw new Exception("No internet connection. Cannot connect service: " + service.Information.ServiceName); } StartInternal(service, wakeLock); } catch (ServiceSchedulerException ex) { Utils.DebugPrint("Problem in scheduler: " + ex.Message); epilogue(); return; } catch (ServiceSpecialRestartException) { Utils.DebugPrint("Service " + service.Information.ServiceName + " is asking to be restarted on connect/authenticate. This should be called sparingly, Disa can easily " + "break under these circumstances. Restarting..."); StopInternal(service); epilogue(); Start(service, smartStart, smartStartSeconds); return; } catch (ServiceExpiredException) { Utils.DebugPrint("The service " + service.Information.ServiceName + " has expired. :("); GetFlags(service).Aborted = true; GetFlags(service).Expired = true; ServiceEvents.RaiseServiceExpired(service); StopInternal(service); epilogue(); return; } catch (Exception ex) { if (smartStart) { StopInternal(service, false); if (smartStartSeconds > 600) { Utils.DebugPrint("Service " + service.Information.ServiceName + " needs to wait over 10minutes to be restarted." + " Killing SmartStart. The service will not be restarted. Reason: " + ex); epilogue(); return; } Utils.DebugPrint("Service " + service.Information.ServiceName + " failed to be started. SmartStart enabled. " + "Service being scheduled to be re-started in T-" + smartStartSeconds + " seconds! Reason: " + ex); var hasSmartStart = new object(); service.HasSmartStart = hasSmartStart; Platform.ScheduleAction(smartStartSeconds, new WakeLockBalancer.ActionObject(() => { if (IsAborted(service)) { Utils.DebugPrint( "Service " + service.Information .ServiceName + " tried to be started, but it deemed killed."); return; } if (service.HasSmartStart != hasSmartStart) { Utils.DebugPrint( "This smart start has been invalidated. There " + "seems to be another one on the block."); return; } Utils.DebugPrint( "Smart start is firing the service " + service.Information .ServiceName + " up again!"); StopInternal(service); Start(service, true, smartStartSeconds * 2); }, WakeLockBalancer.ActionObject.ExecuteType.TaskWithWakeLock)); epilogue(); return; } Utils.DebugPrint("Failed to start service " + service.Information.ServiceName + " (No SmartStart) : " + ex); StopInternal(service, false); epilogue(); return; } BubbleManager.SendSubscribe(service, true); BubbleManager.SendLastPresence(service); service.ReceivingBubblesThread = new Thread(() => { try { StartReceiveBubbles(service); } catch (ThreadAbortException) { Utils.DebugPrint( "Abort thread excepton in receiving bubbles on service (outer thread) " + service.Information.ServiceName); } catch (Exception ex) { Utils.DebugPrint(">>>>>>>>> " + ex.Message + " " + ex.StackTrace); } Utils.DebugPrint("Receiving bubbles for service " + service.Information.ServiceName + " has come to an end."); }); service.ReceivingBubblesThread.Start(); GetFlags(service).Starting = false; BubbleManager.SetNotQueuedToFailures(service); Utils.Delay(1000).ContinueWith(x => { BubbleGroupSync.ResetSyncsIfHasAgent(service); BubbleGroupUpdater.Update(service); BubbleQueueManager.Send(new[] { service.Information.ServiceName }); BubbleGroupManager.ProcessUpdateLastOnlineQueue(service); }); } } })); }
internal static void OnBubbleReceived(Bubble b) { var visualBubble = b as VisualBubble; if (visualBubble != null) { try { BubbleManager.Group(visualBubble); } 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) { Utils.DebugPrint("We got an abstract bubble: " + b.GetType().Name + " Address: " + b.Address); 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 ([email protected] && !prescenceBubble.Available) { var oldPresence = @group.PresenceType; @group.PresenceType = PresenceBubble.PresenceType.Unavailable; if (oldPresence != prescenceBubble.Presence) { BubbleGroupManager.UpdateLastOnline(@group); } } else { @group.PresenceType = prescenceBubble.Presence; @group.PresencePlatformType = prescenceBubble.Platform; } if (!prescenceBubble.Available) { @group.Typing = false; } } } var typingBubble = b as TypingBubble; if (typingBubble != null) { @group = BubbleGroupManager.Find(bubbleGroup => !bubbleGroup.IsParty && bubbleGroup.Service == typingBubble.Service && typingBubble.Service.BubbleGroupComparer( bubbleGroup.Address, typingBubble.Address)); if (@group != null) { if (@group.Presence) { @group.Typing = typingBubble.Typing; @group.TypingIsAudio = typingBubble.IsAudio; } else { @group.Typing = false; } } } try { if (@group != null) { BubbleGroupEvents.RaiseNewAbstractBubble(b as AbstractBubble, @group); } } catch (Exception ex) { Utils.DebugPrint("Problem in OnBubbleReceived (AbstractBubble) from service " + b.Service.Information.ServiceName + ": " + ex.Message); } } }
public static Task <List <Receipt> > Send(string[] serviceNames, bool scheduled = false) { return(Task <List <Receipt> > .Factory.StartNew(() => { //PROCESS: 1) find all bubbles under serviceNames in db // 2) relate db entries to bubbles loaded into memory by Disa // 3) parallel send bubbles based respective to service // 4) delete all successful bubbles out of db lock (_sendLock) { var possibleBubblesFromDatabase = new List <Entry>(); lock (_dbLock) { using (var db = new SqlDatabase <Entry>(Location)) { foreach (var possibleBubble in db.Store.Where(x => serviceNames.Contains(x.ServiceName)).Reverse()) { if (!InsertBubble.IsSending(possibleBubble.Guid)) { possibleBubblesFromDatabase.Add(possibleBubble); } } } } var possibleBubblesInDisa = new List <Tuple <Entry, VisualBubble> >(); foreach (var bubbleGroup in BubbleGroupManager.BubbleGroupsNonUnified) { if (bubbleGroup.PartiallyLoaded) { continue; } foreach (var bubble in bubbleGroup) { if (bubble.Status != Bubble.BubbleStatus.Waiting) { continue; } var possibleBubbleFromDatabase = possibleBubblesFromDatabase.FirstOrDefault(x => x.Guid == bubble.ID); if (possibleBubbleFromDatabase != null) { possibleBubblesInDisa.Add(new Tuple <Entry, VisualBubble>(possibleBubbleFromDatabase, bubble)); } } } var sent = new List <Tuple <Entry, bool> >(); var possibleBubblesInDisaByService = possibleBubblesInDisa.GroupBy(x => x.Item1.ServiceName); Parallel.ForEach(possibleBubblesInDisaByService, possibleBubblesInService => { var failed = false; foreach (var possibleBubble in possibleBubblesInService) { if (failed) { sent.Add(new Tuple <Entry, bool>(possibleBubble.Item1, false)); continue; } Utils.DebugPrint(">>>>>>>>>>> Sending queued bubble on " + possibleBubble.Item2.Service.Information.ServiceName + "!"); var sendBubbleTask = BubbleManager.Send(possibleBubble.Item2, true); sendBubbleTask.Wait(); if (sendBubbleTask.Result) { Utils.DebugPrint(">>>>>>>>> Successfully sent queued bubble on " + possibleBubble.Item2.Service.Information.ServiceName + "!"); sent.Add(new Tuple <Entry, bool>(possibleBubble.Item1, true)); lock (_dbLock) { using (var db = new SqlDatabase <Entry>(Location)) { db.Remove(possibleBubble.Item1); } } Utils.Delay(100).Wait(); } else { Utils.DebugPrint(">>>>>>>>> Failed to send queued bubble on " + possibleBubble.Item2.Service.Information.ServiceName + "!"); sent.Add(new Tuple <Entry, bool>(possibleBubble.Item1, false)); failed = true; // fail the entire chain for this service from here on out so messages aren't sent out of order } } }); SanityCheckup(); var receipts = sent.Select(x => new Receipt(x.Item1.Guid, x.Item2)).ToList(); if (!scheduled) { if (receipts.FirstOrDefault(x => !x.Success) != null) { ScheduleReSend(serviceNames); } else { var needToSendAgain = serviceNames.Where(x => BubbleQueueManager.HasQueuedBubbles(x, true, true)).ToArray(); if (needToSendAgain.Any()) { Send(needToSendAgain); } } } return receipts; } })); }
public static Task Sync(BubbleGroup group, bool force = false) { return(Task.Factory.StartNew(() => { Utils.DebugPrint("Syncing convo " + group.ID); lock (SyncLock) { Utils.DebugPrint("Syncing convo " + group.ID + " (after lock)"); var somethingSynced = false; var groupsToSync = new List <BubbleGroup>(); var unifiedGroup = group as UnifiedBubbleGroup; if (unifiedGroup != null) { groupsToSync.AddRange(unifiedGroup.Groups); } else { groupsToSync.Add(group); } foreach (var groupToSync in groupsToSync) { if (!groupToSync.NeedsSync && !force) { continue; } var groupToSyncAgent = groupToSync.Service as Agent; if (groupToSyncAgent == null) { continue; } if (!ServiceManager.IsRunning(groupToSync.Service)) { continue; } somethingSynced = true; try { ReSync: var syncTime = Time.GetNowUnixTimestamp(); var syncTask = groupToSyncAgent.Sync(groupToSync, Database.GetActionId(groupToSync)); syncTask.Wait(); var syncResult = syncTask.Result; if (!syncResult.EmptyResult && !syncResult.NullActionId && !syncResult.JustRefresh) { lock (BubbleGroupDatabase.OperationLock) { if (syncResult.ResultType == Result.Type.Purge) { if (BubbleGroupManager.LastBubbleSentTimestamps.ContainsKey(groupToSync.ID)) { lock (BubbleGroupManager.LastBubbleSentTimestamps) { var lastBubbleSentTime = BubbleGroupManager.LastBubbleSentTimestamps[groupToSync.ID]; if (lastBubbleSentTime >= syncTime) { Utils.DebugPrint("Sync has detected that a bubble was sent during the time the sync was fetched. Redoing sync..."); goto ReSync; } } } Utils.DebugPrint("Sync is purging the database for bubble group " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName); BubbleGroupDatabase.Kill(groupToSync); } if (!syncResult.EmptyInserts) { Utils.DebugPrint("Sync is inserting " + syncResult.Inserts.Length + " bubbles into " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName); if (syncResult.ResultType == Result.Type.Purge) { syncResult.Inserts.TimSort((x, y) => x.Time.CompareTo(y.Time)); BubbleGroupDatabase.AddBubbles(groupToSync, syncResult.Inserts); // If a purge is issued, then we need to carry over all sending bubbles to the new BubbleGroup being created. // Additionally, we need to remove any sending bubbles that are already on the insert list to avoid duplicates. var sendingBubbles = BubbleManager.FetchAllSending(groupToSync).ToList(); var sendingBubblesFiltered = new List <VisualBubble>(); foreach (var sendingBubble in sendingBubbles) { var hasInsertBubble = syncResult.Inserts.FirstOrDefault(x => x.ID == sendingBubble.ID) != null; if (!hasInsertBubble) { sendingBubblesFiltered.Add(sendingBubble); } } if (sendingBubblesFiltered.Any()) { BubbleGroupDatabase.InsertBubblesByTime(groupToSync, sendingBubblesFiltered.ToArray(), SearchDepth); } } else { BubbleGroupDatabase.InsertBubblesByTime(groupToSync, syncResult.Inserts, SearchDepth); } } if (!syncResult.EmptyUpdates) { Utils.DebugPrint("Sync is updating " + syncResult.Updates.Length + " bubbles into " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName); BubbleManager.Update(groupToSync, syncResult.Updates, SearchDepth); } } } else { Utils.DebugPrint("Sync for bubble group " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName + " returned an empty result (" + syncResult.ResultType.ToString() + ")."); } if (!syncResult.NullActionId) { Database.SetActionId(groupToSync, syncResult.NewActionId); } groupToSync.NeedsSync = false; } catch (Exception ex) { Utils.DebugPrint("Failed to sync bubble group " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName + ": " + ex); } } if (somethingSynced) { lock (BubbleGroupDatabase.OperationLock) { // Here we need to replace all the sending or downloading bubbles with the 'real' ones. // If this is never done, then on the UI, a 'sending' will never be set to 'sent', and any download // progress will never be updated. Why? Different memory objects. var sendingBubbles = BubbleManager.FetchAllSendingAndDownloading(group).ToList(); BubbleGroupFactory.UnloadFullLoad(group); BubbleGroupFactory.LoadFullyIfNeeded(group, true); if (sendingBubbles.Any()) { BubbleManager.Replace(group, sendingBubbles); } group.RaiseBubblesSynced(); } } } })); }
public static Task Sync(BubbleGroup group, bool force = false) { return(Task.Factory.StartNew(() => { Utils.DebugPrint("Syncing convo " + group.ID); lock (SyncLock) { Utils.DebugPrint("Syncing convo " + group.ID + " (after lock)"); var somethingSynced = true; var groupsToSync = new List <BubbleGroup>(); var unifiedGroup = group as UnifiedBubbleGroup; if (unifiedGroup != null) { groupsToSync.AddRange(unifiedGroup.Groups); } else { groupsToSync.Add(group); } foreach (var groupToSync in groupsToSync) { if (!groupToSync.NeedsSync && !force) { continue; } var groupToSyncAgent = groupToSync.Service as Agent; if (groupToSyncAgent == null) { continue; } if (!ServiceManager.IsRunning(groupToSync.Service)) { continue; } try { var syncTask = groupToSyncAgent.Sync(groupToSync, Database.GetActionId(groupToSync)); syncTask.Wait(); var syncResult = syncTask.Result; if (!syncResult.EmptyResult && !syncResult.NullActionId && !syncResult.JustRefresh) { lock (BubbleGroupDatabase.OperationLock) { if (syncResult.ResultType == Result.Type.Purge) { Utils.DebugPrint("Sync is purging the database for bubble group " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName); BubbleGroupDatabase.Kill(groupToSync); } if (!syncResult.EmptyInserts) { Utils.DebugPrint("Sync is inserting " + syncResult.Inserts.Length + " bubbles into " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName); if (syncResult.ResultType == Result.Type.Purge) { syncResult.Inserts.TimSort((x, y) => x.Time.CompareTo(y.Time)); BubbleGroupDatabase.AddBubbles(groupToSync, syncResult.Inserts); } else { BubbleGroupDatabase.InsertBubblesByTime(groupToSync, syncResult.Inserts, SearchDepth); } } if (!syncResult.EmptyUpdates) { Utils.DebugPrint("Sync is updating " + syncResult.Updates.Length + " bubbles into " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName); BubbleManager.Update(groupToSync, syncResult.Updates, SearchDepth); } Database.SetActionId(groupToSync, syncResult.NewActionId); } } else { Utils.DebugPrint("Sync for bubble group " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName + " returned an empty result (" + syncResult.ResultType.ToString() + ")."); somethingSynced = syncResult.JustRefresh; } groupToSync.NeedsSync = false; } catch (Exception ex) { Utils.DebugPrint("Failed to sync bubble group " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName + ": " + ex); somethingSynced = false; } } if (somethingSynced) { lock (BubbleGroupDatabase.OperationLock) { var sendingBubbles = BubbleManager.FetchAllSendingAndDownloading(group).ToList(); BubbleGroupFactory.UnloadFullLoad(group); BubbleGroupFactory.LoadFullyIfNeeded(group, true); if (sendingBubbles.Any()) { BubbleManager.Replace(group, sendingBubbles); } group.RaiseBubblesSynced(); } } } })); }