Java tutorial
/* * Copyright (c) 2016. Matsuda, Akihit (akihito104) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.freshdigitable.udonroad.module.realm; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.freshdigitable.udonroad.datastore.ConfigStore; import com.freshdigitable.udonroad.datastore.MediaCache; import com.freshdigitable.udonroad.datastore.StatusReaction; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import io.realm.Realm; import io.realm.RealmChangeListener; import rx.Observable; import rx.Subscriber; import rx.functions.Action0; import rx.functions.Func1; import twitter4j.ExtendedMediaEntity; import twitter4j.Status; import twitter4j.User; import twitter4j.UserMentionEntity; import static com.freshdigitable.udonroad.module.realm.StatusRealm.KEY_ID; /** * StatusCacheRealm implements StatusCache for Realm. * * Created by akihit on 2016/07/22. */ public class StatusCacheRealm extends TypedCacheBaseRealm<Status> implements MediaCache { @SuppressWarnings("unused") public static final String TAG = StatusCacheRealm.class.getSimpleName(); private final ConfigStore configStore; private UserCacheRealm userTypedCache; public StatusCacheRealm(ConfigStore configStore) { this.configStore = configStore; } @Override public void open() { super.open(); this.userTypedCache = new UserCacheRealm(this); configStore.open(); } @Override public void close() { super.close(); configStore.close(); } @Override public void upsert(@Nullable Status status) { if (status == null) { return; } upsert(Collections.singletonList(status)); } @Override public void upsert(List<Status> statuses) { if (statuses == null || statuses.isEmpty()) { return; } final Collection<Status> updates = splitUpsertingStatus(statuses); upsertUser(updates); upsertStatusReaction(updates); userTypedCache.upsert(splitUserMentionEntity(updates)); cache.executeTransaction(upsertTransaction(statuses)); } @Override public Observable<Void> observeUpsert(Collection<Status> statuses) { if (statuses == null || statuses.isEmpty()) { return Observable.empty(); } final Collection<Status> updates = splitUpsertingStatus(statuses); userTypedCache.upsert(splitUserMentionEntity(updates)); final Collection<User> splitUser = splitUpsertingUser(updates); final Collection<StatusReaction> splitReaction = splitUpsertingStatusReaction(updates); return Observable.concat(userTypedCache.observeUpsert(splitUser), configStore.observeUpsert(splitReaction), super.observeUpsert(updates)).last(); } @NonNull @Override public Realm.Transaction upsertTransaction(final Collection<Status> updates) { return new Realm.Transaction() { @Override public void execute(Realm realm) { final ArrayList<StatusRealm> inserts = new ArrayList<>(updates.size()); for (Status s : updates) { final StatusRealm update = findById(realm, s.getId(), StatusRealm.class); if (update == null) { inserts.add(new StatusRealm(s)); } else { update.merge(s); } } realm.insertOrUpdate(inserts); } }; } @NonNull private Collection<Status> splitUpsertingStatus(Collection<Status> statuses) { final LinkedHashMap<Long, Status> updates = new LinkedHashMap<>(); for (Status s : statuses) { if (!configStore.isIgnoredUser(s.getUser().getId())) { updates.put(s.getId(), s); } final Status quotedStatus = s.getQuotedStatus(); if (quotedStatus != null && !configStore.isIgnoredUser(quotedStatus.getUser().getId())) { updates.put(quotedStatus.getId(), quotedStatus); } final Status retweetedStatus = s.getRetweetedStatus(); if (retweetedStatus != null && !configStore.isIgnoredUser(retweetedStatus.getUser().getId())) { updates.put(retweetedStatus.getId(), retweetedStatus); final Status rtQuotedStatus = retweetedStatus.getQuotedStatus(); if (rtQuotedStatus != null && !configStore.isIgnoredUser(rtQuotedStatus.getUser().getId())) { updates.put(rtQuotedStatus.getId(), rtQuotedStatus); } } } return updates.values(); } private Collection<User> splitUpsertingUser(Collection<Status> updates) { Map<Long, User> res = new LinkedHashMap<>(updates.size()); for (Status s : updates) { final User user = s.getUser(); res.put(user.getId(), user); } return res.values(); } private UserMentionEntity[] splitUserMentionEntity(Collection<Status> updates) { final Map<Long, UserMentionEntity> res = new LinkedHashMap<>(); for (Status s : updates) { for (UserMentionEntity ume : s.getUserMentionEntities()) { res.put(ume.getId(), ume); } } final Collection<UserMentionEntity> values = res.values(); return values.toArray(new UserMentionEntity[values.size()]); } private Collection<StatusReaction> splitUpsertingStatusReaction(Collection<Status> statuses) { Map<Long, StatusReaction> res = new LinkedHashMap<>(statuses.size()); for (Status s : statuses) { res.put(s.getId(), new StatusReactionRealm(s)); } return res.values(); } @Override public void delete(final long statusId) { cache.executeTransactionAsync(new Realm.Transaction() { @Override public void execute(Realm realm) { realm.where(StatusRealm.class).equalTo(KEY_ID, statusId).findAll().deleteAllFromRealm(); } }); configStore.delete(statusId); } /** * to update with response of destroyFavorite or destroy Retweet * @param status new data for update */ @Override public void insert(final Status status) { final Collection<Status> statuses = splitUpsertingStatus(Collections.singletonList(status)); upsertUser(statuses); for (StatusReaction sr : splitUpsertingStatusReaction(statuses)) { configStore.insert(sr); } final ArrayList<StatusRealm> entities = new ArrayList<>(statuses.size()); for (Status s : statuses) { if (s instanceof StatusRealm) { entities.add((StatusRealm) s); } else { entities.add(new StatusRealm(s)); } } cache.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { realm.insertOrUpdate(entities); } }); } @Override @Nullable public Status find(long id) { final StatusRealm res = getStatusInternal(id); if (res == null) { return null; } if (res.isRetweet()) { final StatusRealm rtStatus = getStatusInternal(res.getRetweetedStatusId()); if (rtStatus != null && rtStatus.getQuotedStatusId() > 0) { rtStatus.setQuotedStatus(getStatusInternal(rtStatus.getQuotedStatusId())); } res.setRetweetedStatus(rtStatus); return res; } if (res.getQuotedStatusId() > 0) { res.setQuotedStatus(getStatusInternal(res.getQuotedStatusId())); } return res; } @NonNull @Override public Observable<Status> observeById(long statusId) { final StatusRealm status = (StatusRealm) find(statusId); if (status == null) { return Observable.empty(); } final Observable<Status> statusObservable = Observable.create(new Observable.OnSubscribe<Status>() { @Override public void call(final Subscriber<? super Status> subscriber) { StatusRealm.addChangeListener(status, new RealmChangeListener<StatusRealm>() { @Override public void onChange(StatusRealm element) { subscriber.onNext(element); } }); subscriber.onNext(status); } }).doOnUnsubscribe(new Action0() { @Override public void call() { StatusRealm.removeChangeListeners(status); } }); final Observable<Status> reactionObservable = reactionObservable( status.isRetweet() ? status.getRetweetedStatusId() : status.getId(), status); final Observable<Status> qReactionObservable = reactionObservable(status.getQuotedStatusId(), status); return Observable.merge(statusObservable, reactionObservable, qReactionObservable); } private Observable<Status> reactionObservable(long statusId, @NonNull final StatusRealm original) { return configStore.observeById(statusId).map(new Func1<StatusReaction, Status>() { @Override public Status call(StatusReaction reaction) { original.setReaction(reaction); return original; } }); } @Override public ExtendedMediaEntity getMediaEntity(long mediaId) { return findById(cache, mediaId, ExtendedMediaEntityRealm.class); } @Nullable private StatusRealm getStatusInternal(long id) { final StatusRealm status = findById(cache, id, StatusRealm.class); if (status == null) { return null; } status.setUser(userTypedCache.find(status.getUserId())); status.setReaction(configStore.find(id)); return status; } private void upsertUser(Collection<Status> updates) { final Collection<User> updateUsers = splitUpsertingUser(updates); userTypedCache.upsert(new ArrayList<>(updateUsers)); } private void upsertStatusReaction(Collection<Status> updates) { final Collection<StatusReaction> statusReactions = splitUpsertingStatusReaction(updates); configStore.upsert(new ArrayList<>(statusReactions)); } }