X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fsone%2Fcore%2FCore.java;h=b0df7a9135a9254e438905c5edc6c97909d63061;hp=87f59c200557759db9e7584f95f0dcede2c03051;hb=b9fc06f5bf20b1c0d5e7c5e9b49e87581a843ee7;hpb=a108af80df7846c28e24e59e5710bee69627483b diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index 87f59c2..b0df7a9 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -1,5 +1,5 @@ /* - * Sone - Core.java - Copyright © 2010–2013 David Roden + * Sone - Core.java - Copyright © 2010–2019 David Roden * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,6 +25,7 @@ import static java.lang.String.format; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -39,14 +40,16 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidAlbumFound; import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidImageFound; import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidParentAlbumFound; import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidPostFound; import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidPostReplyFound; -import net.pterodactylus.sone.core.SoneChangeDetector.PostProcessor; -import net.pterodactylus.sone.core.SoneChangeDetector.PostReplyProcessor; import net.pterodactylus.sone.core.event.ImageInsertFinishedEvent; +import net.pterodactylus.sone.core.event.InsertionDelayChangedEvent; import net.pterodactylus.sone.core.event.MarkPostKnownEvent; import net.pterodactylus.sone.core.event.MarkPostReplyKnownEvent; import net.pterodactylus.sone.core.event.MarkSoneKnownEvent; @@ -67,8 +70,8 @@ import net.pterodactylus.sone.data.Profile; import net.pterodactylus.sone.data.Profile.Field; import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.sone.data.Sone.ShowCustomAvatars; import net.pterodactylus.sone.data.Sone.SoneStatus; +import net.pterodactylus.sone.data.SoneOptions.LoadExternalContent; import net.pterodactylus.sone.data.TemporaryImage; import net.pterodactylus.sone.database.AlbumBuilder; import net.pterodactylus.sone.database.Database; @@ -104,17 +107,16 @@ import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import com.google.inject.Inject; import com.google.inject.Singleton; +import kotlin.jvm.functions.Function1; /** * The Sone core. - * - * @author David ‘Bombe’ Roden */ @Singleton public class Core extends AbstractService implements SoneProvider, PostProvider, PostReplyProvider { /** The logger. */ - private static final Logger logger = getLogger("Sone.Core"); + private static final Logger logger = getLogger(Core.class.getName()); /** The start time. */ private final long startupTime = System.currentTimeMillis(); @@ -152,23 +154,20 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, /** The trust updater. */ private final WebOfTrustUpdater webOfTrustUpdater; - /** The times Sones were followed. */ - private final Map soneFollowingTimes = new HashMap(); - /** Locked local Sones. */ /* synchronize on itself. */ - private final Set lockedSones = new HashSet(); + private final Set lockedSones = new HashSet<>(); /** Sone inserters. */ /* synchronize access on this on sones. */ - private final Map soneInserters = new HashMap(); + private final Map soneInserters = new HashMap<>(); /** Sone rescuers. */ /* synchronize access on this on sones. */ - private final Map soneRescuers = new HashMap(); + private final Map soneRescuers = new HashMap<>(); /** All known Sones. */ - private final Set knownSones = new HashSet(); + private final Set knownSones = new HashSet<>(); /** The post database. */ private final Database database; @@ -177,7 +176,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, private final Multimap trustedIdentities = Multimaps.synchronizedSetMultimap(HashMultimap.create()); /** All temporary images. */ - private final Map temporaryImages = new HashMap(); + private final Map temporaryImages = new HashMap<>(); /** Ticker for threads that mark own elements as known. */ private final ScheduledExecutorService localElementTicker = Executors.newScheduledThreadPool(1); @@ -202,22 +201,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * The database */ @Inject - public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, Database database) { - super("Sone Core"); - this.configuration = configuration; - this.freenetInterface = freenetInterface; - this.identityManager = identityManager; - this.soneDownloader = new SoneDownloaderImpl(this, freenetInterface); - this.imageInserter = new ImageInserter(freenetInterface, freenetInterface.new InsertTokenSupplier()); - this.updateChecker = new UpdateChecker(eventBus, freenetInterface); - this.webOfTrustUpdater = webOfTrustUpdater; - this.eventBus = eventBus; - this.database = database; - preferences = new Preferences(eventBus); - } - - @VisibleForTesting - protected Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, SoneDownloader soneDownloader, ImageInserter imageInserter, UpdateChecker updateChecker, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, Database database) { + public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, SoneDownloader soneDownloader, ImageInserter imageInserter, UpdateChecker updateChecker, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, Database database) { super("Sone Core"); this.configuration = configuration; this.freenetInterface = freenetInterface; @@ -312,11 +296,18 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, /** * {@inheritDocs} */ + @Nonnull @Override public Collection getSones() { return database.getSones(); } + @Nonnull + @Override + public Function1 getSoneLoader() { + return database.getSoneLoader(); + } + /** * Returns the Sone with the given ID, regardless whether it’s local or * remote. @@ -327,7 +318,8 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * Sone */ @Override - public Optional getSone(String id) { + @Nullable + public Sone getSone(@Nonnull String id) { return database.getSone(id); } @@ -347,9 +339,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * @return The Sone with the given ID, or {@code null} */ public Sone getLocalSone(String id) { - Optional sone = database.getSone(id); - if (sone.isPresent() && sone.get().isLocal()) { - return sone.get(); + Sone sone = database.getSone(id); + if ((sone != null) && sone.isLocal()) { + return sone; } return null; } @@ -371,7 +363,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * @return The Sone with the given ID */ public Sone getRemoteSone(String id) { - return database.getSone(id).orNull(); + return database.getSone(id); } /** @@ -387,20 +379,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, } /** - * Returns the time when the given was first followed by any local Sone. - * - * @param sone - * The Sone to get the time for - * @return The time (in milliseconds since Jan 1, 1970) the Sone has first - * been followed, or {@link Long#MAX_VALUE} - */ - public long getSoneFollowingTime(Sone sone) { - synchronized (soneFollowingTimes) { - return Optional.fromNullable(soneFollowingTimes.get(sone.getId())).or(Long.MAX_VALUE); - } - } - - /** * Returns a post builder. * * @return A new post builder @@ -409,11 +387,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, return database.newPostBuilder(); } - /** - * {@inheritDoc} - */ + @Nullable @Override - public Optional getPost(String postId) { + public Post getPost(@Nonnull String postId) { return database.getPost(postId); } @@ -446,8 +422,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, /** * {@inheritDoc} */ + @Nullable @Override - public Optional getPostReply(String replyId) { + public PostReply getPostReply(String replyId) { return database.getPostReply(replyId); } @@ -467,7 +444,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * @return The Sones that like the given post */ public Set getLikes(Post post) { - Set sones = new HashSet(); + Set sones = new HashSet<>(); for (Sone sone : getSones()) { if (sone.getLikedPostIds().contains(post.getId())) { sones.add(sone); @@ -484,7 +461,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * @return The Sones that like the given reply */ public Set getLikes(PostReply reply) { - Set sones = new HashSet(); + Set sones = new HashSet<>(); for (Sone sone : getSones()) { if (sone.getLikedReplyIds().contains(reply.getId())) { sones.add(sone); @@ -527,8 +504,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * @return The album with the given ID, or {@code null} if no album with the * given ID exists */ - public Album getAlbum(String albumId) { - return database.getAlbum(albumId).orNull(); + @Nullable + public Album getAlbum(@Nonnull String albumId) { + return database.getAlbum(albumId); } public ImageBuilder imageBuilder() { @@ -542,6 +520,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * The ID of the image * @return The image with the given ID */ + @Nullable public Image getImage(String imageId) { return getImage(imageId, true); } @@ -558,10 +537,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * @return The image with the given ID, or {@code null} if none exists and * none was created */ + @Nullable public Image getImage(String imageId, boolean create) { - Optional image = database.getImage(imageId); - if (image.isPresent()) { - return image.get(); + Image image = database.getImage(imageId); + if (image != null) { + return image; } if (!create) { return null; @@ -634,15 +614,18 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, } logger.info(String.format("Adding Sone from OwnIdentity: %s", ownIdentity)); Sone sone = database.newSoneBuilder().local().from(ownIdentity).build(); - sone.setLatestEdition(fromNullable(tryParse(ownIdentity.getProperty("Sone.LatestEdition"))).or(0L)); - sone.setClient(new Client("Sone", SonePlugin.VERSION.toString())); + String property = fromNullable(ownIdentity.getProperty("Sone.LatestEdition")).or("0"); + sone.setLatestEdition(fromNullable(tryParse(property)).or(0L)); + sone.setClient(new Client("Sone", SonePlugin.getPluginVersion())); sone.setKnown(true); SoneInserter soneInserter = new SoneInserter(this, eventBus, freenetInterface, ownIdentity.getId()); + soneInserter.insertionDelayChanged(new InsertionDelayChangedEvent(preferences.getInsertionDelay())); eventBus.register(soneInserter); synchronized (soneInserters) { soneInserters.put(sone, soneInserter); } loadSone(sone); + database.storeSone(sone); sone.setStatus(SoneStatus.idle); soneInserter.start(); return sone; @@ -679,14 +662,14 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, logger.log(Level.WARNING, "Given Identity is null!"); return null; } - final Long latestEdition = tryParse(fromNullable( - identity.getProperty("Sone.LatestEdition")).or("0")); - Optional existingSone = getSone(identity.getId()); - if (existingSone.isPresent() && existingSone.get().isLocal()) { - return existingSone.get(); + String property = fromNullable(identity.getProperty("Sone.LatestEdition")).or("0"); + long latestEdition = fromNullable(tryParse(property)).or(0L); + Sone existingSone = getSone(identity.getId()); + if ((existingSone != null )&& existingSone.isLocal()) { + return existingSone; } - boolean newSone = !existingSone.isPresent(); - Sone sone = !newSone ? existingSone.get() : database.newSoneBuilder().from(identity).build(); + boolean newSone = existingSone == null; + Sone sone = !newSone ? existingSone : database.newSoneBuilder().from(identity).build(); sone.setLatestEdition(latestEdition); if (newSone) { synchronized (knownSones) { @@ -704,7 +687,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, } database.storeSone(sone); soneDownloader.addSone(sone); - soneDownloaders.execute(soneDownloader.fetchSoneWithUriAction(sone)); + soneDownloaders.execute(soneDownloader.fetchSoneAsUskAction(sone)); return sone; } @@ -719,25 +702,21 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, public void followSone(Sone sone, String soneId) { checkNotNull(sone, "sone must not be null"); checkNotNull(soneId, "soneId must not be null"); - sone.addFriend(soneId); - synchronized (soneFollowingTimes) { - if (!soneFollowingTimes.containsKey(soneId)) { - long now = System.currentTimeMillis(); - soneFollowingTimes.put(soneId, now); - Optional followedSone = getSone(soneId); - if (!followedSone.isPresent()) { - return; - } - for (Post post : followedSone.get().getPosts()) { - if (post.getTime() < now) { - markPostKnown(post); - } - } - for (PostReply reply : followedSone.get().getReplies()) { - if (reply.getTime() < now) { - markReplyKnown(reply); - } - } + database.addFriend(sone, soneId); + @SuppressWarnings("ConstantConditions") // we just followed, this can’t be null. + long now = database.getFollowingTime(soneId); + Sone followedSone = getSone(soneId); + if (followedSone == null) { + return; + } + for (Post post : followedSone.getPosts()) { + if (post.getTime() < now) { + markPostKnown(post); + } + } + for (PostReply reply : followedSone.getReplies()) { + if (reply.getTime() < now) { + markReplyKnown(reply); } } touchConfiguration(); @@ -754,16 +733,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, public void unfollowSone(Sone sone, String soneId) { checkNotNull(sone, "sone must not be null"); checkNotNull(soneId, "soneId must not be null"); - sone.removeFriend(soneId); - boolean unfollowedSoneStillFollowed = false; - for (Sone localSone : getLocalSones()) { - unfollowedSoneStillFollowed |= localSone.hasFriend(soneId); - } - if (!unfollowedSoneStillFollowed) { - synchronized (soneFollowingTimes) { - soneFollowingTimes.remove(soneId); - } - } + database.removeFriend(sone, soneId); touchConfiguration(); } @@ -858,50 +828,20 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * of the age of the given Sone */ public void updateSone(final Sone sone, boolean soneRescueMode) { - Optional storedSone = getSone(sone.getId()); - if (storedSone.isPresent()) { - if (!soneRescueMode && !(sone.getTime() > storedSone.get().getTime())) { + Sone storedSone = getSone(sone.getId()); + if (storedSone != null) { + if (!soneRescueMode && !(sone.getTime() > storedSone.getTime())) { logger.log(Level.FINE, String.format("Downloaded Sone %s is not newer than stored Sone %s.", sone, storedSone)); return; } - /* find removed posts. */ - SoneChangeDetector soneChangeDetector = new SoneChangeDetector(storedSone.get()); - soneChangeDetector.onNewPosts(new PostProcessor() { - @Override - public void processPost(Post post) { - if (post.getTime() < getSoneFollowingTime(sone)) { - post.setKnown(true); - } else if (!post.isKnown()) { - eventBus.post(new NewPostFoundEvent(post)); - } - } - }); - soneChangeDetector.onRemovedPosts(new PostProcessor() { - @Override - public void processPost(Post post) { - eventBus.post(new PostRemovedEvent(post)); - } - }); - soneChangeDetector.onNewPostReplies(new PostReplyProcessor() { - @Override - public void processPostReply(PostReply postReply) { - if (postReply.getTime() < getSoneFollowingTime(sone)) { - postReply.setKnown(true); - } else if (!postReply.isKnown()) { - eventBus.post(new NewPostReplyFoundEvent(postReply)); - } - } - }); - soneChangeDetector.onRemovedPostReplies(new PostReplyProcessor() { - @Override - public void processPostReply(PostReply postReply) { - eventBus.post(new PostReplyRemovedEvent(postReply)); - } - }); - soneChangeDetector.detectChanges(sone); + List events = + collectEventsForChangesInSone(storedSone, sone); database.storeSone(sone); - sone.setOptions(storedSone.get().getOptions()); - sone.setKnown(storedSone.get().isKnown()); + for (Object event : events) { + eventBus.post(event); + } + sone.setOptions(storedSone.getOptions()); + sone.setKnown(storedSone.isKnown()); sone.setStatus((sone.getTime() == 0) ? SoneStatus.unknown : SoneStatus.idle); if (sone.isLocal()) { touchConfiguration(); @@ -909,6 +849,36 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, } } + private List collectEventsForChangesInSone(Sone oldSone, Sone newSone) { + List events = new ArrayList<>(); + SoneComparison soneComparison = new SoneComparison(oldSone, newSone); + for (Post newPost : soneComparison.getNewPosts()) { + if (newPost.getSone().equals(newSone)) { + newPost.setKnown(true); + } else if (newPost.getTime() < database.getFollowingTime(newSone.getId())) { + newPost.setKnown(true); + } else if (!newPost.isKnown()) { + events.add(new NewPostFoundEvent(newPost)); + } + } + for (Post post : soneComparison.getRemovedPosts()) { + events.add(new PostRemovedEvent(post)); + } + for (PostReply postReply : soneComparison.getNewPostReplies()) { + if (postReply.getSone().equals(newSone)) { + postReply.setKnown(true); + } else if (postReply.getTime() < database.getFollowingTime(newSone.getId())) { + postReply.setKnown(true); + } else if (!postReply.isKnown()) { + events.add(new NewPostReplyFoundEvent(postReply)); + } + } + for (PostReply postReply : soneComparison.getRemovedPostReplies()) { + events.add(new PostReplyRemovedEvent(postReply)); + } + return events; + } + /** * Deletes the given Sone. This will remove the Sone from the * {@link #getLocalSones() local Sones}, stop its {@link SoneInserter} and @@ -1009,9 +979,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, Set likedReplyIds = configurationSoneParser.parseLikedPostReplyIds(); - /* load friends. */ - Set friends = configurationSoneParser.parseFriends(); - /* load albums. */ List topLevelAlbums; try { @@ -1048,12 +1015,13 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, } /* load options. */ - sone.getOptions().setAutoFollow(configuration.getBooleanValue(sonePrefix + "/Options/AutoFollow").getValue(null)); - sone.getOptions().setSoneInsertNotificationEnabled(configuration.getBooleanValue(sonePrefix + "/Options/EnableSoneInsertNotifications").getValue(null)); - sone.getOptions().setShowNewSoneNotifications(configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewSones").getValue(null)); - sone.getOptions().setShowNewPostNotifications(configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewPosts").getValue(null)); - sone.getOptions().setShowNewReplyNotifications(configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewReplies").getValue(null)); - sone.getOptions().setShowCustomAvatars(ShowCustomAvatars.valueOf(configuration.getStringValue(sonePrefix + "/Options/ShowCustomAvatars").getValue(ShowCustomAvatars.NEVER.name()))); + sone.getOptions().setAutoFollow(configuration.getBooleanValue(sonePrefix + "/Options/AutoFollow").getValue(false)); + sone.getOptions().setSoneInsertNotificationEnabled(configuration.getBooleanValue(sonePrefix + "/Options/EnableSoneInsertNotifications").getValue(false)); + sone.getOptions().setShowNewSoneNotifications(configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewSones").getValue(true)); + sone.getOptions().setShowNewPostNotifications(configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewPosts").getValue(true)); + sone.getOptions().setShowNewReplyNotifications(configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewReplies").getValue(true)); + sone.getOptions().setShowCustomAvatars(LoadExternalContent.valueOf(configuration.getStringValue(sonePrefix + "/Options/ShowCustomAvatars").getValue(LoadExternalContent.NEVER.name()))); + sone.getOptions().setLoadLinkedImages(LoadExternalContent.valueOf(configuration.getStringValue(sonePrefix + "/Options/LoadLinkedImages").getValue(LoadExternalContent.NEVER.name()))); /* if we’re still here, Sone was loaded successfully. */ synchronized (sone) { @@ -1063,25 +1031,16 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, sone.setReplies(replies); sone.setLikePostIds(likedPostIds); sone.setLikeReplyIds(likedReplyIds); - for (String friendId : friends) { - followSone(sone, friendId); - } for (Album album : sone.getRootAlbum().getAlbums()) { sone.getRootAlbum().removeAlbum(album); } for (Album album : topLevelAlbums) { sone.getRootAlbum().addAlbum(album); } - database.storeSone(sone); synchronized (soneInserters) { soneInserters.get(sone).setLastInsertFingerprint(lastInsertFingerprint); } } - synchronized (knownSones) { - for (String friend : friends) { - knownSones.add(friend); - } - } for (Post post : posts) { post.setKnown(true); } @@ -1105,24 +1064,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * @return The created post */ public Post createPost(Sone sone, Optional recipient, String text) { - return createPost(sone, recipient, System.currentTimeMillis(), text); - } - - /** - * Creates a new post. - * - * @param sone - * The Sone that creates the post - * @param recipient - * The recipient Sone, or {@code null} if this post does not have - * a recipient - * @param time - * The time of the post - * @param text - * The text of the post - * @return The created post - */ - public Post createPost(Sone sone, Optional recipient, long time, String text) { checkNotNull(text, "text must not be null"); checkArgument(text.trim().length() > 0, "text must not be empty"); if (!sone.isLocal()) { @@ -1130,7 +1071,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, return null; } PostBuilder postBuilder = database.newPostBuilder(); - postBuilder.from(sone.getId()).randomId().withTime(time).withText(text.trim()); + postBuilder.from(sone.getId()).randomId().currentTime().withText(text.trim()); if (recipient.isPresent()) { postBuilder.to(recipient.get().getId()); } @@ -1387,7 +1328,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, identityManager.start(); webOfTrustUpdater.init(); webOfTrustUpdater.start(); - database.start(); + database.startAsync(); } /** @@ -1421,8 +1362,13 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, saveSone(soneInserter.getKey()); } } + synchronized (soneRescuers) { + for (SoneRescuer soneRescuer : soneRescuers.values()) { + soneRescuer.stop(); + } + } saveConfiguration(); - database.stop(); + database.stopAsync(); webOfTrustUpdater.stop(); updateChecker.stop(); soneDownloader.stop(); @@ -1513,13 +1459,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, } configuration.getStringValue(sonePrefix + "/Likes/Reply/" + replyLikeCounter + "/ID").setValue(null); - /* save friends. */ - int friendCounter = 0; - for (String friendId : sone.getFriends()) { - configuration.getStringValue(sonePrefix + "/Friends/" + friendCounter++ + "/ID").setValue(friendId); - } - configuration.getStringValue(sonePrefix + "/Friends/" + friendCounter + "/ID").setValue(null); - /* save albums. first, collect in a flat structure, top-level first. */ List albums = FluentIterable.from(sone.getRootAlbum().getAlbums()).transformAndConcat(Album.FLATTENER).toList(); @@ -1530,7 +1469,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, configuration.getStringValue(albumPrefix + "/Title").setValue(album.getTitle()); configuration.getStringValue(albumPrefix + "/Description").setValue(album.getDescription()); configuration.getStringValue(albumPrefix + "/Parent").setValue(album.getParent().equals(sone.getRootAlbum()) ? null : album.getParent().getId()); - configuration.getStringValue(albumPrefix + "/AlbumImage").setValue(album.getAlbumImage() == null ? null : album.getAlbumImage().getId()); } configuration.getStringValue(sonePrefix + "/Albums/" + albumCounter + "/ID").setValue(null); @@ -1561,6 +1499,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewPosts").setValue(sone.getOptions().isShowNewPostNotifications()); configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewReplies").setValue(sone.getOptions().isShowNewReplyNotifications()); configuration.getStringValue(sonePrefix + "/Options/ShowCustomAvatars").setValue(sone.getOptions().getShowCustomAvatars().name()); + configuration.getStringValue(sonePrefix + "/Options/LoadLinkedImages").setValue(sone.getOptions().getLoadLinkedImages().name()); configuration.save(); @@ -1597,27 +1536,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, configuration.getStringValue("KnownSone/" + soneCounter + "/ID").setValue(null); } - /* save Sone following times. */ - soneCounter = 0; - synchronized (soneFollowingTimes) { - for (Entry soneFollowingTime : soneFollowingTimes.entrySet()) { - configuration.getStringValue("SoneFollowingTimes/" + soneCounter + "/Sone").setValue(soneFollowingTime.getKey()); - configuration.getLongValue("SoneFollowingTimes/" + soneCounter + "/Time").setValue(soneFollowingTime.getValue()); - ++soneCounter; - } - configuration.getStringValue("SoneFollowingTimes/" + soneCounter + "/Sone").setValue(null); - } - /* save known posts. */ database.save(); - /* save bookmarked posts. */ - int bookmarkedPostCounter = 0; - for (Post bookmarkedPost : getBookmarkedPosts()) { - configuration.getStringValue("Bookmarks/Post/" + bookmarkedPostCounter++ + "/ID").setValue(bookmarkedPost.getId()); - } - configuration.getStringValue("Bookmarks/Post/" + bookmarkedPostCounter++ + "/ID").setValue(null); - /* now save it. */ configuration.save(); @@ -1649,31 +1570,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, knownSones.add(knownSoneId); } } - - /* load Sone following times. */ - soneCounter = 0; - while (true) { - String soneId = configuration.getStringValue("SoneFollowingTimes/" + soneCounter + "/Sone").getValue(null); - if (soneId == null) { - break; - } - long time = configuration.getLongValue("SoneFollowingTimes/" + soneCounter + "/Time").getValue(Long.MAX_VALUE); - synchronized (soneFollowingTimes) { - soneFollowingTimes.put(soneId, time); - } - ++soneCounter; - } - - /* load bookmarked posts. */ - int bookmarkedPostCounter = 0; - while (true) { - String bookmarkedPostId = configuration.getStringValue("Bookmarks/Post/" + bookmarkedPostCounter++ + "/ID").getValue(null); - if (bookmarkedPostId == null) { - break; - } - database.bookmarkPost(bookmarkedPostId); - } - } /** @@ -1731,9 +1627,15 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, if (sone.isLocal()) { return; } - sone.setLatestEdition(fromNullable(tryParse(identity.getProperty("Sone.LatestEdition"))).or(sone.getLatestEdition())); + String newLatestEdition = identity.getProperty("Sone.LatestEdition"); + if (newLatestEdition != null) { + Long parsedNewLatestEdition = tryParse(newLatestEdition); + if (parsedNewLatestEdition != null) { + sone.setLatestEdition(parsedNewLatestEdition); + } + } soneDownloader.addSone(sone); - soneDownloaders.execute(soneDownloader.fetchSoneAction(sone)); + soneDownloaders.execute(soneDownloader.fetchSoneAsSskAction(sone)); } /** @@ -1755,13 +1657,19 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, return; } } - Optional sone = getSone(identity.getId()); - if (!sone.isPresent()) { + Sone sone = getSone(identity.getId()); + if (sone == null) { /* TODO - we don’t have the Sone anymore. should this happen? */ return; } - database.removeSone(sone.get()); - eventBus.post(new SoneRemovedEvent(sone.get())); + for (PostReply postReply : sone.getReplies()) { + eventBus.post(new PostReplyRemovedEvent(postReply)); + } + for (Post post : sone.getPosts()) { + eventBus.post(new PostRemovedEvent(post)); + } + eventBus.post(new SoneRemovedEvent(sone)); + database.removeSone(sone); } /**